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 "sort"
13 "strconv"
14 "strings"
15 "unicode"
16 )
17
18
19 type declInfo struct {
20 file *Scope
21 lhs []*Var
22 typ ast.Expr
23 init ast.Expr
24 fdecl *ast.FuncDecl
25 alias bool
26
27
28 deps objSet
29 }
30
31
32 type objSet map[Object]bool
33
34
35
36 func (d *declInfo) hasInitializer() bool {
37 return d.init != nil || d.fdecl != nil && d.fdecl.Body != nil
38 }
39
40
41 func (d *declInfo) addDep(obj Object) {
42 m := d.deps
43 if m == nil {
44 m = make(objSet)
45 d.deps = m
46 }
47 m[obj] = true
48 }
49
50
51
52
53
54 func (check *Checker) arityMatch(s, init *ast.ValueSpec) {
55 l := len(s.Names)
56 r := len(s.Values)
57 if init != nil {
58 r = len(init.Values)
59 }
60
61 switch {
62 case init == nil && r == 0:
63
64 if s.Type == nil {
65 check.errorf(s.Pos(), "missing type or init expr")
66 }
67 case l < r:
68 if l < len(s.Values) {
69
70 n := s.Values[l]
71 check.errorf(n.Pos(), "extra init expr %s", n)
72
73 } else {
74
75 check.errorf(s.Pos(), "extra init expr at %s", check.fset.Position(init.Pos()))
76
77 }
78 case l > r && (init != nil || r != 1):
79 n := s.Names[r]
80 check.errorf(n.Pos(), "missing init expr for %s", n)
81 }
82 }
83
84 func validatedImportPath(path string) (string, error) {
85 s, err := strconv.Unquote(path)
86 if err != nil {
87 return "", err
88 }
89 if s == "" {
90 return "", fmt.Errorf("empty string")
91 }
92 const illegalChars = `!"#$%&'()*,:;<=>?[\]^{|}` + "`\uFFFD"
93 for _, r := range s {
94 if !unicode.IsGraphic(r) || unicode.IsSpace(r) || strings.ContainsRune(illegalChars, r) {
95 return s, fmt.Errorf("invalid character %#U", r)
96 }
97 }
98 return s, nil
99 }
100
101
102
103 func (check *Checker) declarePkgObj(ident *ast.Ident, obj Object, d *declInfo) {
104 assert(ident.Name == obj.Name())
105
106
107
108 if ident.Name == "init" {
109 check.errorf(ident.Pos(), "cannot declare init - must be func")
110 return
111 }
112
113
114
115 if ident.Name == "main" && check.pkg.name == "main" {
116 check.errorf(ident.Pos(), "cannot declare main - must be func")
117 return
118 }
119
120 check.declare(check.pkg.scope, ident, obj, token.NoPos)
121 check.objMap[obj] = d
122 obj.setOrder(uint32(len(check.objMap)))
123 }
124
125
126 func (check *Checker) filename(fileNo int) string {
127 file := check.files[fileNo]
128 if pos := file.Pos(); pos.IsValid() {
129 return check.fset.File(pos).Name()
130 }
131 return fmt.Sprintf("file[%d]", fileNo)
132 }
133
134 func (check *Checker) importPackage(pos token.Pos, path, dir string) *Package {
135
136
137
138
139
140 key := importKey{path, dir}
141 imp := check.impMap[key]
142 if imp != nil {
143 return imp
144 }
145
146
147 if path == "C" && check.conf.FakeImportC {
148 imp = NewPackage("C", "C")
149 imp.fake = true
150 } else {
151
152 var err error
153 if importer := check.conf.Importer; importer == nil {
154 err = fmt.Errorf("Config.Importer not installed")
155 } else if importerFrom, ok := importer.(ImporterFrom); ok {
156 imp, err = importerFrom.ImportFrom(path, dir, 0)
157 if imp == nil && err == nil {
158 err = fmt.Errorf("Config.Importer.ImportFrom(%s, %s, 0) returned nil but no error", path, dir)
159 }
160 } else {
161 imp, err = importer.Import(path)
162 if imp == nil && err == nil {
163 err = fmt.Errorf("Config.Importer.Import(%s) returned nil but no error", path)
164 }
165 }
166
167
168 if err == nil && imp != nil && (imp.name == "_" || imp.name == "") {
169 err = fmt.Errorf("invalid package name: %q", imp.name)
170 imp = nil
171 }
172 if err != nil {
173 check.errorf(pos, "could not import %s (%s)", path, err)
174 if imp == nil {
175
176
177 name := path
178 if i := len(name); i > 0 && name[i-1] == '/' {
179 name = name[:i-1]
180 }
181 if i := strings.LastIndex(name, "/"); i >= 0 {
182 name = name[i+1:]
183 }
184 imp = NewPackage(path, name)
185 }
186
187 imp.fake = true
188 }
189 }
190
191
192 if imp.complete || imp.fake {
193 check.impMap[key] = imp
194 return imp
195 }
196
197
198 return nil
199 }
200
201
202
203
204 func (check *Checker) collectObjects() {
205 pkg := check.pkg
206
207
208
209
210
211
212
213 var pkgImports = make(map[*Package]bool)
214 for _, imp := range pkg.imports {
215 pkgImports[imp] = true
216 }
217
218 var methods []*Func
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 if alt := fileScope.Insert(obj); alt != nil {
310 check.errorf(s.Name.Pos(), "%s redeclared in this block", obj.Name())
311 check.reportAltDecl(alt)
312 }
313 }
314 }
315
316
317 check.addUnusedDotImport(fileScope, imp, s.Pos())
318 } else {
319
320
321 check.declare(fileScope, nil, obj, token.NoPos)
322 }
323
324 case *ast.ValueSpec:
325 switch d.Tok {
326 case token.CONST:
327
328 switch {
329 case s.Type != nil || len(s.Values) > 0:
330 last = s
331 case last == nil:
332 last = new(ast.ValueSpec)
333 }
334
335
336 for i, name := range s.Names {
337 obj := NewConst(name.Pos(), pkg, name.Name, nil, constant.MakeInt64(int64(iota)))
338
339 var init ast.Expr
340 if i < len(last.Values) {
341 init = last.Values[i]
342 }
343
344 d := &declInfo{file: fileScope, typ: last.Type, init: init}
345 check.declarePkgObj(name, obj, d)
346 }
347
348 check.arityMatch(s, last)
349
350 case token.VAR:
351 lhs := make([]*Var, len(s.Names))
352
353
354
355
356 var d1 *declInfo
357 if len(s.Values) == 1 {
358
359
360
361 d1 = &declInfo{file: fileScope, lhs: lhs, typ: s.Type, init: s.Values[0]}
362 }
363
364
365 for i, name := range s.Names {
366 obj := NewVar(name.Pos(), pkg, name.Name, nil)
367 lhs[i] = obj
368
369 d := d1
370 if d == nil {
371
372 var init ast.Expr
373 if i < len(s.Values) {
374 init = s.Values[i]
375 }
376 d = &declInfo{file: fileScope, typ: s.Type, init: init}
377 }
378
379 check.declarePkgObj(name, obj, d)
380 }
381
382 check.arityMatch(s, nil)
383
384 default:
385 check.invalidAST(s.Pos(), "invalid token %s", d.Tok)
386 }
387
388 case *ast.TypeSpec:
389 obj := NewTypeName(s.Name.Pos(), pkg, s.Name.Name, nil)
390 check.declarePkgObj(s.Name, obj, &declInfo{file: fileScope, typ: s.Type, alias: s.Assign.IsValid()})
391
392 default:
393 check.invalidAST(s.Pos(), "unknown ast.Spec node %T", s)
394 }
395 }
396
397 case *ast.FuncDecl:
398 name := d.Name.Name
399 obj := NewFunc(d.Name.Pos(), pkg, name, nil)
400 if d.Recv == nil {
401
402 if name == "init" {
403
404 obj.parent = pkg.scope
405 check.recordDef(d.Name, obj)
406
407 if d.Body == nil {
408 check.softErrorf(obj.pos, "missing function body")
409 }
410 } else {
411 check.declare(pkg.scope, d.Name, obj, token.NoPos)
412 }
413 } else {
414
415
416
417
418 if name != "_" {
419 methods = append(methods, obj)
420 }
421 check.recordDef(d.Name, obj)
422 }
423 info := &declInfo{file: fileScope, fdecl: d}
424
425
426
427
428 check.objMap[obj] = info
429 obj.setOrder(uint32(len(check.objMap)))
430
431 default:
432 check.invalidAST(d.Pos(), "unknown ast.Decl node %T", d)
433 }
434 }
435 }
436
437
438 for _, scope := range check.pkg.scope.children {
439 for _, obj := range scope.elems {
440 if alt := pkg.scope.Lookup(obj.Name()); alt != nil {
441 if pkg, ok := obj.(*PkgName); ok {
442 check.errorf(alt.Pos(), "%s already declared through import of %s", alt.Name(), pkg.Imported())
443 check.reportAltDecl(pkg)
444 } else {
445 check.errorf(alt.Pos(), "%s already declared through dot-import of %s", alt.Name(), obj.Pkg())
446
447 check.reportAltDecl(obj)
448 }
449 }
450 }
451 }
452
453
454
455
456
457 if methods == nil {
458 return
459 }
460 check.methods = make(map[*TypeName][]*Func)
461 for _, f := range methods {
462 fdecl := check.objMap[f].fdecl
463 if list := fdecl.Recv.List; len(list) > 0 {
464
465
466 ptr, base := check.resolveBaseTypeName(list[0].Type)
467 if base != nil {
468 f.hasPtrRecv = ptr
469 check.methods[base] = append(check.methods[base], f)
470 }
471 }
472 }
473 }
474
475
476
477
478
479 func (check *Checker) resolveBaseTypeName(typ ast.Expr) (ptr bool, base *TypeName) {
480
481
482
483
484
485 var path []*TypeName
486 for {
487 typ = unparen(typ)
488
489
490 if pexpr, _ := typ.(*ast.StarExpr); pexpr != nil {
491
492 if ptr {
493 return false, nil
494 }
495 ptr = true
496 typ = unparen(pexpr.X)
497 }
498
499
500 name, _ := typ.(*ast.Ident)
501 if name == nil {
502 return false, nil
503 }
504
505
506
507 obj := check.pkg.scope.Lookup(name.Name)
508 if obj == nil {
509 return false, nil
510 }
511
512
513 tname, _ := obj.(*TypeName)
514 if tname == nil {
515 return false, nil
516 }
517
518
519 if check.cycle(tname, path, false) {
520 return false, nil
521 }
522
523
524
525 tdecl := check.objMap[tname]
526 if !tdecl.alias {
527 return ptr, tname
528 }
529
530
531 typ = tdecl.typ
532 path = append(path, tname)
533 }
534 }
535
536
537
538 func (check *Checker) cycle(obj *TypeName, path []*TypeName, report bool) bool {
539
540 for i, prev := range path {
541 if prev == obj {
542 if report {
543 check.errorf(obj.pos, "illegal cycle in declaration of %s", obj.name)
544
545 for _, obj := range path[i:] {
546 check.errorf(obj.Pos(), "\t%s refers to", obj.Name())
547 }
548 check.errorf(obj.Pos(), "\t%s", obj.Name())
549 }
550 return true
551 }
552 }
553 return false
554 }
555
556
557 func (check *Checker) packageObjects() {
558
559 objList := make([]Object, len(check.objMap))
560 i := 0
561 for obj := range check.objMap {
562 objList[i] = obj
563 i++
564 }
565 sort.Sort(inSourceOrder(objList))
566
567
568 for _, obj := range objList {
569 if obj, _ := obj.(*TypeName); obj != nil && obj.typ != nil {
570 check.addMethodDecls(obj)
571 }
572 }
573
574
575
576
577
578
579
580 var aliasList []*TypeName
581
582 for _, obj := range objList {
583
584 if tname, _ := obj.(*TypeName); tname != nil && check.objMap[tname].alias {
585 aliasList = append(aliasList, tname)
586 continue
587 }
588
589 check.objDecl(obj, nil)
590 }
591
592 for _, obj := range aliasList {
593 check.objDecl(obj, nil)
594 }
595
596
597
598
599
600 check.methods = nil
601 }
602
603
604 type inSourceOrder []Object
605
606 func (a inSourceOrder) Len() int { return len(a) }
607 func (a inSourceOrder) Less(i, j int) bool { return a[i].order() < a[j].order() }
608 func (a inSourceOrder) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
609
610
611 func (check *Checker) processDelayed(top int) {
612 for len(check.delayed) > top {
613 i := len(check.delayed) - 1
614 f := check.delayed[i]
615 check.delayed = check.delayed[:i]
616 f()
617 }
618 }
619
620
621 func (check *Checker) unusedImports() {
622
623 if check.conf.IgnoreFuncBodies {
624 return
625 }
626
627
628
629
630
631
632 for _, scope := range check.pkg.scope.children {
633 for _, obj := range scope.elems {
634 if obj, ok := obj.(*PkgName); ok {
635
636
637 if !obj.used {
638 path := obj.imported.path
639 base := pkgName(path)
640 if obj.name == base {
641 check.softErrorf(obj.pos, "%q imported but not used", path)
642 } else {
643 check.softErrorf(obj.pos, "%q imported but not used as %s", path, obj.name)
644 }
645 }
646 }
647 }
648 }
649
650
651 for _, unusedDotImports := range check.unusedDotImports {
652 for pkg, pos := range unusedDotImports {
653 check.softErrorf(pos, "%q imported but not used", pkg.path)
654 }
655 }
656 }
657
658
659 func pkgName(path string) string {
660 if i := strings.LastIndex(path, "/"); i >= 0 {
661 path = path[i+1:]
662 }
663 return path
664 }
665
666
667
668
669
670 func dir(path string) string {
671 if i := strings.LastIndexAny(path, `/\`); i > 0 {
672 return path[:i]
673 }
674
675 return "."
676 }
677
View as plain text