src/pkg/go/ast/resolve.go - The Go Programming Language

Golang

Source file src/pkg/go/ast/resolve.go

     1	// Copyright 2011 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	// This file implements NewPackage.
     6	
     7	package ast
     8	
     9	import (
    10		"fmt"
    11		"go/scanner"
    12		"go/token"
    13		"strconv"
    14	)
    15	
    16	type pkgBuilder struct {
    17		fset   *token.FileSet
    18		errors scanner.ErrorList
    19	}
    20	
    21	func (p *pkgBuilder) error(pos token.Pos, msg string) {
    22		p.errors.Add(p.fset.Position(pos), msg)
    23	}
    24	
    25	func (p *pkgBuilder) errorf(pos token.Pos, format string, args ...interface{}) {
    26		p.error(pos, fmt.Sprintf(format, args...))
    27	}
    28	
    29	func (p *pkgBuilder) declare(scope, altScope *Scope, obj *Object) {
    30		alt := scope.Insert(obj)
    31		if alt == nil && altScope != nil {
    32			// see if there is a conflicting declaration in altScope
    33			alt = altScope.Lookup(obj.Name)
    34		}
    35		if alt != nil {
    36			prevDecl := ""
    37			if pos := alt.Pos(); pos.IsValid() {
    38				prevDecl = fmt.Sprintf("\n\tprevious declaration at %s", p.fset.Position(pos))
    39			}
    40			p.error(obj.Pos(), fmt.Sprintf("%s redeclared in this block%s", obj.Name, prevDecl))
    41		}
    42	}
    43	
    44	func resolve(scope *Scope, ident *Ident) bool {
    45		for ; scope != nil; scope = scope.Outer {
    46			if obj := scope.Lookup(ident.Name); obj != nil {
    47				ident.Obj = obj
    48				return true
    49			}
    50		}
    51		return false
    52	}
    53	
    54	// An Importer resolves import paths to package Objects.
    55	// The imports map records the packages already imported,
    56	// indexed by package id (canonical import path).
    57	// An Importer must determine the canonical import path and
    58	// check the map to see if it is already present in the imports map.
    59	// If so, the Importer can return the map entry.  Otherwise, the
    60	// Importer should load the package data for the given path into 
    61	// a new *Object (pkg), record pkg in the imports map, and then
    62	// return pkg.
    63	type Importer func(imports map[string]*Object, path string) (pkg *Object, err error)
    64	
    65	// NewPackage creates a new Package node from a set of File nodes. It resolves
    66	// unresolved identifiers across files and updates each file's Unresolved list
    67	// accordingly. If a non-nil importer and universe scope are provided, they are
    68	// used to resolve identifiers not declared in any of the package files. Any
    69	// remaining unresolved identifiers are reported as undeclared. If the files
    70	// belong to different packages, one package name is selected and files with
    71	// different package names are reported and then ignored.
    72	// The result is a package node and a scanner.ErrorList if there were errors.
    73	//
    74	func NewPackage(fset *token.FileSet, files map[string]*File, importer Importer, universe *Scope) (*Package, error) {
    75		var p pkgBuilder
    76		p.fset = fset
    77	
    78		// complete package scope
    79		pkgName := ""
    80		pkgScope := NewScope(universe)
    81		for _, file := range files {
    82			// package names must match
    83			switch name := file.Name.Name; {
    84			case pkgName == "":
    85				pkgName = name
    86			case name != pkgName:
    87				p.errorf(file.Package, "package %s; expected %s", name, pkgName)
    88				continue // ignore this file
    89			}
    90	
    91			// collect top-level file objects in package scope
    92			for _, obj := range file.Scope.Objects {
    93				p.declare(pkgScope, nil, obj)
    94			}
    95		}
    96	
    97		// package global mapping of imported package ids to package objects
    98		imports := make(map[string]*Object)
    99	
   100		// complete file scopes with imports and resolve identifiers
   101		for _, file := range files {
   102			// ignore file if it belongs to a different package
   103			// (error has already been reported)
   104			if file.Name.Name != pkgName {
   105				continue
   106			}
   107	
   108			// build file scope by processing all imports
   109			importErrors := false
   110			fileScope := NewScope(pkgScope)
   111			for _, spec := range file.Imports {
   112				if importer == nil {
   113					importErrors = true
   114					continue
   115				}
   116				path, _ := strconv.Unquote(spec.Path.Value)
   117				pkg, err := importer(imports, path)
   118				if err != nil {
   119					p.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err)
   120					importErrors = true
   121					continue
   122				}
   123				// TODO(gri) If a local package name != "." is provided,
   124				// global identifier resolution could proceed even if the
   125				// import failed. Consider adjusting the logic here a bit.
   126	
   127				// local name overrides imported package name
   128				name := pkg.Name
   129				if spec.Name != nil {
   130					name = spec.Name.Name
   131				}
   132	
   133				// add import to file scope
   134				if name == "." {
   135					// merge imported scope with file scope
   136					for _, obj := range pkg.Data.(*Scope).Objects {
   137						p.declare(fileScope, pkgScope, obj)
   138					}
   139				} else {
   140					// declare imported package object in file scope
   141					// (do not re-use pkg in the file scope but create
   142					// a new object instead; the Decl field is different
   143					// for different files)
   144					obj := NewObj(Pkg, name)
   145					obj.Decl = spec
   146					obj.Data = pkg.Data
   147					p.declare(fileScope, pkgScope, obj)
   148				}
   149			}
   150	
   151			// resolve identifiers
   152			if importErrors {
   153				// don't use the universe scope without correct imports
   154				// (objects in the universe may be shadowed by imports;
   155				// with missing imports, identifiers might get resolved
   156				// incorrectly to universe objects)
   157				pkgScope.Outer = nil
   158			}
   159			i := 0
   160			for _, ident := range file.Unresolved {
   161				if !resolve(fileScope, ident) {
   162					p.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
   163					file.Unresolved[i] = ident
   164					i++
   165				}
   166	
   167			}
   168			file.Unresolved = file.Unresolved[0:i]
   169			pkgScope.Outer = universe // reset universe scope
   170		}
   171	
   172		p.errors.Sort()
   173		return &Package{pkgName, pkgScope, imports, files}, p.errors.Err()
   174	}