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