...

Source file src/go/types/resolver.go

     1	// Copyright 2013 The Go Authors. All rights reserved.
     2	// Use of this source code is governed by a BSD-style
     3	// license that can be found in the LICENSE file.
     4	
     5	package types
     6	
     7	import (
     8		"fmt"
     9		"go/ast"
    10		"go/constant"
    11		"go/token"
    12		"sort"
    13		"strconv"
    14		"strings"
    15		"unicode"
    16	)
    17	
    18	// A declInfo describes a package-level const, type, var, or func declaration.
    19	type declInfo struct {
    20		file  *Scope        // scope of file containing this declaration
    21		lhs   []*Var        // lhs of n:1 variable declarations, or nil
    22		typ   ast.Expr      // type, or nil
    23		init  ast.Expr      // init/orig expression, or nil
    24		fdecl *ast.FuncDecl // func declaration, or nil
    25		alias bool          // type alias declaration
    26	
    27		// The deps field tracks initialization expression dependencies.
    28		deps objSet // lazily initialized
    29	}
    30	
    31	// An objSet is simply a set of objects.
    32	type objSet map[Object]bool
    33	
    34	// hasInitializer reports whether the declared object has an initialization
    35	// expression or function body.
    36	func (d *declInfo) hasInitializer() bool {
    37		return d.init != nil || d.fdecl != nil && d.fdecl.Body != nil
    38	}
    39	
    40	// addDep adds obj to the set of objects d's init expression depends on.
    41	func (d *declInfo) addDep(obj Object) {
    42		m := d.deps
    43		if m == nil {
    44			m = make(objSet)
    45			d.deps = m
    46		}
    47		m[obj] = true
    48	}
    49	
    50	// arityMatch checks that the lhs and rhs of a const or var decl
    51	// have the appropriate number of names and init exprs. For const
    52	// decls, init is the value spec providing the init exprs; for
    53	// var decls, init is nil (the init exprs are in s in this case).
    54	func (check *Checker) arityMatch(s, init *ast.ValueSpec) {
    55		l := len(s.Names)
    56		r := len(s.Values)
    57		if init != nil {
    58			r = len(init.Values)
    59		}
    60	
    61		switch {
    62		case init == nil && r == 0:
    63			// var decl w/o init expr
    64			if s.Type == nil {
    65				check.errorf(s.Pos(), "missing type or init expr")
    66			}
    67		case l < r:
    68			if l < len(s.Values) {
    69				// init exprs from s
    70				n := s.Values[l]
    71				check.errorf(n.Pos(), "extra init expr %s", n)
    72				// TODO(gri) avoid declared but not used error here
    73			} else {
    74				// init exprs "inherited"
    75				check.errorf(s.Pos(), "extra init expr at %s", check.fset.Position(init.Pos()))
    76				// TODO(gri) avoid declared but not used error here
    77			}
    78		case l > r && (init != nil || r != 1):
    79			n := s.Names[r]
    80			check.errorf(n.Pos(), "missing init expr for %s", n)
    81		}
    82	}
    83	
    84	func validatedImportPath(path string) (string, error) {
    85		s, err := strconv.Unquote(path)
    86		if err != nil {
    87			return "", err
    88		}
    89		if s == "" {
    90			return "", fmt.Errorf("empty string")
    91		}
    92		const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
    93		for _, r := range s {
    94			if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
    95				return s, fmt.Errorf("invalid character %#U", r)
    96			}
    97		}
    98		return s, nil
    99	}
   100	
   101	// declarePkgObj declares obj in the package scope, records its ident -> obj mapping,
   102	// and updates check.objMap. The object must not be a function or method.
   103	func (check *Checker) declarePkgObj(ident *ast.Ident, obj Object, d *declInfo) {
   104		assert(ident.Name == obj.Name())
   105	
   106		// spec: "A package-scope or file-scope identifier with name init
   107		// may only be declared to be a function with this (func()) signature."
   108		if ident.Name == "init" {
   109			check.errorf(ident.Pos(), "cannot declare init - must be func")
   110			return
   111		}
   112	
   113		// spec: "The main package must have package name main and declare
   114		// a function main that takes no arguments and returns no value."
   115		if ident.Name == "main" && check.pkg.name == "main" {
   116			check.errorf(ident.Pos(), "cannot declare main - must be func")
   117			return
   118		}
   119	
   120		check.declare(check.pkg.scope, ident, obj, token.NoPos)
   121		check.objMap[obj] = d
   122		obj.setOrder(uint32(len(check.objMap)))
   123	}
   124	
   125	// filename returns a filename suitable for debugging output.
   126	func (check *Checker) filename(fileNo int) string {
   127		file := check.files[fileNo]
   128		if pos := file.Pos(); pos.IsValid() {
   129			return check.fset.File(pos).Name()
   130		}
   131		return fmt.Sprintf("file[%d]", fileNo)
   132	}
   133	
   134	func (check *Checker) importPackage(pos token.Pos, path, dir string) *Package {
   135		// If we already have a package for the given (path, dir)
   136		// pair, use it instead of doing a full import.
   137		// Checker.impMap only caches packages that are marked Complete
   138		// or fake (dummy packages for failed imports). Incomplete but
   139		// non-fake packages do require an import to complete them.
   140		key := importKey{path, dir}
   141		imp := check.impMap[key]
   142		if imp != nil {
   143			return imp
   144		}
   145	
   146		// no package yet => import it
   147		if path == "C" && check.conf.FakeImportC {
   148			imp = NewPackage("C", "C")
   149			imp.fake = true
   150		} else {
   151			// ordinary import
   152			var err error
   153			if importer := check.conf.Importer; importer == nil {
   154				err = fmt.Errorf("Config.Importer not installed")
   155			} else if importerFrom, ok := importer.(ImporterFrom); ok {
   156				imp, err = importerFrom.ImportFrom(path, dir, 0)
   157				if imp == nil && err == nil {
   158					err = fmt.Errorf("Config.Importer.ImportFrom(%s, %s, 0) returned nil but no error", path, dir)
   159				}
   160			} else {
   161				imp, err = importer.Import(path)
   162				if imp == nil && err == nil {
   163					err = fmt.Errorf("Config.Importer.Import(%s) returned nil but no error", path)
   164				}
   165			}
   166			// make sure we have a valid package name
   167			// (errors here can only happen through manipulation of packages after creation)
   168			if err == nil && imp != nil && (imp.name == "_" || imp.name == "") {
   169				err = fmt.Errorf("invalid package name: %q", imp.name)
   170				imp = nil // create fake package below
   171			}
   172			if err != nil {
   173				check.errorf(pos, "could not import %s (%s)", path, err)
   174				if imp == nil {
   175					// create a new fake package
   176					// come up with a sensible package name (heuristic)
   177					name := path
   178					if i := len(name); i > 0 && name[i-1] == '/' {
   179						name = name[:i-1]
   180					}
   181					if i := strings.LastIndex(name, "/"); i >= 0 {
   182						name = name[i+1:]
   183					}
   184					imp = NewPackage(path, name)
   185				}
   186				// continue to use the package as best as we can
   187				imp.fake = true // avoid follow-up lookup failures
   188			}
   189		}
   190	
   191		// package should be complete or marked fake, but be cautious
   192		if imp.complete || imp.fake {
   193			check.impMap[key] = imp
   194			return imp
   195		}
   196	
   197		// something went wrong (importer may have returned incomplete package without error)
   198		return nil
   199	}
   200	
   201	// collectObjects collects all file and package objects and inserts them
   202	// into their respective scopes. It also performs imports and associates
   203	// methods with receiver base type names.
   204	func (check *Checker) collectObjects() {
   205		pkg := check.pkg
   206	
   207		// pkgImports is the set of packages already imported by any package file seen
   208		// so far. Used to avoid duplicate entries in pkg.imports. Allocate and populate
   209		// it (pkg.imports may not be empty if we are checking test files incrementally).
   210		// Note that pkgImports is keyed by package (and thus package path), not by an
   211		// importKey value. Two different importKey values may map to the same package
   212		// which is why we cannot use the check.impMap here.
   213		var pkgImports = make(map[*Package]bool)
   214		for _, imp := range pkg.imports {
   215			pkgImports[imp] = true
   216		}
   217	
   218		var methods []*Func // list of methods with non-blank _ names
   219		for fileNo, file := range check.files {
   220			// The package identifier denotes the current package,
   221			// but there is no corresponding package object.
   222			check.recordDef(file.Name, nil)
   223	
   224			// Use the actual source file extent rather than *ast.File extent since the
   225			// latter doesn't include comments which appear at the start or end of the file.
   226			// Be conservative and use the *ast.File extent if we don't have a *token.File.
   227			pos, end := file.Pos(), file.End()
   228			if f := check.fset.File(file.Pos()); f != nil {
   229				pos, end = token.Pos(f.Base()), token.Pos(f.Base()+f.Size())
   230			}
   231			fileScope := NewScope(check.pkg.scope, pos, end, check.filename(fileNo))
   232			check.recordScope(file, fileScope)
   233	
   234			// determine file directory, necessary to resolve imports
   235			// FileName may be "" (typically for tests) in which case
   236			// we get "." as the directory which is what we would want.
   237			fileDir := dir(check.fset.Position(file.Name.Pos()).Filename)
   238	
   239			for _, decl := range file.Decls {
   240				switch d := decl.(type) {
   241				case *ast.BadDecl:
   242					// ignore
   243	
   244				case *ast.GenDecl:
   245					var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
   246					for iota, spec := range d.Specs {
   247						switch s := spec.(type) {
   248						case *ast.ImportSpec:
   249							// import package
   250							path, err := validatedImportPath(s.Path.Value)
   251							if err != nil {
   252								check.errorf(s.Path.Pos(), "invalid import path (%s)", err)
   253								continue
   254							}
   255	
   256							imp := check.importPackage(s.Path.Pos(), path, fileDir)
   257							if imp == nil {
   258								continue
   259							}
   260	
   261							// add package to list of explicit imports
   262							// (this functionality is provided as a convenience
   263							// for clients; it is not needed for type-checking)
   264							if !pkgImports[imp] {
   265								pkgImports[imp] = true
   266								pkg.imports = append(pkg.imports, imp)
   267							}
   268	
   269							// local name overrides imported package name
   270							name := imp.name
   271							if s.Name != nil {
   272								name = s.Name.Name
   273								if path == "C" {
   274									// match cmd/compile (not prescribed by spec)
   275									check.errorf(s.Name.Pos(), `cannot rename import "C"`)
   276									continue
   277								}
   278								if name == "init" {
   279									check.errorf(s.Name.Pos(), "cannot declare init - must be func")
   280									continue
   281								}
   282							}
   283	
   284							obj := NewPkgName(s.Pos(), pkg, name, imp)
   285							if s.Name != nil {
   286								// in a dot-import, the dot represents the package
   287								check.recordDef(s.Name, obj)
   288							} else {
   289								check.recordImplicit(s, obj)
   290							}
   291	
   292							if path == "C" {
   293								// match cmd/compile (not prescribed by spec)
   294								obj.used = true
   295							}
   296	
   297							// add import to file scope
   298							if name == "." {
   299								// merge imported scope with file scope
   300								for _, obj := range imp.scope.elems {
   301									// A package scope may contain non-exported objects,
   302									// do not import them!
   303									if obj.Exported() {
   304										// declare dot-imported object
   305										// (Do not use check.declare because it modifies the object
   306										// via Object.setScopePos, which leads to a race condition;
   307										// the object may be imported into more than one file scope
   308										// concurrently. See issue #32154.)
   309										if alt := fileScope.Insert(obj); alt != nil {
   310											check.errorf(s.Name.Pos(), "%s redeclared in this block", obj.Name())
   311											check.reportAltDecl(alt)
   312										}
   313									}
   314								}
   315								// add position to set of dot-import positions for this file
   316								// (this is only needed for "imported but not used" errors)
   317								check.addUnusedDotImport(fileScope, imp, s.Pos())
   318							} else {
   319								// declare imported package object in file scope
   320								// (no need to provide s.Name since we called check.recordDef earlier)
   321								check.declare(fileScope, nil, obj, token.NoPos)
   322							}
   323	
   324						case *ast.ValueSpec:
   325							switch d.Tok {
   326							case token.CONST:
   327								// determine which initialization expressions to use
   328								switch {
   329								case s.Type != nil || len(s.Values) > 0:
   330									last = s
   331								case last == nil:
   332									last = new(ast.ValueSpec) // make sure last exists
   333								}
   334	
   335								// declare all constants
   336								for i, name := range s.Names {
   337									obj := NewConst(name.Pos(), pkg, name.Name, nil, constant.MakeInt64(int64(iota)))
   338	
   339									var init ast.Expr
   340									if i < len(last.Values) {
   341										init = last.Values[i]
   342									}
   343	
   344									d := &declInfo{file: fileScope, typ: last.Type, init: init}
   345									check.declarePkgObj(name, obj, d)
   346								}
   347	
   348								check.arityMatch(s, last)
   349	
   350							case token.VAR:
   351								lhs := make([]*Var, len(s.Names))
   352								// If there's exactly one rhs initializer, use
   353								// the same declInfo d1 for all lhs variables
   354								// so that each lhs variable depends on the same
   355								// rhs initializer (n:1 var declaration).
   356								var d1 *declInfo
   357								if len(s.Values) == 1 {
   358									// The lhs elements are only set up after the for loop below,
   359									// but that's ok because declareVar only collects the declInfo
   360									// for a later phase.
   361									d1 = &declInfo{file: fileScope, lhs: lhs, typ: s.Type, init: s.Values[0]}
   362								}
   363	
   364								// declare all variables
   365								for i, name := range s.Names {
   366									obj := NewVar(name.Pos(), pkg, name.Name, nil)
   367									lhs[i] = obj
   368	
   369									d := d1
   370									if d == nil {
   371										// individual assignments
   372										var init ast.Expr
   373										if i < len(s.Values) {
   374											init = s.Values[i]
   375										}
   376										d = &declInfo{file: fileScope, typ: s.Type, init: init}
   377									}
   378	
   379									check.declarePkgObj(name, obj, d)
   380								}
   381	
   382								check.arityMatch(s, nil)
   383	
   384							default:
   385								check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
   386							}
   387	
   388						case *ast.TypeSpec:
   389							obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
   390							check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, typ: s.Type, alias: s.Assign.IsValid()})
   391	
   392						default:
   393							check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
   394						}
   395					}
   396	
   397				case *ast.FuncDecl:
   398					name := d.Name.Name
   399					obj := NewFunc(d.Name.Pos(), pkg, name, nil)
   400					if d.Recv == nil {
   401						// regular function
   402						if name == "init" {
   403							// don't declare init functions in the package scope - they are invisible
   404							obj.parent = pkg.scope
   405							check.recordDef(d.Name, obj)
   406							// init functions must have a body
   407							if d.Body == nil {
   408								check.softErrorf(obj.pos, "missing function body")
   409							}
   410						} else {
   411							check.declare(pkg.scope, d.Name, obj, token.NoPos)
   412						}
   413					} else {
   414						// method
   415						// (Methods with blank _ names are never found; no need to collect
   416						// them for later type association. They will still be type-checked
   417						// with all the other functions.)
   418						if name != "_" {
   419							methods = append(methods, obj)
   420						}
   421						check.recordDef(d.Name, obj)
   422					}
   423					info := &declInfo{file: fileScope, fdecl: d}
   424					// Methods are not package-level objects but we still track them in the
   425					// object map so that we can handle them like regular functions (if the
   426					// receiver is invalid); also we need their fdecl info when associating
   427					// them with their receiver base type, below.
   428					check.objMap[obj] = info
   429					obj.setOrder(uint32(len(check.objMap)))
   430	
   431				default:
   432					check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
   433				}
   434			}
   435		}
   436	
   437		// verify that objects in package and file scopes have different names
   438		for _, scope := range check.pkg.scope.children /* file scopes */ {
   439			for _, obj := range scope.elems {
   440				if alt := pkg.scope.Lookup(obj.Name()); alt != nil {
   441					if pkg, ok := obj.(*PkgName); ok {
   442						check.errorf(alt.Pos(), "%s already declared through import of %s", alt.Name(), pkg.Imported())
   443						check.reportAltDecl(pkg)
   444					} else {
   445						check.errorf(alt.Pos(), "%s already declared through dot-import of %s", alt.Name(), obj.Pkg())
   446						// TODO(gri) dot-imported objects don't have a position; reportAltDecl won't print anything
   447						check.reportAltDecl(obj)
   448					}
   449				}
   450			}
   451		}
   452	
   453		// Now that we have all package scope objects and all methods,
   454		// associate methods with receiver base type name where possible.
   455		// Ignore methods that have an invalid receiver. They will be
   456		// type-checked later, with regular functions.
   457		if methods == nil {
   458			return // nothing to do
   459		}
   460		check.methods = make(map[*TypeName][]*Func)
   461		for _, f := range methods {
   462			fdecl := check.objMap[f].fdecl
   463			if list := fdecl.Recv.List; len(list) > 0 {
   464				// f is a method.
   465				// Determine the receiver base type and associate f with it.
   466				ptr, base := check.resolveBaseTypeName(list[0].Type)
   467				if base != nil {
   468					f.hasPtrRecv = ptr
   469					check.methods[base] = append(check.methods[base], f)
   470				}
   471			}
   472		}
   473	}
   474	
   475	// resolveBaseTypeName returns the non-alias base type name for typ, and whether
   476	// there was a pointer indirection to get to it. The base type name must be declared
   477	// in package scope, and there can be at most one pointer indirection. If no such type
   478	// name exists, the returned base is nil.
   479	func (check *Checker) resolveBaseTypeName(typ ast.Expr) (ptr bool, base *TypeName) {
   480		// Algorithm: Starting from a type expression, which may be a name,
   481		// we follow that type through alias declarations until we reach a
   482		// non-alias type name. If we encounter anything but pointer types or
   483		// parentheses we're done. If we encounter more than one pointer type
   484		// we're done.
   485		var path []*TypeName
   486		for {
   487			typ = unparen(typ)
   488	
   489			// check if we have a pointer type
   490			if pexpr, _ := typ.(*ast.StarExpr); pexpr != nil {
   491				// if we've already seen a pointer, we're done
   492				if ptr {
   493					return false, nil
   494				}
   495				ptr = true
   496				typ = unparen(pexpr.X) // continue with pointer base type
   497			}
   498	
   499			// typ must be the name
   500			name, _ := typ.(*ast.Ident)
   501			if name == nil {
   502				return false, nil
   503			}
   504	
   505			// name must denote an object found in the current package scope
   506			// (note that dot-imported objects are not in the package scope!)
   507			obj := check.pkg.scope.Lookup(name.Name)
   508			if obj == nil {
   509				return false, nil
   510			}
   511	
   512			// the object must be a type name...
   513			tname, _ := obj.(*TypeName)
   514			if tname == nil {
   515				return false, nil
   516			}
   517	
   518			// ... which we have not seen before
   519			if check.cycle(tname, path, false) {
   520				return false, nil
   521			}
   522	
   523			// we're done if tdecl defined tname as a new type
   524			// (rather than an alias)
   525			tdecl := check.objMap[tname] // must exist for objects in package scope
   526			if !tdecl.alias {
   527				return ptr, tname
   528			}
   529	
   530			// otherwise, continue resolving
   531			typ = tdecl.typ
   532			path = append(path, tname)
   533		}
   534	}
   535	
   536	// cycle reports whether obj appears in path or not.
   537	// If it does, and report is set, it also reports a cycle error.
   538	func (check *Checker) cycle(obj *TypeName, path []*TypeName, report bool) bool {
   539		// (it's ok to iterate forward because each named type appears at most once in path)
   540		for i, prev := range path {
   541			if prev == obj {
   542				if report {
   543					check.errorf(obj.pos, "illegal cycle in declaration of %s", obj.name)
   544					// print cycle
   545					for _, obj := range path[i:] {
   546						check.errorf(obj.Pos(), "\t%s refers to", obj.Name()) // secondary error, \t indented
   547					}
   548					check.errorf(obj.Pos(), "\t%s", obj.Name())
   549				}
   550				return true
   551			}
   552		}
   553		return false
   554	}
   555	
   556	// packageObjects typechecks all package objects, but not function bodies.
   557	func (check *Checker) packageObjects() {
   558		// process package objects in source order for reproducible results
   559		objList := make([]Object, len(check.objMap))
   560		i := 0
   561		for obj := range check.objMap {
   562			objList[i] = obj
   563			i++
   564		}
   565		sort.Sort(inSourceOrder(objList))
   566	
   567		// add new methods to already type-checked types (from a prior Checker.Files call)
   568		for _, obj := range objList {
   569			if obj, _ := obj.(*TypeName); obj != nil && obj.typ != nil {
   570				check.addMethodDecls(obj)
   571			}
   572		}
   573	
   574		// We process non-alias declarations first, in order to avoid situations where
   575		// the type of an alias declaration is needed before it is available. In general
   576		// this is still not enough, as it is possible to create sufficiently convoluted
   577		// recursive type definitions that will cause a type alias to be needed before it
   578		// is available (see issue #25838 for examples).
   579		// As an aside, the cmd/compiler suffers from the same problem (#25838).
   580		var aliasList []*TypeName
   581		// phase 1
   582		for _, obj := range objList {
   583			// If we have a type alias, collect it for the 2nd phase.
   584			if tname, _ := obj.(*TypeName); tname != nil && check.objMap[tname].alias {
   585				aliasList = append(aliasList, tname)
   586				continue
   587			}
   588	
   589			check.objDecl(obj, nil)
   590		}
   591		// phase 2
   592		for _, obj := range aliasList {
   593			check.objDecl(obj, nil)
   594		}
   595	
   596		// At this point we may have a non-empty check.methods map; this means that not all
   597		// entries were deleted at the end of typeDecl because the respective receiver base
   598		// types were not found. In that case, an error was reported when declaring those
   599		// methods. We can now safely discard this map.
   600		check.methods = nil
   601	}
   602	
   603	// inSourceOrder implements the sort.Sort interface.
   604	type inSourceOrder []Object
   605	
   606	func (a inSourceOrder) Len() int           { return len(a) }
   607	func (a inSourceOrder) Less(i, j int) bool { return a[i].order() < a[j].order() }
   608	func (a inSourceOrder) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
   609	
   610	// processDelayed processes all delayed actions pushed after top.
   611	func (check *Checker) processDelayed(top int) {
   612		for len(check.delayed) > top {
   613			i := len(check.delayed) - 1
   614			f := check.delayed[i]
   615			check.delayed = check.delayed[:i]
   616			f() // may append to check.delayed
   617		}
   618	}
   619	
   620	// unusedImports checks for unused imports.
   621	func (check *Checker) unusedImports() {
   622		// if function bodies are not checked, packages' uses are likely missing - don't check
   623		if check.conf.IgnoreFuncBodies {
   624			return
   625		}
   626	
   627		// spec: "It is illegal (...) to directly import a package without referring to
   628		// any of its exported identifiers. To import a package solely for its side-effects
   629		// (initialization), use the blank identifier as explicit package name."
   630	
   631		// check use of regular imported packages
   632		for _, scope := range check.pkg.scope.children /* file scopes */ {
   633			for _, obj := range scope.elems {
   634				if obj, ok := obj.(*PkgName); ok {
   635					// Unused "blank imports" are automatically ignored
   636					// since _ identifiers are not entered into scopes.
   637					if !obj.used {
   638						path := obj.imported.path
   639						base := pkgName(path)
   640						if obj.name == base {
   641							check.softErrorf(obj.pos, "%q imported but not used", path)
   642						} else {
   643							check.softErrorf(obj.pos, "%q imported but not used as %s", path, obj.name)
   644						}
   645					}
   646				}
   647			}
   648		}
   649	
   650		// check use of dot-imported packages
   651		for _, unusedDotImports := range check.unusedDotImports {
   652			for pkg, pos := range unusedDotImports {
   653				check.softErrorf(pos, "%q imported but not used", pkg.path)
   654			}
   655		}
   656	}
   657	
   658	// pkgName returns the package name (last element) of an import path.
   659	func pkgName(path string) string {
   660		if i := strings.LastIndex(path, "/"); i >= 0 {
   661			path = path[i+1:]
   662		}
   663		return path
   664	}
   665	
   666	// dir makes a good-faith attempt to return the directory
   667	// portion of path. If path is empty, the result is ".".
   668	// (Per the go/build package dependency tests, we cannot import
   669	// path/filepath and simply use filepath.Dir.)
   670	func dir(path string) string {
   671		if i := strings.LastIndexAny(path, `/\`); i > 0 {
   672			return path[:i]
   673		}
   674		// i <= 0
   675		return "."
   676	}
   677	

View as plain text