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 if !typeparams.Enabled {
46 t.Skip("type params are not enabled")
47 }
48 } else {
49 mode |= typeparams.DisallowParsing
50 }
51 file, err := ParseFile(fset, path, src, mode)
52 if err != nil {
53 t.Fatal(err)
54 }
55
56
57
58
59 handle := fset.File(file.Package)
60 fromParser := declsFromParser(file)
61 fromComments := declsFromComments(handle, src)
62
63 pos := func(pos token.Pos) token.Position {
64 p := handle.Position(pos)
65
66
67 p.Filename = ""
68 return p
69 }
70 for k, want := range fromComments {
71 if got := fromParser[k]; got != want {
72 t.Errorf("%s resolved to %s, want %s", pos(k), pos(got), pos(want))
73 }
74 delete(fromParser, k)
75 }
76
77 for k, got := range fromParser {
78 t.Errorf("%s resolved to %s, want no object", pos(k), pos(got))
79 }
80 })
81 }
82 }
83
84
85
86 func declsFromParser(file *ast.File) map[token.Pos]token.Pos {
87 objmap := map[token.Pos]token.Pos{}
88 ast.Inspect(file, func(node ast.Node) bool {
89
90 if ident, _ := node.(*ast.Ident); ident != nil && ident.Obj != nil && ident.Name != "_" {
91 objmap[ident.Pos()] = ident.Obj.Pos()
92 }
93 return true
94 })
95 return objmap
96 }
97
98
99
100
101 func declsFromComments(handle *token.File, src []byte) map[token.Pos]token.Pos {
102 decls, uses := positionMarkers(handle, src)
103
104 objmap := make(map[token.Pos]token.Pos)
105
106 for name, posns := range uses {
107 declpos, ok := decls[name]
108 if !ok {
109 panic(fmt.Sprintf("missing declaration for %s", name))
110 }
111 for _, pos := range posns {
112 objmap[pos] = declpos
113 }
114 }
115 return objmap
116 }
117
118
119
120
121
122 func positionMarkers(handle *token.File, src []byte) (decls map[string]token.Pos, uses map[string][]token.Pos) {
123 var s scanner.Scanner
124 s.Init(handle, src, nil, scanner.ScanComments)
125 decls = make(map[string]token.Pos)
126 uses = make(map[string][]token.Pos)
127 var prev token.Pos
128
129 scanFile:
130 for {
131 pos, tok, lit := s.Scan()
132 switch tok {
133 case token.EOF:
134 break scanFile
135 case token.COMMENT:
136 name, decl, use := annotatedObj(lit)
137 if len(name) > 0 {
138 if decl {
139 if _, ok := decls[name]; ok {
140 panic(fmt.Sprintf("duplicate declaration markers for %s", name))
141 }
142 decls[name] = prev
143 }
144 if use {
145 uses[name] = append(uses[name], prev)
146 }
147 }
148 case token.SEMICOLON:
149
150 if lit == "\n" {
151 continue scanFile
152 }
153 fallthrough
154 default:
155 prev = pos
156 }
157 }
158 return decls, uses
159 }
160
161 func annotatedObj(lit string) (name string, decl, use bool) {
162 if lit[1] == '*' {
163 lit = lit[:len(lit)-2]
164 }
165 lit = strings.TrimSpace(lit[2:])
166
167 scanLit:
168 for idx, r := range lit {
169 switch r {
170 case '=':
171 decl = true
172 case '@':
173 use = true
174 default:
175 name = lit[idx:]
176 break scanLit
177 }
178 }
179 return
180 }
181
View as plain text