Skip to content

Commit 5f6385b

Browse files
committed
feat: support for Python.NameError
1 parent 9a51b00 commit 5f6385b

File tree

5 files changed

+299
-8
lines changed

5 files changed

+299
-8
lines changed

error_templates/python/name_error.go

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,47 @@
11
package python
22

33
import (
4+
"fmt"
5+
"strings"
6+
47
lib "github.com/nedpals/errgoengine"
58
)
69

710
var NameError = lib.ErrorTemplate{
811
Name: "NameError",
912
Pattern: `NameError: name '(?P<variable>\S+)' is not defined`,
13+
OnAnalyzeErrorFn: func(cd *lib.ContextData, m *lib.MainError) {
14+
lib.QueryNode(m.Nearest, strings.NewReader(fmt.Sprintf(`((identifier) @name (#eq? @name "%s"))`, cd.Variables["variable"])), func(ctx lib.QueryNodeCtx) bool {
15+
match := ctx.Cursor.FilterPredicates(ctx.Match, []byte(m.Nearest.Doc.Contents))
16+
for _, c := range match.Captures {
17+
node := lib.WrapNode(m.Nearest.Doc, c.Node)
18+
m.Nearest = node
19+
return false
20+
}
21+
return true
22+
})
23+
},
1024
OnGenExplainFn: func(cd *lib.ContextData, gen *lib.ExplainGenerator) {
11-
gen.Add("Your program tried to access the '%s' variable which was not found on your program.", cd.Variables["variable"])
25+
gen.Add("This error occurs when trying to use a variable (`%s`) or name that has not been defined in the current scope.", cd.Variables["variable"])
1226
},
1327
OnGenBugFixFn: func(cd *lib.ContextData, gen *lib.BugFixGenerator) {
14-
// TODO:
28+
gen.Add("Define the variable before using it", func(s *lib.BugFixSuggestion) {
29+
// get to the very parent (before `block`)
30+
parent := cd.MainError.Nearest.Parent()
31+
for !parent.IsNull() && parent.Type() != "module" && parent.Type() != "block" {
32+
fmt.Println(parent.Type())
33+
parent = parent.Parent()
34+
}
35+
36+
spaces := cd.MainError.Document.LineAt(parent.StartPosition().Line)[:parent.StartPosition().Column]
37+
38+
s.AddStep("Make sure to define the variable `%s` before using it.", cd.Variables["variable"]).
39+
AddFix(lib.FixSuggestion{
40+
NewText: spaces + fmt.Sprintf("%s = \"Hello!\"\n", cd.Variables["variable"]),
41+
StartPosition: lib.Position{
42+
Line: parent.StartPosition().Line,
43+
},
44+
})
45+
})
1546
},
1647
}

error_templates/python/test_files/name_error/test.txt

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,19 @@ NameError: name 'test' is not defined
88
===
99
template: "Python.NameError"
1010
---
11-
Your program tried to access the 'test' variable which was not found on your program.
11+
# NameError
12+
This error occurs when trying to use a variable (`test`) or name that has not been defined in the current scope.
13+
```
14+
print(test)
15+
^^^^
16+
17+
```
18+
## Steps to fix
19+
### Define the variable before using it
20+
Make sure to define the variable `test` before using it.
21+
```diff
22+
- print(test)
23+
+ test = "Hello!"
24+
+ print(test)
25+
26+
```

languages/python/language.go

Lines changed: 87 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,99 @@ type pyAnalyzer struct {
2727
}
2828

2929
func (an *pyAnalyzer) FallbackSymbol() lib.Symbol {
30-
return lib.Builtin("any")
30+
return BuiltinTypes.AnySymbol
3131
}
3232

3333
func (an *pyAnalyzer) FindSymbol(name string) lib.Symbol {
34-
// TODO:
35-
return nil
34+
sym, _ := builtinTypesStore.FindByName(name)
35+
return sym
36+
}
37+
38+
func (an *pyAnalyzer) analyzeTypeNode(n lib.SyntaxNode) lib.Symbol {
39+
switch n.Type() {
40+
case "identifier":
41+
builtinSym, found := builtinTypesStore.FindByName(n.Text())
42+
if found {
43+
return builtinSym
44+
}
45+
sym := an.ContextData.FindSymbol(n.Text(), int(n.StartByte()))
46+
if sym == nil {
47+
return lib.UnresolvedSymbol
48+
}
49+
return sym
50+
case "subscript":
51+
valueNode := n.ChildByFieldName("value")
52+
_, validCollectionType := strToBuiltinCollectionTypeSyms[valueNode.Text()]
53+
if !validCollectionType {
54+
baseTypeSym := an.analyzeTypeNode(valueNode)
55+
// TODO: inject value syms
56+
return baseTypeSym
57+
}
58+
59+
// get value syms
60+
valueSyms := make([]lib.Symbol, n.NamedChildCount()-1)
61+
for i := 1; i < int(n.NamedChildCount()); i++ {
62+
valueSyms[i-1] = an.AnalyzeNode(n.NamedChild(i))
63+
}
64+
65+
if cSym, err := collectionIfy(valueNode.Text(), valueSyms...); err != nil {
66+
return cSym
67+
}
68+
return BuiltinTypes.AnySymbol
69+
case "none":
70+
return BuiltinTypes.NoneSymbol
71+
}
72+
73+
// TODO: any or unresolved?
74+
return BuiltinTypes.AnySymbol
3675
}
3776

3877
func (an *pyAnalyzer) AnalyzeNode(n lib.SyntaxNode) lib.Symbol {
39-
// TODO:
40-
return lib.Builtin("void")
78+
switch n.Type() {
79+
case "type":
80+
return an.analyzeTypeNode(n.NamedChild(0))
81+
case "true", "false":
82+
return BuiltinTypes.BooleanSymbol
83+
case "string":
84+
return BuiltinTypes.StringSymbol
85+
case "integer":
86+
return BuiltinTypes.IntSymbol
87+
case "float":
88+
return BuiltinTypes.FloatSymbol
89+
// case "array_creation_expression":
90+
// var gotLen int
91+
// typeSym := an.AnalyzeNode(n.ChildByFieldName("type"))
92+
// rawLen := n.ChildByFieldName("dimensions").LastNamedChild().Text()
93+
// fmt.Sscanf(rawLen, "%d", &gotLen)
94+
// return arrayIfy(typeSym, gotLen)
95+
// case "object_creation_expression":
96+
// return an.AnalyzeNode(n.ChildByFieldName("type"))
97+
case "identifier":
98+
sym := an.ContextData.FindSymbol(n.Text(), int(n.StartByte()))
99+
if sym == nil {
100+
return BuiltinTypes.NoneSymbol
101+
}
102+
return sym
103+
// case "subscript":
104+
// sym := an.AnalyzeNode(n.ChildByFieldName("array"))
105+
// if aSym, ok := sym.(); ok {
106+
// return aSym.ItemSymbol
107+
// } else {
108+
// return BuiltinTypes.VoidSymbol
109+
// }
110+
case "attribute":
111+
if objNodeSym := an.AnalyzeNode(n.ChildByFieldName("object")); objNodeSym != nil {
112+
if objNodeSym == BuiltinTypes.AnySymbol {
113+
return objNodeSym
114+
}
115+
116+
fieldNode := n.ChildByFieldName("attribute")
117+
if sym := lib.GetFromSymbol(lib.CastChildrenSymbol(objNodeSym), fieldNode.Text()); sym != nil {
118+
return sym
119+
}
120+
}
121+
}
122+
return BuiltinTypes.AnySymbol
41123
}
42124

43125
func (an *pyAnalyzer) AnalyzeImport(params lib.ImportParams) lib.ResolvedImport {

languages/python/symbols.go

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
package python
2+
3+
import (
4+
"fmt"
5+
6+
lib "github.com/nedpals/errgoengine"
7+
)
8+
9+
type pythonBuiltinTypeStore struct {
10+
typesSymbols map[string]lib.Symbol
11+
}
12+
13+
func (store *pythonBuiltinTypeStore) Builtin(name string) lib.Symbol {
14+
if store.typesSymbols == nil {
15+
store.typesSymbols = make(map[string]lib.Symbol)
16+
} else if sym, ok := store.FindByName(name); ok {
17+
return sym
18+
}
19+
store.typesSymbols[name] = lib.Builtin(name)
20+
return store.typesSymbols[name]
21+
}
22+
23+
func (store *pythonBuiltinTypeStore) FindByName(name string) (lib.Symbol, bool) {
24+
if store.typesSymbols == nil {
25+
return nil, false
26+
}
27+
sym, ok := store.typesSymbols[name]
28+
return sym, ok
29+
}
30+
31+
var builtinTypesStore = &pythonBuiltinTypeStore{}
32+
33+
// built-in types in python
34+
var BuiltinTypes = struct {
35+
NoneSymbol lib.Symbol
36+
AnySymbol lib.Symbol
37+
VoidSymbol lib.Symbol
38+
BooleanSymbol lib.Symbol
39+
StringSymbol lib.Symbol
40+
IntSymbol lib.Symbol
41+
FloatSymbol lib.Symbol
42+
}{
43+
NoneSymbol: builtinTypesStore.Builtin("none"),
44+
AnySymbol: builtinTypesStore.Builtin("any"),
45+
VoidSymbol: builtinTypesStore.Builtin("void"),
46+
BooleanSymbol: builtinTypesStore.Builtin("bool"),
47+
StringSymbol: builtinTypesStore.Builtin("str"),
48+
IntSymbol: builtinTypesStore.Builtin("int"),
49+
FloatSymbol: builtinTypesStore.Builtin("float"),
50+
}
51+
52+
type TupleSymbol struct {
53+
Items []lib.Symbol
54+
}
55+
56+
func (sym TupleSymbol) Name() string {
57+
return "tuple"
58+
}
59+
60+
func (sym TupleSymbol) Kind() lib.SymbolKind {
61+
return lib.SymbolKindType
62+
}
63+
64+
func (sym TupleSymbol) Location() lib.Location {
65+
return lib.Location{}
66+
}
67+
68+
func (sym TupleSymbol) IsFixed() bool {
69+
return true
70+
}
71+
72+
type BuiltinCollectionTypeKind int
73+
74+
func (k BuiltinCollectionTypeKind) String() string {
75+
switch k {
76+
case CollectionTypeList:
77+
return "list"
78+
case CollectionTypeDict:
79+
return "dict"
80+
case CollectionTypeTuple:
81+
return "tuple"
82+
case CollectionTypeSet:
83+
return "set"
84+
default:
85+
return "unknown"
86+
}
87+
}
88+
89+
func (k BuiltinCollectionTypeKind) IsFixed() bool {
90+
return k == CollectionTypeTuple
91+
}
92+
93+
var strToBuiltinCollectionTypeSyms = map[string]BuiltinCollectionTypeKind{
94+
CollectionTypeList.String(): CollectionTypeList,
95+
CollectionTypeDict.String(): CollectionTypeDict,
96+
CollectionTypeTuple.String(): CollectionTypeTuple,
97+
CollectionTypeSet.String(): CollectionTypeSet,
98+
}
99+
100+
const (
101+
CollectionTypeList BuiltinCollectionTypeKind = 0
102+
CollectionTypeDict BuiltinCollectionTypeKind = iota
103+
CollectionTypeTuple BuiltinCollectionTypeKind = iota
104+
CollectionTypeSet BuiltinCollectionTypeKind = iota
105+
)
106+
107+
type CollectionTypeSymbol struct {
108+
TypeKind BuiltinCollectionTypeKind
109+
KeyType lib.Symbol
110+
ValueType []lib.Symbol
111+
}
112+
113+
func (sym CollectionTypeSymbol) Name() string {
114+
return sym.TypeKind.String()
115+
}
116+
117+
func (sym CollectionTypeSymbol) Kind() lib.SymbolKind {
118+
return lib.SymbolKindType
119+
}
120+
121+
func (sym CollectionTypeSymbol) Location() lib.Location {
122+
return lib.Location{}
123+
}
124+
125+
func (sym CollectionTypeSymbol) IsFixed() bool {
126+
return sym.TypeKind.IsFixed()
127+
}
128+
129+
func collectionIfy(name string, syms ...lib.Symbol) (lib.Symbol, error) {
130+
kind, ok := strToBuiltinCollectionTypeSyms[name]
131+
if !ok {
132+
return nil, fmt.Errorf("not a valid collection type")
133+
}
134+
135+
sym := CollectionTypeSymbol{
136+
TypeKind: kind,
137+
KeyType: BuiltinTypes.IntSymbol,
138+
ValueType: []lib.Symbol{BuiltinTypes.AnySymbol},
139+
}
140+
141+
switch kind {
142+
case CollectionTypeList, CollectionTypeSet:
143+
if len(syms) == 1 {
144+
sym.ValueType[0] = syms[0]
145+
}
146+
case CollectionTypeDict:
147+
if len(syms) == 2 {
148+
sym.KeyType = syms[0]
149+
sym.ValueType[0] = syms[1]
150+
} else {
151+
sym.KeyType = BuiltinTypes.StringSymbol
152+
}
153+
case CollectionTypeTuple:
154+
sym.ValueType = syms
155+
}
156+
157+
return sym, nil
158+
}

symbols.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,3 +241,8 @@ var UnresolvedSymbol Symbol = unresolvedSymbol{}
241241
// // TODO:
242242
// return nil
243243
// }
244+
245+
// Collection types
246+
type ICollectionTypeSymbol interface {
247+
IsFixed() bool
248+
}

0 commit comments

Comments
 (0)