diff --git a/builtins.go b/builtins.go index bc15ff8..421d5d3 100644 --- a/builtins.go +++ b/builtins.go @@ -345,6 +345,19 @@ func builtinFlatMap(i *interpreter, funcv, arrv value) (value, error) { } } +// builtinFlatMapArray is like builtinFlatMap, but only accepts array as the +// arrv value. Desugared comprehensions contain a call to this function, rather +// than builtinFlatMap, so that a better error message is printed when the +// comprehension would iterate over a non-array. +func builtinFlatMapArray(i *interpreter, funcv, arrv value) (value, error) { + switch arrv := arrv.(type) { + case *valueArray: + return builtinFlatMap(i, funcv, arrv) + default: + return nil, i.typeErrorSpecific(arrv, &valueArray{}) + } +} + func joinArrays(i *interpreter, sep *valueArray, arr *valueArray) (value, error) { result := make([]*cachedThunk, 0, arr.length()) first := true @@ -2880,4 +2893,5 @@ var funcBuiltins = buildBuiltinMap([]builtin{ // internal &unaryBuiltin{name: "$objectFlatMerge", function: builtinUglyObjectFlatMerge, params: ast.Identifiers{"x"}}, + &binaryBuiltin{name: "$flatMapArray", function: builtinFlatMapArray, params: ast.Identifiers{"func", "arr"}}, }) diff --git a/internal/program/desugarer.go b/internal/program/desugarer.go index d36acbe..763a729 100644 --- a/internal/program/desugarer.go +++ b/internal/program/desugarer.go @@ -184,7 +184,7 @@ func desugarForSpec(inside ast.Node, loc ast.LocationRange, forSpec *ast.ForSpec if err != nil { return nil, err } - current := buildStdCall("flatMap", loc, function, forSpec.Expr) + current := buildStdCall("$flatMapArray", loc, function, forSpec.Expr) if forSpec.Outer == nil { return current, nil } diff --git a/linter/internal/types/stdlib.go b/linter/internal/types/stdlib.go index 27c54a5..b1afb2d 100644 --- a/linter/internal/types/stdlib.go +++ b/linter/internal/types/stdlib.go @@ -199,6 +199,7 @@ func prepareStdlib(g *typeGraph) { "mod": g.newSimpleFuncType(stringOrNumber, "a", "b"), "native": g.newSimpleFuncType(anyFunctionType, "x"), "$objectFlatMerge": g.newSimpleFuncType(anyObjectType, "x"), + "$flatMapArray": g.newSimpleFuncType(anyArrayType, "func", "arr"), // Boolean diff --git a/testdata/array_comp_try_iterate_over_empty_string.golden b/testdata/array_comp_try_iterate_over_empty_string.golden new file mode 100644 index 0000000..9112098 --- /dev/null +++ b/testdata/array_comp_try_iterate_over_empty_string.golden @@ -0,0 +1,10 @@ +RUNTIME ERROR: Unexpected type string, expected array +------------------------------------------------- + testdata/array_comp_try_iterate_over_empty_string:1:1-16 + +[a for a in ''] + +------------------------------------------------- + During evaluation + + diff --git a/testdata/array_comp_try_iterate_over_empty_string.jsonnet b/testdata/array_comp_try_iterate_over_empty_string.jsonnet new file mode 100644 index 0000000..d081b80 --- /dev/null +++ b/testdata/array_comp_try_iterate_over_empty_string.jsonnet @@ -0,0 +1 @@ +[a for a in ''] diff --git a/testdata/array_comp_try_iterate_over_empty_string.linter.golden b/testdata/array_comp_try_iterate_over_empty_string.linter.golden new file mode 100644 index 0000000..e69de29 diff --git a/testdata/array_comp_try_iterate_over_obj.golden b/testdata/array_comp_try_iterate_over_obj.golden index 123943f..4a72d1d 100644 --- a/testdata/array_comp_try_iterate_over_obj.golden +++ b/testdata/array_comp_try_iterate_over_obj.golden @@ -1,4 +1,4 @@ -RUNTIME ERROR: std.flatMap second param must be array / string, got object +RUNTIME ERROR: Unexpected type object, expected array ------------------------------------------------- testdata/array_comp_try_iterate_over_obj:1:1-16 diff --git a/testdata/array_comp_try_iterate_over_string.golden b/testdata/array_comp_try_iterate_over_string.golden new file mode 100644 index 0000000..fc9a815 --- /dev/null +++ b/testdata/array_comp_try_iterate_over_string.golden @@ -0,0 +1,10 @@ +RUNTIME ERROR: Unexpected type string, expected array +------------------------------------------------- + testdata/array_comp_try_iterate_over_string:1:1-17 + +[a for a in 'b'] + +------------------------------------------------- + During evaluation + + diff --git a/testdata/array_comp_try_iterate_over_string.jsonnet b/testdata/array_comp_try_iterate_over_string.jsonnet new file mode 100644 index 0000000..05cd1e8 --- /dev/null +++ b/testdata/array_comp_try_iterate_over_string.jsonnet @@ -0,0 +1 @@ +[a for a in 'b'] diff --git a/testdata/array_comp_try_iterate_over_string.linter.golden b/testdata/array_comp_try_iterate_over_string.linter.golden new file mode 100644 index 0000000..e69de29 diff --git a/testdata/object_comp_try_iterate_over_obj.golden b/testdata/object_comp_try_iterate_over_obj.golden new file mode 100644 index 0000000..75df53e --- /dev/null +++ b/testdata/object_comp_try_iterate_over_obj.golden @@ -0,0 +1,15 @@ +RUNTIME ERROR: Unexpected type object, expected array +------------------------------------------------- + testdata/object_comp_try_iterate_over_obj:1:1-23 + +{ [a]: 0 for a in {} } + +------------------------------------------------- + testdata/object_comp_try_iterate_over_obj:1:1-23 + +{ [a]: 0 for a in {} } + +------------------------------------------------- + During evaluation + + diff --git a/testdata/object_comp_try_iterate_over_obj.jsonnet b/testdata/object_comp_try_iterate_over_obj.jsonnet new file mode 100644 index 0000000..18b4289 --- /dev/null +++ b/testdata/object_comp_try_iterate_over_obj.jsonnet @@ -0,0 +1 @@ +{ [a]: 0 for a in {} } diff --git a/testdata/object_comp_try_iterate_over_obj.linter.golden b/testdata/object_comp_try_iterate_over_obj.linter.golden new file mode 100644 index 0000000..e69de29 diff --git a/testdata/object_comp_try_iterate_over_string.golden b/testdata/object_comp_try_iterate_over_string.golden new file mode 100644 index 0000000..1841378 --- /dev/null +++ b/testdata/object_comp_try_iterate_over_string.golden @@ -0,0 +1,15 @@ +RUNTIME ERROR: Unexpected type string, expected array +------------------------------------------------- + testdata/object_comp_try_iterate_over_string:1:1-24 + +{ [a]: 0 for a in 'b' } + +------------------------------------------------- + testdata/object_comp_try_iterate_over_string:1:1-24 + +{ [a]: 0 for a in 'b' } + +------------------------------------------------- + During evaluation + + diff --git a/testdata/object_comp_try_iterate_over_string.jsonnet b/testdata/object_comp_try_iterate_over_string.jsonnet new file mode 100644 index 0000000..52a0610 --- /dev/null +++ b/testdata/object_comp_try_iterate_over_string.jsonnet @@ -0,0 +1 @@ +{ [a]: 0 for a in 'b' } diff --git a/testdata/object_comp_try_iterate_over_string.linter.golden b/testdata/object_comp_try_iterate_over_string.linter.golden new file mode 100644 index 0000000..e69de29