]> Cypherpunks.ru repositories - gostls13.git/blob - src/go/ast/resolve.go
go/ast: deprecate Object
[gostls13.git] / src / 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 ...any) {
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 //
64 // Deprecated: use the type checker [go/types] instead; see [Object].
65 type Importer func(imports map[string]*Object, path string) (pkg *Object, err error)
66
67 // NewPackage creates a new Package node from a set of File nodes. It resolves
68 // unresolved identifiers across files and updates each file's Unresolved list
69 // accordingly. If a non-nil importer and universe scope are provided, they are
70 // used to resolve identifiers not declared in any of the package files. Any
71 // remaining unresolved identifiers are reported as undeclared. If the files
72 // belong to different packages, one package name is selected and files with
73 // different package names are reported and then ignored.
74 // The result is a package node and a scanner.ErrorList if there were errors.
75 //
76 // Deprecated: use the type checker [go/types] instead; see [Object].
77 func NewPackage(fset *token.FileSet, files map[string]*File, importer Importer, universe *Scope) (*Package, error) {
78         var p pkgBuilder
79         p.fset = fset
80
81         // complete package scope
82         pkgName := ""
83         pkgScope := NewScope(universe)
84         for _, file := range files {
85                 // package names must match
86                 switch name := file.Name.Name; {
87                 case pkgName == "":
88                         pkgName = name
89                 case name != pkgName:
90                         p.errorf(file.Package, "package %s; expected %s", name, pkgName)
91                         continue // ignore this file
92                 }
93
94                 // collect top-level file objects in package scope
95                 for _, obj := range file.Scope.Objects {
96                         p.declare(pkgScope, nil, obj)
97                 }
98         }
99
100         // package global mapping of imported package ids to package objects
101         imports := make(map[string]*Object)
102
103         // complete file scopes with imports and resolve identifiers
104         for _, file := range files {
105                 // ignore file if it belongs to a different package
106                 // (error has already been reported)
107                 if file.Name.Name != pkgName {
108                         continue
109                 }
110
111                 // build file scope by processing all imports
112                 importErrors := false
113                 fileScope := NewScope(pkgScope)
114                 for _, spec := range file.Imports {
115                         if importer == nil {
116                                 importErrors = true
117                                 continue
118                         }
119                         path, _ := strconv.Unquote(spec.Path.Value)
120                         pkg, err := importer(imports, path)
121                         if err != nil {
122                                 p.errorf(spec.Path.Pos(), "could not import %s (%s)", path, err)
123                                 importErrors = true
124                                 continue
125                         }
126                         // TODO(gri) If a local package name != "." is provided,
127                         // global identifier resolution could proceed even if the
128                         // import failed. Consider adjusting the logic here a bit.
129
130                         // local name overrides imported package name
131                         name := pkg.Name
132                         if spec.Name != nil {
133                                 name = spec.Name.Name
134                         }
135
136                         // add import to file scope
137                         if name == "." {
138                                 // merge imported scope with file scope
139                                 for _, obj := range pkg.Data.(*Scope).Objects {
140                                         p.declare(fileScope, pkgScope, obj)
141                                 }
142                         } else if name != "_" {
143                                 // declare imported package object in file scope
144                                 // (do not re-use pkg in the file scope but create
145                                 // a new object instead; the Decl field is different
146                                 // for different files)
147                                 obj := NewObj(Pkg, name)
148                                 obj.Decl = spec
149                                 obj.Data = pkg.Data
150                                 p.declare(fileScope, pkgScope, obj)
151                         }
152                 }
153
154                 // resolve identifiers
155                 if importErrors {
156                         // don't use the universe scope without correct imports
157                         // (objects in the universe may be shadowed by imports;
158                         // with missing imports, identifiers might get resolved
159                         // incorrectly to universe objects)
160                         pkgScope.Outer = nil
161                 }
162                 i := 0
163                 for _, ident := range file.Unresolved {
164                         if !resolve(fileScope, ident) {
165                                 p.errorf(ident.Pos(), "undeclared name: %s", ident.Name)
166                                 file.Unresolved[i] = ident
167                                 i++
168                         }
169
170                 }
171                 file.Unresolved = file.Unresolved[0:i]
172                 pkgScope.Outer = universe // reset universe scope
173         }
174
175         p.errors.Sort()
176         return &Package{pkgName, pkgScope, imports, files}, p.errors.Err()
177 }