Home | History | Annotate | Download | only in ast
      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 if name != "_" {
    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 }
    175