/
typeinfer.go
308 lines (298 loc) · 8.88 KB
/
typeinfer.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
// Copyright (c) 2018, Cogent Core. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package golang
import (
"fmt"
"os"
"strings"
"cogentcore.org/core/parse"
"cogentcore.org/core/parse/parser"
"cogentcore.org/core/parse/syms"
"cogentcore.org/core/parse/token"
)
// TypeErr indicates is the type name we use to indicate that the type could not be inferred
var TypeErr = "<err>"
// TypeInProcess indicates is the type name we use to indicate that the type
// is currently being processed -- prevents loops
var TypeInProcess = "<in-process>"
// InferSymbolType infers the symbol types for given symbol and all of its children
// funInternal determines whether to include function-internal symbols
// (e.g., variables within function scope -- only for local files).
func (gl *GoLang) InferSymbolType(sy *syms.Symbol, fs *parse.FileState, pkg *syms.Symbol, funInternal bool) {
if sy.Name == "" {
sy.Type = TypeErr
return
}
if sy.Name[0] == '_' {
sy.Type = TypeErr
return
}
if sy.Ast != nil {
ast := sy.Ast.(*parser.Ast)
switch {
case sy.Kind == token.NameField:
stsc, ok := sy.Scopes[token.NameStruct]
if ok {
stty, _ := gl.FindTypeName(stsc, fs, pkg)
if stty != nil {
fldel := stty.Els.ByName(sy.Name)
if fldel != nil {
sy.Type = fldel.Type
// fmt.Printf("set field type: %s\n", sy.Label())
} else {
if TraceTypes {
fmt.Printf("InferSymbolType: field named: %v not found in struct type: %v\n", sy.Name, stty.Name)
}
}
} else {
if TraceTypes {
fmt.Printf("InferSymbolType: field named: %v struct type: %v not found\n", sy.Name, stsc)
}
}
if sy.Type == "" {
sy.Type = stsc + "." + sy.Name
}
} else {
if TraceTypes {
fmt.Printf("InferSymbolType: field named: %v doesn't have NameStruct scope\n", sy.Name)
}
}
case sy.Kind == token.NameVarClass: // method receiver
stsc, ok := sy.Scopes.SubCat(token.NameType)
if ok {
sy.Type = stsc
}
case sy.Kind.SubCat() == token.NameVar:
var astyp *parser.Ast
if ast.HasChildren() {
if strings.HasPrefix(ast.Nm, "ForRange") {
gl.InferForRangeSymbolType(sy, fs, pkg)
} else {
astyp = ast.ChildAst(len(ast.Kids) - 1)
vty, ok := gl.TypeFromAst(fs, pkg, nil, astyp)
if ok {
sy.Type = SymTypeNameForPkg(vty, pkg)
// if TraceTypes {
// fmt.Printf("namevar: %v type: %v from ast\n", sy.Name, sy.Type)
// }
} else {
sy.Type = TypeErr // actively mark as err so not re-processed
if TraceTypes {
fmt.Printf("InferSymbolType: NameVar: %v NOT resolved from ast: %v\n", sy.Name, astyp.Path())
astyp.WriteTree(os.Stdout, 0)
}
}
}
} else {
sy.Type = TypeErr
if TraceTypes {
fmt.Printf("InferSymbolType: NameVar: %v has no children\n", sy.Name)
}
}
case sy.Kind == token.NameConstant:
if !strings.HasPrefix(ast.Nm, "ConstSpec") {
if TraceTypes {
fmt.Printf("InferSymbolType: NameConstant: %v not a const: %v\n", sy.Name, ast.Nm)
}
return
}
parent := ast.ParentAst()
if parent != nil && parent.HasChildren() {
fc := parent.ChildAst(0)
if fc.HasChildren() {
ffc := fc.ChildAst(0)
if ffc.Nm == "Name" {
ffc = ffc.NextAst()
}
vty, ok := gl.TypeFromAst(fs, pkg, nil, ffc)
if ok {
sy.Type = SymTypeNameForPkg(vty, pkg)
} else {
sy.Type = TypeErr
if TraceTypes {
fmt.Printf("InferSymbolType: NameConstant: %v NOT resolved from ast: %v\n", sy.Name, ffc.Path())
ffc.WriteTree(os.Stdout, 1)
}
}
} else {
sy.Type = TypeErr
}
} else {
sy.Type = TypeErr
}
case sy.Kind.SubCat() == token.NameType:
vty, _ := gl.FindTypeName(sy.Name, fs, pkg)
if vty != nil {
sy.Type = SymTypeNameForPkg(vty, pkg)
} else {
// if TraceTypes {
// fmt.Printf("InferSymbolType: NameType: %v\n", sy.Name)
// }
if ast.HasChildren() {
astyp := ast.ChildAst(len(ast.Kids) - 1)
if astyp.Nm == "FieldTag" {
// ast.WriteTree(os.Stdout, 1)
astyp = ast.ChildAst(len(ast.Kids) - 2)
}
vty, ok := gl.TypeFromAst(fs, pkg, nil, astyp)
if ok {
sy.Type = SymTypeNameForPkg(vty, pkg)
// if TraceTypes {
// fmt.Printf("InferSymbolType: NameType: %v type: %v from ast\n", sy.Name, sy.Type)
// }
} else {
sy.Type = TypeErr // actively mark as err so not re-processed
if TraceTypes {
fmt.Printf("InferSymbolType: NameType: %v NOT resolved from ast: %v\n", sy.Name, astyp.Path())
ast.WriteTree(os.Stdout, 1)
}
}
} else {
sy.Type = TypeErr
}
}
case sy.Kind == token.NameFunction:
ftyp := gl.FuncTypeFromAst(fs, pkg, ast, nil)
if ftyp != nil {
ftyp.Name = "func " + sy.Name
ftyp.Filename = sy.Filename
ftyp.Region = sy.Region
sy.Type = ftyp.Name
pkg.Types.Add(ftyp)
sy.Detail = "(" + ftyp.ArgString() + ") " + ftyp.ReturnString()
// if TraceTypes {
// fmt.Printf("InferSymbolType: added function type: %v %v\n", ftyp.Name, ftyp.String())
// }
}
}
}
if !funInternal && sy.Kind.SubCat() == token.NameFunction {
sy.Children = nil // nuke!
} else {
for _, ss := range sy.Children {
if sy != ss {
if false && TraceTypes {
fmt.Printf("InferSymbolType: processing child: %v\n", ss)
}
gl.InferSymbolType(ss, fs, pkg, funInternal)
}
}
}
}
// InferForRangeSymbolType infers the type of a ForRange expr
// gets the container type properly
func (gl *GoLang) InferForRangeSymbolType(sy *syms.Symbol, fs *parse.FileState, pkg *syms.Symbol) {
ast := sy.Ast.(*parser.Ast)
if ast.NumChildren() < 2 {
sy.Type = TypeErr // actively mark as err so not re-processed
if TraceTypes {
fmt.Printf("InferSymbolType: ForRange NameVar: %v does not have expected 2+ children\n", sy.Name)
ast.WriteTree(os.Stdout, 0)
}
return
}
// vars are in first child, type is in second child, rest of code is on last node
astyp := ast.ChildAst(1)
vty, ok := gl.TypeFromAst(fs, pkg, nil, astyp)
if !ok {
sy.Type = TypeErr // actively mark as err so not re-processed
if TraceTypes {
fmt.Printf("InferSymbolType: NameVar: %v NOT resolved from ForRange ast: %v\n", sy.Name, astyp.Path())
astyp.WriteTree(os.Stdout, 0)
}
return
}
varidx := 1 // which variable are we: first or second?
vast := ast.ChildAst(0)
if vast.NumChildren() <= 1 {
varidx = 0
} else if vast.ChildAst(0).Src == sy.Name {
varidx = 0
}
// vty is the container -- first el should be the type of element
switch vty.Kind {
case syms.Map: // need to know if we are the key or el
if len(vty.Els) > 1 {
tn := vty.Els[varidx].Type
if IsQualifiedType(vty.Name) && !IsQualifiedType(tn) {
pnm, _ := SplitType(vty.Name)
sy.Type = QualifyType(pnm, tn)
} else {
sy.Type = tn
}
} else {
sy.Type = TypeErr
if TraceTypes {
fmt.Printf("InferSymbolType: %s has ForRange over Map on type without an el type: %v\n", sy.Name, vty.Name)
}
}
case syms.Array, syms.List:
if varidx == 0 {
sy.Type = "int"
} else if len(vty.Els) > 0 {
tn := vty.Els[0].Type
if IsQualifiedType(vty.Name) && !IsQualifiedType(tn) {
pnm, _ := SplitType(vty.Name)
sy.Type = QualifyType(pnm, tn)
} else {
sy.Type = tn
}
} else {
sy.Type = TypeErr
if TraceTypes {
fmt.Printf("InferSymbolType: %s has ForRange over Array, List on type without an el type: %v\n", sy.Name, vty.Name)
}
}
case syms.String:
if varidx == 0 {
sy.Type = "int"
} else {
sy.Type = "rune"
}
default:
sy.Type = TypeErr
if TraceTypes {
fmt.Printf("InferSymbolType: %s has ForRange over non-container type: %v kind: %v\n", sy.Name, vty.Name, vty.Kind)
}
}
}
// InferEmptySymbolType ensures that any empty symbol type is resolved during
// processing of other type information -- returns true if was able to resolve
func (gl *GoLang) InferEmptySymbolType(sym *syms.Symbol, fs *parse.FileState, pkg *syms.Symbol) bool {
if sym.Type == "" { // hasn't happened yet
// if TraceTypes {
// fmt.Printf("TExpr: trying to infer type\n")
// }
sym.Type = TypeInProcess
gl.InferSymbolType(sym, fs, pkg, true)
}
if sym.Type == TypeInProcess {
if TraceTypes {
fmt.Printf("TExpr: source symbol is in process -- we have a loop: %v kind: %v\n", sym.Name, sym.Kind)
}
sym.Type = TypeErr
return false
}
if sym.Type == TypeErr {
if TraceTypes {
fmt.Printf("TExpr: source symbol has type err: %v kind: %v\n", sym.Name, sym.Kind)
}
return false
}
if sym.Type == "" { // shouldn't happen
sym.Type = TypeErr
if TraceTypes {
fmt.Printf("TExpr: source symbol has type err (but wasn't marked): %v kind: %v\n", sym.Name, sym.Kind)
}
return false
}
return true
}
func SymTypeNameForPkg(ty *syms.Type, pkg *syms.Symbol) string {
sc, has := ty.Scopes[token.NamePackage]
if has && sc != pkg.Name {
return QualifyType(sc, ty.Name)
}
return ty.Name
}