Source file
src/go/parser/resolver_test.go
1
2
3
4
5 package parser
6
7 import (
8 "fmt"
9 "go/ast"
10 "go/internal/typeparams"
11 "go/scanner"
12 "go/token"
13 "os"
14 "path/filepath"
15 "strings"
16 "testing"
17 )
18
19
20
21
22
23
24
25
26
27
28
29
30
31 func TestResolution(t *testing.T) {
32 dir := filepath.Join("testdata", "resolution")
33 fis, err := os.ReadDir(dir)
34 if err != nil {
35 t.Fatal(err)
36 }
37
38 for _, fi := range fis {
39 t.Run(fi.Name(), func(t *testing.T) {
40 fset := token.NewFileSet()
41 path := filepath.Join(dir, fi.Name())
42 src := readFile(path)
43 var mode Mode
44 if !strings.HasSuffix(path, ".go2") {
45 mode |= typeparams.DisallowParsing
46 }
47 file, err := ParseFile(fset, path, src, mode)
48 if err != nil {
49 t.Fatal(err)
50 }
51
52
53
54
55 handle := fset.File(file.Package)
56 fromParser := declsFromParser(file)
57 fromComments := declsFromComments(handle, src)
58
59 pos := func(pos token.Pos) token.Position {
60 p := handle.Position(pos)
61
62
63 p.Filename = ""
64 return p
65 }
66 for k, want := range fromComments {
67 if got := fromParser[k]; got != want {
68 t.Errorf("%s resolved to %s, want %s", pos(k), pos(got), pos(want))
69 }
70 delete(fromParser, k)
71 }
72
73 for k, got := range fromParser {
74 t.Errorf("%s resolved to %s, want no object", pos(k), pos(got))
75 }
76 })
77 }
78 }
79
80
81
82 func declsFromParser(file *ast.File) map[token.Pos]token.Pos {
83 objmap := map[token.Pos]token.Pos{}
84 ast.Inspect(file, func(node ast.Node) bool {
85
86 if ident, _ := node.(*ast.Ident); ident != nil && ident.Obj != nil && ident.Name != "_" {
87 objmap[ident.Pos()] = ident.Obj.Pos()
88 }
89 return true
90 })
91 return objmap
92 }
93
94
95
96
97 func declsFromComments(handle *token.File, src []byte) map[token.Pos]token.Pos {
98 decls, uses := positionMarkers(handle, src)
99
100 objmap := make(map[token.Pos]token.Pos)
101
102 for name, posns := range uses {
103 declpos, ok := decls[name]
104 if !ok {
105 panic(fmt.Sprintf("missing declaration for %s", name))
106 }
107 for _, pos := range posns {
108 objmap[pos] = declpos
109 }
110 }
111 return objmap
112 }
113
114
115
116
117
118 func positionMarkers(handle *token.File, src []byte) (decls map[string]token.Pos, uses map[string][]token.Pos) {
119 var s scanner.Scanner
120 s.Init(handle, src, nil, scanner.ScanComments)
121 decls = make(map[string]token.Pos)
122 uses = make(map[string][]token.Pos)
123 var prev token.Pos
124
125 scanFile:
126 for {
127 pos, tok, lit := s.Scan()
128 switch tok {
129 case token.EOF:
130 break scanFile
131 case token.COMMENT:
132 name, decl, use := annotatedObj(lit)
133 if len(name) > 0 {
134 if decl {
135 if _, ok := decls[name]; ok {
136 panic(fmt.Sprintf("duplicate declaration markers for %s", name))
137 }
138 decls[name] = prev
139 }
140 if use {
141 uses[name] = append(uses[name], prev)
142 }
143 }
144 case token.SEMICOLON:
145
146 if lit == "\n" {
147 continue scanFile
148 }
149 fallthrough
150 default:
151 prev = pos
152 }
153 }
154 return decls, uses
155 }
156
157 func annotatedObj(lit string) (name string, decl, use bool) {
158 if lit[1] == '*' {
159 lit = lit[:len(lit)-2]
160 }
161 lit = strings.TrimSpace(lit[2:])
162
163 scanLit:
164 for idx, r := range lit {
165 switch r {
166 case '=':
167 decl = true
168 case '@':
169 use = true
170 default:
171 name = lit[idx:]
172 break scanLit
173 }
174 }
175 return
176 }
177
View as plain text