Skip to content

Commit c4026d0

Browse files
committed
Fix some remaining issues
1 parent 47c34c8 commit c4026d0

17 files changed

+113
-34
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,6 @@ gojsonnet.egg-info/
2727
/linter/jsonnet-lint/jsonnet-lint
2828
/tests_path.source
2929

30+
/jsonnet-lint
31+
3032
/builtin-benchmark-results

linter/internal/types/build_graph.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -158,19 +158,26 @@ func calcTP(node ast.Node, g *typeGraph) typePlaceholder {
158158
}
159159

160160
case *ast.DesugaredObject:
161-
// XXX: not sure what about it
162161
obj := &objectDesc{
163162
allFieldsKnown: true,
164163
fieldContains: make(map[string][]placeholderID),
165164
}
166165
for _, field := range node.Fields {
167-
// XXX: what about plussuper, how does it change things here?
166+
// XXX(sbarzowski): what about plussuper, how does it change things here?
168167
switch fieldName := field.Name.(type) {
169168
case *ast.LiteralString:
170-
obj.fieldContains[fieldName.Value] = append(obj.fieldContains[fieldName.Value], g.getExprPlaceholder(field.Body))
169+
if field.PlusSuper {
170+
obj.fieldContains[fieldName.Value] = []placeholderID{anyType}
171+
} else {
172+
obj.fieldContains[fieldName.Value] = append(obj.fieldContains[fieldName.Value], g.getExprPlaceholder(field.Body))
173+
}
171174
default:
172-
obj.allContain = append(obj.allContain, g.getExprPlaceholder(field.Body))
173-
obj.allFieldsKnown = false
175+
if field.PlusSuper {
176+
obj.allContain = []placeholderID{anyType}
177+
} else {
178+
obj.allContain = append(obj.allContain, g.getExprPlaceholder(field.Body))
179+
obj.allFieldsKnown = false
180+
}
174181
}
175182
}
176183
return concreteTP(TypeDesc{ObjectDesc: obj})

linter/internal/types/desc.go

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
package types
22

33
import (
4+
"math"
45
"strings"
56

67
"github.com/google/go-jsonnet/ast"
78
)
89

10+
// Technically on 64-bit system, if someone really tries, maybe they can
11+
// go over that and get strange errors. At this point I don't care.
12+
const maxPossibleArity = math.MaxInt32
13+
914
type arrayDesc struct {
15+
// TODO(sbarzowski) Change to "otherContain" to improve precision.
1016
allContain []placeholderID
1117

1218
elementContains [][]placeholderID
@@ -34,6 +40,7 @@ func (a *arrayDesc) normalize() {
3440
}
3541

3642
type objectDesc struct {
43+
// Change to unknownContain to improve precision
3744
allContain []placeholderID
3845
fieldContains map[string][]placeholderID
3946
allFieldsKnown bool
@@ -129,8 +136,7 @@ type TypeDesc struct {
129136

130137
// Any returns whether all values are allowed (i.e. we know nothing about it).
131138
func (t *TypeDesc) Any() bool {
132-
// XXX(sbarzowski) BUG - must check that function, object and array allow any values inside
133-
return t.Bool && t.Number && t.String && t.Null && t.Function() && t.Object() && t.Array()
139+
return t.Bool && t.Number && t.String && t.Null && t.AnyFunction() && t.AnyObject() && t.AnyArray()
134140
}
135141

136142
// Void returns whether the type is empty (no values are possible).
@@ -143,16 +149,52 @@ func (t *TypeDesc) Function() bool {
143149
return t.FunctionDesc != nil
144150
}
145151

152+
func (t *TypeDesc) AnyFunction() bool {
153+
if !t.Function() || t.FunctionDesc.maxArity < maxPossibleArity || t.FunctionDesc.minArity > 0 || t.FunctionDesc.params != nil {
154+
return false
155+
}
156+
for _, elemType := range t.FunctionDesc.resultContains {
157+
if elemType == anyType {
158+
return true
159+
}
160+
}
161+
return false
162+
}
163+
146164
// Object returns whether the types contains an object.
147165
func (t *TypeDesc) Object() bool {
148166
return t.ObjectDesc != nil
149167
}
150168

169+
func (t *TypeDesc) AnyObject() bool {
170+
if !t.Object() || t.ObjectDesc.allFieldsKnown {
171+
return false
172+
}
173+
for _, elemType := range t.ObjectDesc.allContain {
174+
if elemType == anyType {
175+
return true
176+
}
177+
}
178+
return false
179+
}
180+
151181
// Array returns whether the types contains an array.
152182
func (t *TypeDesc) Array() bool {
153183
return t.ArrayDesc != nil
154184
}
155185

186+
func (t *TypeDesc) AnyArray() bool {
187+
if !t.Array() {
188+
return false
189+
}
190+
for _, elemType := range t.ArrayDesc.allContain {
191+
if elemType == anyType {
192+
return true
193+
}
194+
}
195+
return false
196+
}
197+
156198
func voidTypeDesc() TypeDesc {
157199
return TypeDesc{}
158200
}

linter/internal/types/graph.go

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package types
22

33
import (
44
"fmt"
5-
"math"
65
"sort"
76

87
"github.com/google/go-jsonnet/ast"
@@ -155,7 +154,6 @@ func (g *typeGraph) newPlaceholder() placeholderID {
155154
g._placeholders = append(g._placeholders, typePlaceholder{})
156155
g.elementType = append(g.elementType, nil)
157156

158-
// fmt.Println("placeholder:", len(g._placeholders)-1)
159157
return placeholderID(len(g._placeholders) - 1)
160158
}
161159

@@ -214,7 +212,7 @@ func (g *typeGraph) separateElementTypes() {
214212
contains := make([]placeholderID, 0, 1)
215213

216214
// Direct indexing
217-
// XXX(barzowski) handle fromBuiltin.concrete
215+
// XXX(sbarzowski) handle fromBuiltin.concrete
218216
if index.indexType == knownStringIndex {
219217
if c.concrete.Object() {
220218
contains = append(contains, c.concrete.ObjectDesc.allContain...)
@@ -659,10 +657,8 @@ func NewTypeGraph(varAt map[ast.Node]*common.Variable, importFunc ImportFunc) *t
659657
}
660658

661659
anyFunctionDesc := &functionDesc{
662-
minArity: 0,
663-
// Technically on 64-bit system if someone really tries maybe they can
664-
// go over that and get strange errors. At this point I don't care.
665-
maxArity: math.MaxInt32,
660+
minArity: 0,
661+
maxArity: maxPossibleArity,
666662
resultContains: []placeholderID{anyType},
667663
}
668664

linter/linter.go

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ import (
1515
"github.com/google/go-jsonnet/linter/internal/utils"
1616
)
1717

18-
// XXX(sbarzowski) refactoring idea - put ast.Node and its path in a struct as it gets
19-
// passed around together constantly
20-
2118
// ErrorWriter encapsulates a writer and an error state indicating when at least
2219
// one error has been written to the writer.
2320
type ErrorWriter struct {
@@ -33,10 +30,17 @@ func (e *ErrorWriter) writeError(vm *jsonnet.VM, err errors.StaticError) {
3330
}
3431
}
3532

33+
// nodeWithLocation represents a Jsonnet program with its location
34+
// for the importer.
35+
type nodeWithLocation struct {
36+
node ast.Node
37+
path string
38+
}
39+
3640
// Lint analyses a node and reports any issues it encounters to an error writer.
37-
func lint(vm *jsonnet.VM, node ast.Node, currentPath string, errWriter *ErrorWriter) {
41+
func lint(vm *jsonnet.VM, node nodeWithLocation, errWriter *ErrorWriter) {
3842
roots := make(map[string]ast.Node)
39-
getImports(vm, node, roots, currentPath, errWriter)
43+
getImports(vm, node, roots, errWriter)
4044

4145
variablesInFile := make(map[string]common.VariableInfo)
4246

@@ -46,13 +50,13 @@ func lint(vm *jsonnet.VM, node ast.Node, currentPath string, errWriter *ErrorWri
4650
VariableKind: common.VarStdlib,
4751
}
4852

49-
findVariables := func(node ast.Node, currentPath string) common.VariableInfo {
53+
findVariables := func(node nodeWithLocation) common.VariableInfo {
5054
variableInfo := common.VariableInfo{
5155
Variables: nil,
5256
VarAt: make(map[ast.Node]*common.Variable),
5357
}
5458
variableInfo.Variables = append(variableInfo.Variables, &std)
55-
findVariables(node, &variableInfo, vScope{"std": &std})
59+
findVariables(node.node, &variableInfo, vScope{"std": &std})
5660
for _, v := range variableInfo.Variables {
5761
for _, u := range v.Occurences {
5862
variableInfo.VarAt[u] = v
@@ -61,9 +65,9 @@ func lint(vm *jsonnet.VM, node ast.Node, currentPath string, errWriter *ErrorWri
6165
return variableInfo
6266
}
6367

64-
variableInfo := findVariables(node, currentPath)
68+
variableInfo := findVariables(node)
6569
for importedPath, rootNode := range roots {
66-
variablesInFile[importedPath] = findVariables(rootNode, importedPath)
70+
variablesInFile[importedPath] = findVariables(nodeWithLocation{rootNode, importedPath})
6771
}
6872

6973
for _, v := range variableInfo.Variables {
@@ -89,22 +93,28 @@ func lint(vm *jsonnet.VM, node ast.Node, currentPath string, errWriter *ErrorWri
8993

9094
g.AddRoots(roots, vars)
9195

92-
g.PrepareTypes(node, et, variableInfo.VarAt)
93-
types.Check(node, et, &ec)
96+
g.PrepareTypes(node.node, et, variableInfo.VarAt)
97+
98+
// TODO(sbarzowski) Useful for debugging – expose it in CLI
99+
// t := et[node.node]
100+
// fmt.Fprintf(os.Stderr, "%v\n", types.Describe(&t))
101+
102+
types.Check(node.node, et, &ec)
94103

95-
traversal.Traverse(node, &ec)
104+
traversal.Traverse(node.node, &ec)
96105

97106
for _, err := range ec.Errs {
98107
errWriter.writeError(vm, err)
99108
}
100109
}
101110

102-
func getImports(vm *jsonnet.VM, node ast.Node, roots map[string]ast.Node, currentPath string, errWriter *ErrorWriter) {
111+
func getImports(vm *jsonnet.VM, node nodeWithLocation, roots map[string]ast.Node, errWriter *ErrorWriter) {
103112
// TODO(sbarzowski) consider providing some way to disable warnings about nonexistent imports
104113
// At least for 3rd party code.
105114
// Perhaps there may be some valid use cases for conditional imports where one of the imported
106115
// files doesn't exist.
107-
switch node := node.(type) {
116+
currentPath := node.path
117+
switch node := node.node.(type) {
108118
case *ast.Import:
109119
p := node.File.Value
110120
contents, foundAt, err := vm.ImportAST(currentPath, p)
@@ -113,7 +123,7 @@ func getImports(vm *jsonnet.VM, node ast.Node, roots map[string]ast.Node, curren
113123
} else {
114124
if _, visited := roots[foundAt]; !visited {
115125
roots[foundAt] = contents
116-
getImports(vm, contents, roots, foundAt, errWriter)
126+
getImports(vm, nodeWithLocation{contents, foundAt}, roots, errWriter)
117127
}
118128
}
119129
case *ast.ImportStr:
@@ -124,7 +134,7 @@ func getImports(vm *jsonnet.VM, node ast.Node, roots map[string]ast.Node, curren
124134
}
125135
default:
126136
for _, c := range parser.Children(node) {
127-
getImports(vm, c, roots, currentPath, errWriter)
137+
getImports(vm, nodeWithLocation{c, currentPath}, roots, errWriter)
128138
}
129139
}
130140
}
@@ -161,7 +171,7 @@ func (linter *Linter) Check() bool {
161171
errWriter.writeError(linter.vm, err.(errors.StaticError)) // ugly but true
162172
return true
163173
}
164-
lint(linter.vm, node, linter.filename, &errWriter)
174+
lint(linter.vm, nodeWithLocation{node, linter.filename}, &errWriter)
165175
return errWriter.ErrorsFound
166176
}
167177

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}["foo"]
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
testdata/index_object_with_string:1:1 Unexpected end of file
2-
1+
testdata/index_object_with_string:1:1-10 Indexed object has no field "foo"
32

3+
{}["foo"]
44

55

linter/testdata/not_any.jsonnet

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[
2+
2,
3+
true,
4+
"str",
5+
null,
6+
{},
7+
function() null,
8+
[]
9+
][1+3].foo

linter/testdata/not_any.linter.golden

Whitespace-only changes.

linter/testdata/not_any2.jsonnet

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[
2+
2,
3+
true,
4+
"str",
5+
null,
6+
{},
7+
function() null,
8+
[]
9+
][4].foo

linter/testdata/not_any2.linter.golden

Whitespace-only changes.

linter/testdata/plussuper2.jsonnet

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
a +:: {},
3+
}.a + 3

linter/testdata/plussuper2.linter.golden

Whitespace-only changes.

testdata/bitwise_shift5.linter.golden

Whitespace-only changes.

testdata/bitwise_shift6.linter.golden

Whitespace-only changes.

testdata/builtinManifestJsonEx.linter.golden

Whitespace-only changes.

0 commit comments

Comments
 (0)