Source file
src/go/types/resolver.go
1
2
3
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
18 type declInfo struct {
19 file *Scope
20 lhs []*Var
21 typ ast.Expr
22 init ast.Expr
23 fdecl *ast.FuncDecl
24 alias bool
25
26
27
28
29 deps objSet
30 }
31
32
33 type objSet map[Object]bool
34
35
36
37 func (d *declInfo) hasInitializer() bool {
38 return d.init != nil || d.fdecl != nil && d.fdecl.Body != nil
39 }
40
41
42 func (d *declInfo) addDep(obj Object) {
43 m := d.deps
44 if m == nil {
45 m = make(objSet)
46 d.deps = m
47 }
48 m[obj] = true
49 }
50
51
52
53
54
55 func (check *Checker) arityMatch(s, init *ast.ValueSpec) {
56 l := len(s.Names)
57 r := len(s.Values)
58 if init != nil {
59 r = len(init.Values)
60 }
61
62 switch {
63 case init == nil && r == 0:
64
65 if s.Type == nil {
66 check.errorf(s.Pos(), "missing type or init expr")
67 }
68 case l < r:
69 if l < len(s.Values) {
70
71 n := s.Values[l]
72 check.errorf(n.Pos(), "extra init expr %s", n)
73
74 } else {
75
76 check.errorf(s.Pos(), "extra init expr at %s", check.fset.Position(init.Pos()))
77
78 }
79 case l > r && (init != nil || r != 1):
80 n := s.Names[r]
81 check.errorf(n.Pos(), "missing init expr for %s", n)
82 }
83 }
84
85 func validatedImportPath(path string) (string, error) {
86 s, err := strconv.Unquote(path)
87 if err != nil {
88 return "", err
89 }
90 if s == "" {
91 return "", fmt.Errorf("empty string")
92 }
93 const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
94 for _, r := range s {
95 if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
96 return s, fmt.Errorf("invalid character %#U", r)
97 }
98 }
99 return s, nil
100 }
101
102
103
104 func (check *Checker) declarePkgObj(ident *ast.Ident, obj Object, d *declInfo) {
105 assert(ident.Name == obj.Name())
106
107
108
109 if ident.Name == "init" {
110 check.errorf(ident.Pos(), "cannot declare init - must be func")
111 return
112 }
113
114
115
116 if ident.Name == "main" && check.pkg.name == "main" {
117 check.errorf(ident.Pos(), "cannot declare main - must be func")
118 return
119 }
120
121 check.declare(check.pkg.scope, ident, obj, token.NoPos)
122 check.objMap[obj] = d
123 obj.setOrder(uint32(len(check.objMap)))
124 }
125
126
127 func (check *Checker) filename(fileNo int) string {
128 file := check.files[fileNo]
129 if pos := file.Pos(); pos.IsValid() {
130 return check.fset.File(pos).Name()
131 }
132 return fmt.Sprintf("file[%d]", fileNo)
133 }
134
135 func (check *Checker) importPackage(pos token.Pos, path, dir string) *Package {
136
137
138
139
140
141 key := importKey{path, dir}
142 imp := check.impMap[key]
143 if imp != nil {
144 return imp
145 }
146
147
148 if path == "C" && check.conf.FakeImportC {
149 imp = NewPackage("C", "C")
150 imp.fake = true
151 } else {
152
153 var err error
154 if importer := check.conf.Importer; importer == nil {
155 err = fmt.Errorf("Config.Importer not installed")
156 } else if importerFrom, ok := importer.(ImporterFrom); ok {
157 imp, err = importerFrom.ImportFrom(path, dir, 0)
158 if imp == nil && err == nil {
159 err = fmt.Errorf("Config.Importer.ImportFrom(%s, %s, 0) returned nil but no error", path, dir)
160 }
161 } else {
162 imp, err = importer.Import(path)
163 if imp == nil && err == nil {
164 err = fmt.Errorf("Config.Importer.Import(%s) returned nil but no error", path)
165 }
166 }
167
168
169 if err == nil && imp != nil && (imp.name == "_" || imp.name == "") {
170 err = fmt.Errorf("invalid package name: %q", imp.name)
171 imp = nil
172 }
173 if err != nil {
174 check.errorf(pos, "could not import %s (%s)", path, err)
175 if imp == nil {
176
177
178 name := path
179 if i := len(name); i > 0 && name[i-1] == '/' {
180 name = name[:i-1]
181 }
182 if i := strings.LastIndex(name, "/"); i >= 0 {
183 name = name[i+1:]
184 }
185 imp = NewPackage(path, name)
186 }
187
188 imp.fake = true
189 }
190 }
191
192
193 if imp.complete || imp.fake {
194 check.impMap[key] = imp
195 return imp
196 }
197
198
199 return nil
200 }
201
202
203
204
205 func (check *Checker) collectObjects() {
206 pkg := check.pkg
207
208
209
210
211
212
213
214 var pkgImports = make(map[*Package]bool)
215 for _, imp := range pkg.imports {
216 pkgImports[imp] = true
217 }
218
219 for fileNo, file := range check.files {
220
221
222 check.recordDef(file.Name, nil)
223
224
225
226
227 pos, end := file.Pos(), file.End()
228 if f := check.fset.File(file.Pos()); f != nil {
229 pos, end = token.Pos(f.Base()), token.Pos(f.Base()+f.Size())
230 }
231 fileScope := NewScope(check.pkg.scope, pos, end, check.filename(fileNo))
232 check.recordScope(file, fileScope)
233
234
235
236
237 fileDir := dir(check.fset.Position(file.Name.Pos()).Filename)
238
239 for _, decl := range file.Decls {
240 switch d := decl.(type) {
241 case *ast.BadDecl:
242
243
244 case *ast.GenDecl:
245 var last *ast.ValueSpec
246 for iota, spec := range d.Specs {
247 switch s := spec.(type) {
248 case *ast.ImportSpec:
249
250 path, err := validatedImportPath(s.Path.Value)
251 if err != nil {
252 check.errorf(s.Path.Pos(), "invalid import path (%s)", err)
253 continue
254 }
255
256 imp := check.importPackage(s.Path.Pos(), path, fileDir)
257 if imp == nil {
258 continue
259 }
260
261
262
263
264 if !pkgImports[imp] {
265 pkgImports[imp] = true
266 pkg.imports = append(pkg.imports, imp)
267 }
268
269
270 name := imp.name
271 if s.Name != nil {
272 name = s.Name.Name
273 if path == "C" {
274
275 check.errorf(s.Name.Pos(), `cannot rename import "C"`)
276 continue
277 }
278 if name == "init" {
279 check.errorf(s.Name.Pos(), "cannot declare init - must be func")
280 continue
281 }
282 }
283
284 obj := NewPkgName(s.Pos(), pkg, name, imp)
285 if s.Name != nil {
286
287 check.recordDef(s.Name, obj)
288 } else {
289 check.recordImplicit(s, obj)
290 }
291
292 if path == "C" {
293
294 obj.used = true
295 }
296
297
298 if name == "." {
299
300 for _, obj := range imp.scope.elems {
301
302
303 if obj.Exported() {
304
305
306
307
308
309
310
311
312 check.declare(fileScope, nil, obj, token.NoPos)
313 }
314 }
315
316
317 check.addUnusedDotImport(fileScope, imp, s.Pos())
318 } else {
319
320 check.declare(fileScope, nil, obj, token.NoPos)
321 }
322
323 case *ast.ValueSpec:
324 switch d.Tok {
325 case token.CONST:
326
327 switch {
328 case s.Type != nil || len(s.Values) > 0:
329 last = s
330 case last == nil:
331 last = new(ast.ValueSpec)
332 }
333
334
335 for i, name := range s.Names {
336 obj := NewConst(name.Pos(), pkg, name.Name, nil, constant.MakeInt64(int64(iota)))
337
338 var init ast.Expr
339 if i < len(last.Values) {
340 init = last.Values[i]
341 }
342
343 d := &declInfo{file: fileScope, typ: last.Type, init: init}
344 check.declarePkgObj(name, obj, d)
345 }
346
347 check.arityMatch(s, last)
348
349 case token.VAR:
350 lhs := make([]*Var, len(s.Names))
351
352
353
354
355 var d1 *declInfo
356 if len(s.Values) == 1 {
357
358
359
360 d1 = &declInfo{file: fileScope, lhs: lhs, typ: s.Type, init: s.Values[0]}
361 }
362
363
364 for i, name := range s.Names {
365 obj := NewVar(name.Pos(), pkg, name.Name, nil)
366 lhs[i] = obj
367
368 d := d1
369 if d == nil {
370
371 var init ast.Expr
372 if i < len(s.Values) {
373 init = s.Values[i]
374 }
375 d = &declInfo{file: fileScope, typ: s.Type, init: init}
376 }
377
378 check.declarePkgObj(name, obj, d)
379 }
380
381 check.arityMatch(s, nil)
382
383 default:
384 check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
385 }
386
387 case *ast.TypeSpec:
388 obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
389 check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, typ: s.Type, alias: s.Assign.IsValid()})
390
391 default:
392 check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
393 }
394 }
395
396 case *ast.FuncDecl:
397 name := d.Name.Name
398 obj := NewFunc(d.Name.Pos(), pkg, name, nil)
399 if d.Recv == nil {
400
401 if name == "init" {
402
403 obj.parent = pkg.scope
404 check.recordDef(d.Name, obj)
405
406 if d.Body == nil {
407 check.softErrorf(obj.pos, "missing function body")
408 }
409 } else {
410 check.declare(pkg.scope, d.Name, obj, token.NoPos)
411 }
412 } else {
413
414 check.recordDef(d.Name, obj)
415
416
417
418
419 if list := d.Recv.List; len(list) > 0 {
420 typ := unparen(list[0].Type)
421 if ptr, _ := typ.(*ast.StarExpr); ptr != nil {
422 typ = unparen(ptr.X)
423 }
424 if base, _ := typ.(*ast.Ident); base != nil && base.Name != "_" {
425 check.assocMethod(base.Name, obj)
426 }
427 }
428 }
429 info := &declInfo{file: fileScope, fdecl: d}
430 check.objMap[obj] = info
431 obj.setOrder(uint32(len(check.objMap)))
432
433 default:
434 check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
435 }
436 }
437 }
438
439
440 for _, scope := range check.pkg.scope.children {
441 for _, obj := range scope.elems {
442 if alt := pkg.scope.Lookup(obj.Name()); alt != nil {
443 if pkg, ok := obj.(*PkgName); ok {
444 check.errorf(alt.Pos(), "%s already declared through import of %s", alt.Name(), pkg.Imported())
445 check.reportAltDecl(pkg)
446 } else {
447 check.errorf(alt.Pos(), "%s already declared through dot-import of %s", alt.Name(), obj.Pkg())
448
449 check.reportAltDecl(obj)
450 }
451 }
452 }
453 }
454 }
455
456
457 func (check *Checker) packageObjects(objList []Object) {
458
459 for _, obj := range objList {
460 if obj, _ := obj.(*TypeName); obj != nil && obj.typ != nil {
461 check.addMethodDecls(obj)
462 }
463 }
464
465
466 typePath := make([]*TypeName, 0, 8)
467
468 for _, obj := range objList {
469 check.objDecl(obj, nil, typePath)
470 }
471
472
473
474
475
476 check.methods = nil
477 }
478
479
480 func (check *Checker) functionBodies() {
481 for _, f := range check.funcs {
482 check.funcBody(f.decl, f.name, f.sig, f.body)
483 }
484 }
485
486
487 func (check *Checker) unusedImports() {
488
489 if check.conf.IgnoreFuncBodies {
490 return
491 }
492
493
494
495
496
497
498 for _, scope := range check.pkg.scope.children {
499 for _, obj := range scope.elems {
500 if obj, ok := obj.(*PkgName); ok {
501
502
503 if !obj.used {
504 path := obj.imported.path
505 base := pkgName(path)
506 if obj.name == base {
507 check.softErrorf(obj.pos, "%q imported but not used", path)
508 } else {
509 check.softErrorf(obj.pos, "%q imported but not used as %s", path, obj.name)
510 }
511 }
512 }
513 }
514 }
515
516
517 for _, unusedDotImports := range check.unusedDotImports {
518 for pkg, pos := range unusedDotImports {
519 check.softErrorf(pos, "%q imported but not used", pkg.path)
520 }
521 }
522 }
523
524
525 func pkgName(path string) string {
526 if i := strings.LastIndex(path, "/"); i >= 0 {
527 path = path[i+1:]
528 }
529 return path
530 }
531
532
533
534
535
536 func dir(path string) string {
537 if i := strings.LastIndexAny(path, `/\`); i > 0 {
538 return path[:i]
539 }
540
541 return "."
542 }
543
View as plain text