github.com/aloncn/graphics-go@v0.0.1/src/go/types/resolver.go (about)

     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  	"strconv"
    13  	"strings"
    14  	"unicode"
    15  )
    16  
    17  // A declInfo describes a package-level const, type, var, or func declaration.
    18  type declInfo struct {
    19  	file  *Scope        // scope of file containing this declaration
    20  	lhs   []*Var        // lhs of n:1 variable declarations, or nil
    21  	typ   ast.Expr      // type, or nil
    22  	init  ast.Expr      // init expression, or nil
    23  	fdecl *ast.FuncDecl // func declaration, or nil
    24  
    25  	deps map[Object]bool // type and init dependencies; lazily allocated
    26  	mark int             // for dependency analysis
    27  }
    28  
    29  // hasInitializer reports whether the declared object has an initialization
    30  // expression or function body.
    31  func (d *declInfo) hasInitializer() bool {
    32  	return d.init != nil || d.fdecl != nil && d.fdecl.Body != nil
    33  }
    34  
    35  // addDep adds obj as a dependency to d.
    36  func (d *declInfo) addDep(obj Object) {
    37  	m := d.deps
    38  	if m == nil {
    39  		m = make(map[Object]bool)
    40  		d.deps = m
    41  	}
    42  	m[obj] = true
    43  }
    44  
    45  // arityMatch checks that the lhs and rhs of a const or var decl
    46  // have the appropriate number of names and init exprs. For const
    47  // decls, init is the value spec providing the init exprs; for
    48  // var decls, init is nil (the init exprs are in s in this case).
    49  func (check *Checker) arityMatch(s, init *ast.ValueSpec) {
    50  	l := len(s.Names)
    51  	r := len(s.Values)
    52  	if init != nil {
    53  		r = len(init.Values)
    54  	}
    55  
    56  	switch {
    57  	case init == nil && r == 0:
    58  		// var decl w/o init expr
    59  		if s.Type == nil {
    60  			check.errorf(s.Pos(), "missing type or init expr")
    61  		}
    62  	case l < r:
    63  		if l < len(s.Values) {
    64  			// init exprs from s
    65  			n := s.Values[l]
    66  			check.errorf(n.Pos(), "extra init expr %s", n)
    67  			// TODO(gri) avoid declared but not used error here
    68  		} else {
    69  			// init exprs "inherited"
    70  			check.errorf(s.Pos(), "extra init expr at %s", init.Pos())
    71  			// TODO(gri) avoid declared but not used error here
    72  		}
    73  	case l > r && (init != nil || r != 1):
    74  		n := s.Names[r]
    75  		check.errorf(n.Pos(), "missing init expr for %s", n)
    76  	}
    77  }
    78  
    79  func validatedImportPath(path string) (string, error) {
    80  	s, err := strconv.Unquote(path)
    81  	if err != nil {
    82  		return "", err
    83  	}
    84  	if s == "" {
    85  		return "", fmt.Errorf("empty string")
    86  	}
    87  	const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
    88  	for _, r := range s {
    89  		if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
    90  			return s, fmt.Errorf("invalid character %#U", r)
    91  		}
    92  	}
    93  	return s, nil
    94  }
    95  
    96  // declarePkgObj declares obj in the package scope, records its ident -> obj mapping,
    97  // and updates check.objMap. The object must not be a function or method.
    98  func (check *Checker) declarePkgObj(ident *ast.Ident, obj Object, d *declInfo) {
    99  	assert(ident.Name == obj.Name())
   100  
   101  	// spec: "A package-scope or file-scope identifier with name init
   102  	// may only be declared to be a function with this (func()) signature."
   103  	if ident.Name == "init" {
   104  		check.errorf(ident.Pos(), "cannot declare init - must be func")
   105  		return
   106  	}
   107  
   108  	check.declare(check.pkg.scope, ident, obj, token.NoPos)
   109  	check.objMap[obj] = d
   110  	obj.setOrder(uint32(len(check.objMap)))
   111  }
   112  
   113  // filename returns a filename suitable for debugging output.
   114  func (check *Checker) filename(fileNo int) string {
   115  	file := check.files[fileNo]
   116  	if pos := file.Pos(); pos.IsValid() {
   117  		return check.fset.File(pos).Name()
   118  	}
   119  	return fmt.Sprintf("file[%d]", fileNo)
   120  }
   121  
   122  // collectObjects collects all file and package objects and inserts them
   123  // into their respective scopes. It also performs imports and associates
   124  // methods with receiver base type names.
   125  func (check *Checker) collectObjects() {
   126  	pkg := check.pkg
   127  
   128  	// pkgImports is the set of packages already imported by any package file seen
   129  	// so far. Used to avoid duplicate entries in pkg.imports. Allocate and populate
   130  	// it (pkg.imports may not be empty if we are checking test files incrementally).
   131  	var pkgImports = make(map[*Package]bool)
   132  	for _, imp := range pkg.imports {
   133  		pkgImports[imp] = true
   134  	}
   135  
   136  	// srcDir is the directory used by the Importer to look up packages.
   137  	// The typechecker itself doesn't need this information so it is not
   138  	// explicitly provided. Instead, we extract it from position info of
   139  	// the source files as needed.
   140  	// This is the only place where the type-checker (just the importer)
   141  	// needs to know the actual source location of a file.
   142  	// TODO(gri) can we come up with a better API instead?
   143  	var srcDir string
   144  	if len(check.files) > 0 {
   145  		// FileName may be "" (typically for tests) in which case
   146  		// we get "." as the srcDir which is what we would want.
   147  		srcDir = dir(check.fset.Position(check.files[0].Name.Pos()).Filename)
   148  	}
   149  
   150  	for fileNo, file := range check.files {
   151  		// The package identifier denotes the current package,
   152  		// but there is no corresponding package object.
   153  		check.recordDef(file.Name, nil)
   154  
   155  		// Use the actual source file extent rather than *ast.File extent since the
   156  		// latter doesn't include comments which appear at the start or end of the file.
   157  		// Be conservative and use the *ast.File extent if we don't have a *token.File.
   158  		pos, end := file.Pos(), file.End()
   159  		if f := check.fset.File(file.Pos()); f != nil {
   160  			pos, end = token.Pos(f.Base()), token.Pos(f.Base()+f.Size())
   161  		}
   162  		fileScope := NewScope(check.pkg.scope, pos, end, check.filename(fileNo))
   163  		check.recordScope(file, fileScope)
   164  
   165  		for _, decl := range file.Decls {
   166  			switch d := decl.(type) {
   167  			case *ast.BadDecl:
   168  				// ignore
   169  
   170  			case *ast.GenDecl:
   171  				var last *ast.ValueSpec // last ValueSpec with type or init exprs seen
   172  				for iota, spec := range d.Specs {
   173  					switch s := spec.(type) {
   174  					case *ast.ImportSpec:
   175  						// import package
   176  						var imp *Package
   177  						path, err := validatedImportPath(s.Path.Value)
   178  						if err != nil {
   179  							check.errorf(s.Path.Pos(), "invalid import path (%s)", err)
   180  							continue
   181  						}
   182  						if path == "C" && check.conf.FakeImportC {
   183  							// TODO(gri) shouldn't create a new one each time
   184  							imp = NewPackage("C", "C")
   185  							imp.fake = true
   186  						} else {
   187  							// ordinary import
   188  							if importer := check.conf.Importer; importer == nil {
   189  								err = fmt.Errorf("Config.Importer not installed")
   190  							} else if importerFrom, ok := importer.(ImporterFrom); ok {
   191  								imp, err = importerFrom.ImportFrom(path, srcDir, 0)
   192  								if imp == nil && err == nil {
   193  									err = fmt.Errorf("Config.Importer.ImportFrom(%s, %s, 0) returned nil but no error", path, pkg.path)
   194  								}
   195  							} else {
   196  								imp, err = importer.Import(path)
   197  								if imp == nil && err == nil {
   198  									err = fmt.Errorf("Config.Importer.Import(%s) returned nil but no error", path)
   199  								}
   200  							}
   201  							if err != nil {
   202  								check.errorf(s.Path.Pos(), "could not import %s (%s)", path, err)
   203  								continue
   204  							}
   205  						}
   206  
   207  						// add package to list of explicit imports
   208  						// (this functionality is provided as a convenience
   209  						// for clients; it is not needed for type-checking)
   210  						if !pkgImports[imp] {
   211  							pkgImports[imp] = true
   212  							if imp != Unsafe {
   213  								pkg.imports = append(pkg.imports, imp)
   214  							}
   215  						}
   216  
   217  						// local name overrides imported package name
   218  						name := imp.name
   219  						if s.Name != nil {
   220  							name = s.Name.Name
   221  							if path == "C" {
   222  								// match cmd/compile (not prescribed by spec)
   223  								check.errorf(s.Name.Pos(), `cannot rename import "C"`)
   224  								continue
   225  							}
   226  							if name == "init" {
   227  								check.errorf(s.Name.Pos(), "cannot declare init - must be func")
   228  								continue
   229  							}
   230  						}
   231  
   232  						obj := NewPkgName(s.Pos(), pkg, name, imp)
   233  						if s.Name != nil {
   234  							// in a dot-import, the dot represents the package
   235  							check.recordDef(s.Name, obj)
   236  						} else {
   237  							check.recordImplicit(s, obj)
   238  						}
   239  
   240  						if path == "C" {
   241  							// match cmd/compile (not prescribed by spec)
   242  							obj.used = true
   243  						}
   244  
   245  						// add import to file scope
   246  						if name == "." {
   247  							// merge imported scope with file scope
   248  							for _, obj := range imp.scope.elems {
   249  								// A package scope may contain non-exported objects,
   250  								// do not import them!
   251  								if obj.Exported() {
   252  									// TODO(gri) When we import a package, we create
   253  									// a new local package object. We should do the
   254  									// same for each dot-imported object. That way
   255  									// they can have correct position information.
   256  									// (We must not modify their existing position
   257  									// information because the same package - found
   258  									// via Config.Packages - may be dot-imported in
   259  									// another package!)
   260  									check.declare(fileScope, nil, obj, token.NoPos)
   261  									check.recordImplicit(s, obj)
   262  								}
   263  							}
   264  							// add position to set of dot-import positions for this file
   265  							// (this is only needed for "imported but not used" errors)
   266  							check.addUnusedDotImport(fileScope, imp, s.Pos())
   267  						} else {
   268  							// declare imported package object in file scope
   269  							check.declare(fileScope, nil, obj, token.NoPos)
   270  						}
   271  
   272  					case *ast.ValueSpec:
   273  						switch d.Tok {
   274  						case token.CONST:
   275  							// determine which initialization expressions to use
   276  							switch {
   277  							case s.Type != nil || len(s.Values) > 0:
   278  								last = s
   279  							case last == nil:
   280  								last = new(ast.ValueSpec) // make sure last exists
   281  							}
   282  
   283  							// declare all constants
   284  							for i, name := range s.Names {
   285  								obj := NewConst(name.Pos(), pkg, name.Name, nil, constant.MakeInt64(int64(iota)))
   286  
   287  								var init ast.Expr
   288  								if i < len(last.Values) {
   289  									init = last.Values[i]
   290  								}
   291  
   292  								d := &declInfo{file: fileScope, typ: last.Type, init: init}
   293  								check.declarePkgObj(name, obj, d)
   294  							}
   295  
   296  							check.arityMatch(s, last)
   297  
   298  						case token.VAR:
   299  							lhs := make([]*Var, len(s.Names))
   300  							// If there's exactly one rhs initializer, use
   301  							// the same declInfo d1 for all lhs variables
   302  							// so that each lhs variable depends on the same
   303  							// rhs initializer (n:1 var declaration).
   304  							var d1 *declInfo
   305  							if len(s.Values) == 1 {
   306  								// The lhs elements are only set up after the for loop below,
   307  								// but that's ok because declareVar only collects the declInfo
   308  								// for a later phase.
   309  								d1 = &declInfo{file: fileScope, lhs: lhs, typ: s.Type, init: s.Values[0]}
   310  							}
   311  
   312  							// declare all variables
   313  							for i, name := range s.Names {
   314  								obj := NewVar(name.Pos(), pkg, name.Name, nil)
   315  								lhs[i] = obj
   316  
   317  								d := d1
   318  								if d == nil {
   319  									// individual assignments
   320  									var init ast.Expr
   321  									if i < len(s.Values) {
   322  										init = s.Values[i]
   323  									}
   324  									d = &declInfo{file: fileScope, typ: s.Type, init: init}
   325  								}
   326  
   327  								check.declarePkgObj(name, obj, d)
   328  							}
   329  
   330  							check.arityMatch(s, nil)
   331  
   332  						default:
   333  							check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
   334  						}
   335  
   336  					case *ast.TypeSpec:
   337  						obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
   338  						check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, typ: s.Type})
   339  
   340  					default:
   341  						check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
   342  					}
   343  				}
   344  
   345  			case *ast.FuncDecl:
   346  				name := d.Name.Name
   347  				obj := NewFunc(d.Name.Pos(), pkg, name, nil)
   348  				if d.Recv == nil {
   349  					// regular function
   350  					if name == "init" {
   351  						// don't declare init functions in the package scope - they are invisible
   352  						obj.parent = pkg.scope
   353  						check.recordDef(d.Name, obj)
   354  						// init functions must have a body
   355  						if d.Body == nil {
   356  							check.softErrorf(obj.pos, "missing function body")
   357  						}
   358  					} else {
   359  						check.declare(pkg.scope, d.Name, obj, token.NoPos)
   360  					}
   361  				} else {
   362  					// method
   363  					check.recordDef(d.Name, obj)
   364  					// Associate method with receiver base type name, if possible.
   365  					// Ignore methods that have an invalid receiver, or a blank _
   366  					// receiver name. They will be type-checked later, with regular
   367  					// functions.
   368  					if list := d.Recv.List; len(list) > 0 {
   369  						typ := list[0].Type
   370  						if ptr, _ := typ.(*ast.StarExpr); ptr != nil {
   371  							typ = ptr.X
   372  						}
   373  						if base, _ := typ.(*ast.Ident); base != nil && base.Name != "_" {
   374  							check.assocMethod(base.Name, obj)
   375  						}
   376  					}
   377  				}
   378  				info := &declInfo{file: fileScope, fdecl: d}
   379  				check.objMap[obj] = info
   380  				obj.setOrder(uint32(len(check.objMap)))
   381  
   382  			default:
   383  				check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
   384  			}
   385  		}
   386  	}
   387  
   388  	// verify that objects in package and file scopes have different names
   389  	for _, scope := range check.pkg.scope.children /* file scopes */ {
   390  		for _, obj := range scope.elems {
   391  			if alt := pkg.scope.Lookup(obj.Name()); alt != nil {
   392  				if pkg, ok := obj.(*PkgName); ok {
   393  					check.errorf(alt.Pos(), "%s already declared through import of %s", alt.Name(), pkg.Imported())
   394  					check.reportAltDecl(pkg)
   395  				} else {
   396  					check.errorf(alt.Pos(), "%s already declared through dot-import of %s", alt.Name(), obj.Pkg())
   397  					// TODO(gri) dot-imported objects don't have a position; reportAltDecl won't print anything
   398  					check.reportAltDecl(obj)
   399  				}
   400  			}
   401  		}
   402  	}
   403  }
   404  
   405  // packageObjects typechecks all package objects in objList, but not function bodies.
   406  func (check *Checker) packageObjects(objList []Object) {
   407  	// add new methods to already type-checked types (from a prior Checker.Files call)
   408  	for _, obj := range objList {
   409  		if obj, _ := obj.(*TypeName); obj != nil && obj.typ != nil {
   410  			check.addMethodDecls(obj)
   411  		}
   412  	}
   413  
   414  	// pre-allocate space for type declaration paths so that the underlying array is reused
   415  	typePath := make([]*TypeName, 0, 8)
   416  
   417  	for _, obj := range objList {
   418  		check.objDecl(obj, nil, typePath)
   419  	}
   420  
   421  	// At this point we may have a non-empty check.methods map; this means that not all
   422  	// entries were deleted at the end of typeDecl because the respective receiver base
   423  	// types were not found. In that case, an error was reported when declaring those
   424  	// methods. We can now safely discard this map.
   425  	check.methods = nil
   426  }
   427  
   428  // functionBodies typechecks all function bodies.
   429  func (check *Checker) functionBodies() {
   430  	for _, f := range check.funcs {
   431  		check.funcBody(f.decl, f.name, f.sig, f.body)
   432  	}
   433  }
   434  
   435  // unusedImports checks for unused imports.
   436  func (check *Checker) unusedImports() {
   437  	// if function bodies are not checked, packages' uses are likely missing - don't check
   438  	if check.conf.IgnoreFuncBodies {
   439  		return
   440  	}
   441  
   442  	// spec: "It is illegal (...) to directly import a package without referring to
   443  	// any of its exported identifiers. To import a package solely for its side-effects
   444  	// (initialization), use the blank identifier as explicit package name."
   445  
   446  	// check use of regular imported packages
   447  	for _, scope := range check.pkg.scope.children /* file scopes */ {
   448  		for _, obj := range scope.elems {
   449  			if obj, ok := obj.(*PkgName); ok {
   450  				// Unused "blank imports" are automatically ignored
   451  				// since _ identifiers are not entered into scopes.
   452  				if !obj.used {
   453  					path := obj.imported.path
   454  					base := pkgName(path)
   455  					if obj.name == base {
   456  						check.softErrorf(obj.pos, "%q imported but not used", path)
   457  					} else {
   458  						check.softErrorf(obj.pos, "%q imported but not used as %s", path, obj.name)
   459  					}
   460  				}
   461  			}
   462  		}
   463  	}
   464  
   465  	// check use of dot-imported packages
   466  	for _, unusedDotImports := range check.unusedDotImports {
   467  		for pkg, pos := range unusedDotImports {
   468  			check.softErrorf(pos, "%q imported but not used", pkg.path)
   469  		}
   470  	}
   471  }
   472  
   473  // pkgName returns the package name (last element) of an import path.
   474  func pkgName(path string) string {
   475  	if i := strings.LastIndex(path, "/"); i >= 0 {
   476  		path = path[i+1:]
   477  	}
   478  	return path
   479  }
   480  
   481  // dir makes a good-faith attempt to return the directory
   482  // portion of path. If path is empty, the result is ".".
   483  // (Per the go/build package dependency tests, we cannot import
   484  // path/filepath and simply use filepath.Dir.)
   485  func dir(path string) string {
   486  	if i := strings.LastIndexAny(path, `/\`); i > 0 {
   487  		return path[:i]
   488  	}
   489  	// i <= 0
   490  	return "."
   491  }