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