From 88f9e232ba34c879f9f95fdee0871b64759fef4e Mon Sep 17 00:00:00 2001 From: Johan Fylling Date: Thu, 20 Aug 2026 19:13:48 +0200 Subject: [PATCH] ast: Enable `and`/`or` keywords (#9054) Promoting `and`/`or` future keywords out of their experimental status; enabling them in default capabilities. Signed-off-by: Johan Fylling --- capabilities.json | 4 +- cmd/capabilities_jsonv2_test.go | 4 + cmd/capabilities_test.go | 4 + cmd/fmt_test.go | 62 +--- cmd/oracle_jsonv2_test.go | 26 +- cmd/oracle_test.go | 26 +- internal/planner/planner_test.go | 1 - v1/ast/compile.go | 2 - v1/ast/compile_test.go | 35 +-- v1/ast/oracle/oracle_logical_test.go | 23 +- v1/ast/oracle/oracle_test.go | 20 +- v1/ast/parser.go | 7 +- v1/ast/parser_logical_test.go | 42 +-- v1/ast/parser_test.go | 48 ++-- v1/ast/policy_appenders_test.go | 4 - v1/format/format_test.go | 21 +- .../v1/logic_operators/and_basic.yaml | 11 - .../v1/logic_operators/and_explicit_body.yaml | 7 - .../v1/logic_operators/and_short_circuit.yaml | 3 - .../v1/logic_operators/builtin_calls.yaml | 5 - .../testdata/v1/logic_operators/chained.yaml | 7 - .../v1/logic_operators/comprehension.yaml | 7 - .../v1/logic_operators/default_and_else.yaml | 6 - .../testdata/v1/logic_operators/every.yaml | 6 - .../testdata/v1/logic_operators/grouping.yaml | 13 - .../v1/logic_operators/iteration.yaml | 12 - .../v1/logic_operators/local_collision.yaml | 5 - .../testdata/v1/logic_operators/negation.yaml | 8 - .../testdata/v1/logic_operators/nesting.yaml | 5 - .../testdata/v1/logic_operators/or_basic.yaml | 13 - .../v1/logic_operators/or_explicit_body.yaml | 7 - .../v1/logic_operators/or_short_circuit.yaml | 3 - .../v1/logic_operators/or_single_result.yaml | 3 - .../v1/logic_operators/precedence.yaml | 4 - .../v1/logic_operators/rule_head.yaml | 9 - .../v1/logic_operators/with_modifier.yaml | 4 - v1/topdown/topdown_logical_test.go | 3 +- v1/topdown/topdown_partial_test.go | 265 +++++++++--------- 38 files changed, 239 insertions(+), 496 deletions(-) diff --git a/capabilities.json b/capabilities.json index 1b02dd4ace..ccecfa24b1 100644 --- a/capabilities.json +++ b/capabilities.json @@ -5006,7 +5006,9 @@ } ], "future_keywords": [ - "not" + "and", + "not", + "or" ], "wasm_abi_versions": [ { diff --git a/cmd/capabilities_jsonv2_test.go b/cmd/capabilities_jsonv2_test.go index 7b626e1e45..e6993c2025 100644 --- a/cmd/capabilities_jsonv2_test.go +++ b/cmd/capabilities_jsonv2_test.go @@ -162,7 +162,9 @@ func TestCapabilitiesCurrent(t *testing.T) { ast.FeatureTemplateStrings, }, expFutureKeywords: []string{ + "and", "not", + "or", }, }, { @@ -176,11 +178,13 @@ func TestCapabilitiesCurrent(t *testing.T) { ast.FeatureKeywordsInRefs, }, expFutureKeywords: []string{ + "and", "in", "every", "contains", "if", "not", + "or", }, }, } diff --git a/cmd/capabilities_test.go b/cmd/capabilities_test.go index bcb50b2bf7..0e9c2def78 100644 --- a/cmd/capabilities_test.go +++ b/cmd/capabilities_test.go @@ -163,6 +163,8 @@ func TestCapabilitiesCurrent(t *testing.T) { }, expFutureKeywords: []string{ "not", + "and", + "or", }, }, { @@ -181,6 +183,8 @@ func TestCapabilitiesCurrent(t *testing.T) { "contains", "if", "not", + "and", + "or", }, }, } diff --git a/cmd/fmt_test.go b/cmd/fmt_test.go index d792201f9a..381733109b 100644 --- a/cmd/fmt_test.go +++ b/cmd/fmt_test.go @@ -1346,9 +1346,7 @@ foo["if"]["else"] := true } } -// Experimental future keywords are hidden from the default capabilities, so -// formatting a module that imports one requires --capabilities to list it. -func TestFmtFormatExperimentalKeywords(t *testing.T) { +func TestFmtFormatLogicalKeywords(t *testing.T) { unformatted := `package test import future.keywords.and @@ -1365,36 +1363,22 @@ import future.keywords.or p if input.a and input.b or input.c ` - experimental := func() fmtCommandParams { - params := newFmtCommandParams() - params.capabilitiesFlag.C = ast.CapabilitiesForThisVersion( - ast.CapabilitiesRegoVersion(ast.RegoV1), - ast.CapabilitiesExperimentalKeywords(true)) - return *params - } - cases := []struct { - note string - params fmtCommandParams - expected string - expectedErr string + note string + params fmtCommandParams + expected string }{ { - note: "default capabilities", - params: *newFmtCommandParams(), - expectedErr: "rego_parse_error: unexpected keyword, must be one of [contains every if in not]", - }, - { - note: "capabilities listing the keywords", - params: experimental(), + note: "default capabilities", + params: *newFmtCommandParams(), expected: formatted, }, { - note: "capabilities listing the keywords, --check-result", + note: "default capabilities, --check-result", params: func() fmtCommandParams { - params := experimental() + params := newFmtCommandParams() params.checkResult = true - return params + return *params }(), expected: formatted, }, @@ -1413,19 +1397,7 @@ p if input.a and input.b or input.c t.Fatalf("Unexpected error: %s", err) } - err = formatFile(&tc.params, &stdout, policyFile, info, err) - - if tc.expectedErr != "" { - if err == nil { - t.Fatalf("Expected error but got: %s", stdout.String()) - } - if !strings.Contains(err.Error(), tc.expectedErr) { - t.Fatalf("Expected error to contain:\n\n%s\n\nGot:\n\n%s", tc.expectedErr, err.Error()) - } - return - } - - if err != nil { + if err := formatFile(&tc.params, &stdout, policyFile, info, err); err != nil { t.Fatalf("Unexpected error: %s", err) } if actual := stdout.String(); actual != tc.expected { @@ -1437,19 +1409,7 @@ p if input.a and input.b or input.c t.Run("stdin, "+tc.note, func(t *testing.T) { var stdout bytes.Buffer - err := formatStdin(&tc.params, bytes.NewReader([]byte(unformatted)), &stdout) - - if tc.expectedErr != "" { - if err == nil { - t.Fatalf("Expected error but got: %s", stdout.String()) - } - if !strings.Contains(err.Error(), tc.expectedErr) { - t.Fatalf("Expected error to contain:\n\n%s\n\nGot:\n\n%s", tc.expectedErr, err.Error()) - } - return - } - - if err != nil { + if err := formatStdin(&tc.params, bytes.NewReader([]byte(unformatted)), &stdout); err != nil { t.Fatalf("Unexpected error: %s", err) } if actual := stdout.String(); actual != tc.expected { diff --git a/cmd/oracle_jsonv2_test.go b/cmd/oracle_jsonv2_test.go index d7404a9c36..25e4a181ea 100644 --- a/cmd/oracle_jsonv2_test.go +++ b/cmd/oracle_jsonv2_test.go @@ -9,6 +9,7 @@ import ( "fmt" "path" "reflect" + "slices" "strings" "testing" @@ -152,7 +153,7 @@ q = true`) // The oracle parses the stdin buffer itself, so keywords that are gated behind // capabilities must reach it through the --capabilities flag. -func TestOracleFindDefinitionExperimentalKeywords(t *testing.T) { +func TestOracleFindDefinitionCapabilities(t *testing.T) { module := `package test import future.keywords.or @@ -165,7 +166,12 @@ q := 1 r := 2` - capabilities, err := json.Marshal(ast.CapabilitiesForThisVersion(ast.CapabilitiesExperimentalKeywords(true))) + restricted := ast.CapabilitiesForThisVersion() + restricted.FutureKeywords = slices.DeleteFunc(restricted.FutureKeywords, func(kw string) bool { + return kw == "or" + }) + + capabilities, err := json.Marshal(restricted) if err != nil { t.Fatal(err) } @@ -191,9 +197,11 @@ r := 2` stdout := bytes.NewBuffer(nil) err = dofindDefinition(params, bytes.NewBufferString(module), stdout, []string{arg}) - if err == nil || !strings.Contains(err.Error(), "rego_parse_error") { - t.Fatal("expected parse error without capabilities but got:", err, "result:", stdout.String()) - } + expectJSON(t, err, stdout, fmt.Sprintf(`{"result": { + "file": %q, + "row": 9, + "col": 1 + }}`, path.Join(rootDir, "test.rego"))) if err := params.capabilities.Set(path.Join(rootDir, "capabilities.json")); err != nil { t.Fatal(err) @@ -202,11 +210,9 @@ r := 2` stdout.Reset() err = dofindDefinition(params, bytes.NewBufferString(module), stdout, []string{arg}) - expectJSON(t, err, stdout, fmt.Sprintf(`{"result": { - "file": %q, - "row": 9, - "col": 1 - }}`, path.Join(rootDir, "test.rego"))) + if err == nil || !strings.Contains(err.Error(), "rego_parse_error") { + t.Fatal("expected parse error with restricted capabilities but got:", err, "result:", stdout.String()) + } } // The rego-version must be left undefined unless explicitly asked for, so that the diff --git a/cmd/oracle_test.go b/cmd/oracle_test.go index 4a1ad3711a..1c91094a56 100644 --- a/cmd/oracle_test.go +++ b/cmd/oracle_test.go @@ -9,6 +9,7 @@ import ( "fmt" "path" "reflect" + "slices" "strings" "testing" @@ -152,7 +153,7 @@ q = true`) // The oracle parses the stdin buffer itself, so keywords that are gated behind // capabilities must reach it through the --capabilities flag. -func TestOracleFindDefinitionExperimentalKeywords(t *testing.T) { +func TestOracleFindDefinitionCapabilities(t *testing.T) { module := `package test import future.keywords.or @@ -165,7 +166,12 @@ q := 1 r := 2` - capabilities, err := json.Marshal(ast.CapabilitiesForThisVersion(ast.CapabilitiesExperimentalKeywords(true))) + restricted := ast.CapabilitiesForThisVersion() + restricted.FutureKeywords = slices.DeleteFunc(restricted.FutureKeywords, func(kw string) bool { + return kw == "or" + }) + + capabilities, err := json.Marshal(restricted) if err != nil { t.Fatal(err) } @@ -191,9 +197,11 @@ r := 2` stdout := bytes.NewBuffer(nil) err = dofindDefinition(params, bytes.NewBufferString(module), stdout, []string{arg}) - if err == nil || !strings.Contains(err.Error(), "rego_parse_error") { - t.Fatal("expected parse error without capabilities but got:", err, "result:", stdout.String()) - } + expectJSON(t, err, stdout, fmt.Sprintf(`{"result": { + "file": %q, + "row": 9, + "col": 1 + }}`, path.Join(rootDir, "test.rego"))) if err := params.capabilities.Set(path.Join(rootDir, "capabilities.json")); err != nil { t.Fatal(err) @@ -202,11 +210,9 @@ r := 2` stdout.Reset() err = dofindDefinition(params, bytes.NewBufferString(module), stdout, []string{arg}) - expectJSON(t, err, stdout, fmt.Sprintf(`{"result": { - "file": %q, - "row": 9, - "col": 1 - }}`, path.Join(rootDir, "test.rego"))) + if err == nil || !strings.Contains(err.Error(), "rego_parse_error") { + t.Fatal("expected parse error with restricted capabilities but got:", err, "result:", stdout.String()) + } } // The rego-version must be left undefined unless explicitly asked for, so that the diff --git a/internal/planner/planner_test.go b/internal/planner/planner_test.go index 61014ae084..11d433c0d2 100644 --- a/internal/planner/planner_test.go +++ b/internal/planner/planner_test.go @@ -927,7 +927,6 @@ func TestPlannerLogicalOps(t *testing.T) { } parserOpts := ast.ParserOptions{ - Capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesExperimentalKeywords(true)), FutureKeywords: []string{"and", "or", "not"}, } diff --git a/v1/ast/compile.go b/v1/ast/compile.go index 9e7577773a..803e9a8bd2 100644 --- a/v1/ast/compile.go +++ b/v1/ast/compile.go @@ -1216,7 +1216,6 @@ func (c *Compiler) buildRequiredCapabilities() { if c.moduleIsRegoV1(c.Modules[name]) { for kw := range futureKeywords { // Don't output experimental keywords for wildcard imports - // TODO: Remove on and/or release if _, internal := experimentalFutureKeywords[kw]; internal { continue } @@ -1224,7 +1223,6 @@ func (c *Compiler) buildRequiredCapabilities() { } } else { for kw := range allFutureKeywords { - // TODO: Remove on and/or release if _, internal := experimentalFutureKeywords[kw]; internal { continue } diff --git a/v1/ast/compile_test.go b/v1/ast/compile_test.go index 8926fe7269..2a5efeb8ca 100644 --- a/v1/ast/compile_test.go +++ b/v1/ast/compile_test.go @@ -1607,7 +1607,6 @@ func TestCompilerCheckSafetyBodyReordering(t *testing.T) { for i, tc := range tests { t.Run(tc.note, func(t *testing.T) { opts := ParserOptions{ - Capabilities: CapabilitiesForThisVersion(CapabilitiesExperimentalKeywords(true)), AllFutureKeywords: true, } c := NewCompiler() @@ -1815,7 +1814,6 @@ func TestCompilerCheckSafetyBodyErrors(t *testing.T) { // Compile test module. opts := ParserOptions{ - Capabilities: CapabilitiesForThisVersion(CapabilitiesExperimentalKeywords(true)), FutureKeywords: []string{"and", "or"}, } @@ -3894,10 +3892,6 @@ func runStrictnessTestCase(t *testing.T, cases []strictnessTestCase, assertLocat compiler.Modules = map[string]*Module{ "test": MustParseModuleWithOpts(tc.module, ParserOptions{ RegoVersion: RegoV0, - Capabilities: CapabilitiesForThisVersion( - CapabilitiesRegoVersion(RegoV0), - CapabilitiesExperimentalKeywords(true), - ), }), } compileStages(compiler, "") @@ -8201,16 +8195,7 @@ func TestCompilerRewriteTemplateStrings(t *testing.T) { exp string } - caps := CapabilitiesForThisVersion(CapabilitiesExperimentalKeywords(true)) - - opts := CompileOpts{ - ParserOptions: ParserOptions{ - Capabilities: caps, - }, - } - popts := func(options ParserOptions) ParserOptions { - options.Capabilities = caps options.AllFutureKeywords = true return options } @@ -8223,7 +8208,7 @@ func TestCompilerRewriteTemplateStrings(t *testing.T) { t.Run(tc.note, func(t *testing.T) { t.Parallel() t.Helper() - c := MustCompileModulesWithOpts(map[string]string{"test.rego": tc.module}, opts) + c := MustCompileModules(map[string]string{"test.rego": tc.module}) if exp, act := module(tc.exp, popts), c.Modules["test.rego"]; !exp.Equal(act) { t.Fatalf("Expected:\n\n%v\n\nGot:\n\n%v", exp, act) } @@ -11376,7 +11361,7 @@ func TestCompilerBuildRequiredCapabilities(t *testing.T) { import future.keywords `, opts: CompileOpts{ParserOptions: ParserOptions{RegoVersion: RegoV0}}, - keywords: []string{"contains", "every", "if", "in", "not"}, + keywords: []string{"and", "contains", "every", "if", "in", "not", "or"}, }, { note: "future.keywords wildcard, v1 module", @@ -11387,7 +11372,7 @@ func TestCompilerBuildRequiredCapabilities(t *testing.T) { `, opts: CompileOpts{ParserOptions: ParserOptions{RegoVersion: RegoV1}}, features: []string{"rego_v1"}, - keywords: []string{"not"}, + keywords: []string{"and", "not", "or"}, }, { note: "future.keywords wildcard, default rego-version module (v1)", @@ -11397,7 +11382,7 @@ func TestCompilerBuildRequiredCapabilities(t *testing.T) { import future.keywords `, features: []string{"rego_v1"}, - keywords: []string{"not"}, + keywords: []string{"and", "not", "or"}, }, { note: "future.keywords specific, v0 module", @@ -14918,11 +14903,7 @@ func TestCompilerAndOrRegoVersionParity(t *testing.T) { for _, tc := range tests { t.Run(tc.note, func(t *testing.T) { popts := ParserOptions{ - RegoVersion: tc.regoVersion, - Capabilities: CapabilitiesForThisVersion( - CapabilitiesRegoVersion(tc.regoVersion), - CapabilitiesExperimentalKeywords(true), - ), + RegoVersion: tc.regoVersion, FutureKeywords: []string{"and", "or"}, } @@ -14943,7 +14924,6 @@ func TestCompilerAndOrRegoVersionParity(t *testing.T) { func TestCompilerAndOrImports(t *testing.T) { popts := ParserOptions{ - Capabilities: CapabilitiesForThisVersion(CapabilitiesExperimentalKeywords(true)), FutureKeywords: []string{"and", "or"}, } @@ -15917,7 +15897,6 @@ func TestCompilerAndOrImports(t *testing.T) { } } `, ParserOptions{ - Capabilities: CapabilitiesForThisVersion(CapabilitiesExperimentalKeywords(true)), FutureKeywords: []string{"and", "or", "not"}, }), }, @@ -15942,7 +15921,6 @@ func TestCompilerAndOrImports(t *testing.T) { } } `, ParserOptions{ - Capabilities: CapabilitiesForThisVersion(CapabilitiesExperimentalKeywords(true)), FutureKeywords: []string{"and", "or", "not"}, }), }, @@ -16145,7 +16123,6 @@ func TestCompilerAndOrImports(t *testing.T) { not (input.a or input.b) } `, ParserOptions{ - Capabilities: CapabilitiesForThisVersion(CapabilitiesExperimentalKeywords(true)), FutureKeywords: []string{"and", "or", "not"}, }), }, @@ -16232,7 +16209,6 @@ func TestCompilerAndOrImports(t *testing.T) { func TestCompilerLogicalGroupRewrites(t *testing.T) { popts := ParserOptions{ - Capabilities: CapabilitiesForThisVersion(CapabilitiesExperimentalKeywords(true)), FutureKeywords: []string{"and", "or", "not"}, } @@ -16311,7 +16287,6 @@ func TestCompilerLogicalGroupRewrites(t *testing.T) { func TestQueryCompilerAndOrImports(t *testing.T) { popts := ParserOptions{ - Capabilities: CapabilitiesForThisVersion(CapabilitiesExperimentalKeywords(true)), AllFutureKeywords: true, } c := NewCompiler() diff --git a/v1/ast/oracle/oracle_logical_test.go b/v1/ast/oracle/oracle_logical_test.go index 0b44e3aa06..cc5f451db0 100644 --- a/v1/ast/oracle/oracle_logical_test.go +++ b/v1/ast/oracle/oracle_logical_test.go @@ -15,13 +15,8 @@ import ( // from the module before parsing, and its byte offset becomes the query position. const cursorMarker = "‸" -func experimentalCapabilities() *ast.Capabilities { - return ast.CapabilitiesForThisVersion(ast.CapabilitiesExperimentalKeywords(true)) -} - func logicalParserOpts() ast.ParserOptions { return ast.ParserOptions{ - Capabilities: experimentalCapabilities(), FutureKeywords: []string{"and", "or", "not"}, } } @@ -288,7 +283,7 @@ r := 2`, }, { note: "keywords activated by future import", - opts: &ast.ParserOptions{Capabilities: experimentalCapabilities()}, + opts: &ast.ParserOptions{}, modules: map[string]string{ "buffer.rego": `package test @@ -397,22 +392,6 @@ q := 1 r := 2`, exp: errors.New("rego_parse_error"), }, - { - note: "buffer parse error - future import not in capabilities", - opts: &ast.ParserOptions{}, - buffer: `package test - -import future.keywords.and - -p if { - ‸q and r -} - -q := 1 - -r := 2`, - exp: errors.New("unexpected keyword, must be one of"), - }, } for _, tc := range cases { diff --git a/v1/ast/oracle/oracle_test.go b/v1/ast/oracle/oracle_test.go index f1849fe056..9599d3d451 100644 --- a/v1/ast/oracle/oracle_test.go +++ b/v1/ast/oracle/oracle_test.go @@ -2,6 +2,7 @@ package oracle import ( "errors" + "slices" "strings" "testing" @@ -1190,10 +1191,7 @@ r := 2` t.Run("capabilities from compiler", func(t *testing.T) { t.Parallel() - capabilities := ast.CapabilitiesForThisVersion(ast.CapabilitiesExperimentalKeywords(true)) - o := New().WithCompiler(ast.NewCompiler().WithCapabilities(capabilities)) - - _, module, err := o.compileUpto("SetRuleTree", DefinitionQuery{ + _, module, err := New().compileUpto("SetRuleTree", DefinitionQuery{ Buffer: []byte(logicalModule), Filename: "test.rego", }) @@ -1203,6 +1201,20 @@ r := 2` if !module.Rules[0].Body[0].IsAnd() { t.Fatal("expected logical and expression but got:", module.Rules[0].Body[0]) } + + // The compiler's capabilities gate the keywords available to the buffer. + capabilities := ast.CapabilitiesForThisVersion() + capabilities.FutureKeywords = slices.DeleteFunc(capabilities.FutureKeywords, func(kw string) bool { + return kw == "and" + }) + o := New().WithCompiler(ast.NewCompiler().WithCapabilities(capabilities)) + + if _, _, err := o.compileUpto("SetRuleTree", DefinitionQuery{ + Buffer: []byte(logicalModule), + Filename: "test.rego", + }); err == nil { + t.Fatal("expected parse error") + } }) } diff --git a/v1/ast/parser.go b/v1/ast/parser.go index 7993897620..e402e46fff 100644 --- a/v1/ast/parser.go +++ b/v1/ast/parser.go @@ -3893,11 +3893,8 @@ var allFutureKeywords map[string]tokens.Token // experimentalFutureKeywords are future keywords that exist in the parser but are // intentionally hidden from the default capabilities advertisement. // They are only activated when a policy imports them AND the active -// capabilities explicitly list them. -var experimentalFutureKeywords = map[string]struct{}{ - "and": {}, - "or": {}, -} +// capabilities explicitly list them. There are currently none. +var experimentalFutureKeywords = map[string]struct{}{} var allFutureKeywordTokens map[tokens.Token]struct{} diff --git a/v1/ast/parser_logical_test.go b/v1/ast/parser_logical_test.go index 0e962ed056..282b8b922e 100644 --- a/v1/ast/parser_logical_test.go +++ b/v1/ast/parser_logical_test.go @@ -8,10 +8,8 @@ import ( ) func logicalParserOpts(extraFuture ...string) ParserOptions { - caps := CapabilitiesForThisVersion(CapabilitiesExperimentalKeywords(true)) fk := append([]string{"and", "or"}, extraFuture...) return ParserOptions{ - Capabilities: caps, FutureKeywords: fk, } } @@ -19,7 +17,6 @@ func logicalParserOpts(extraFuture ...string) ParserOptions { func logicalParserOptsForVersion(v RegoVersion, extraFuture ...string) ParserOptions { opts := logicalParserOpts(extraFuture...) opts.RegoVersion = v - opts.Capabilities = CapabilitiesForThisVersion(CapabilitiesRegoVersion(v), CapabilitiesExperimentalKeywords(true)) return opts } @@ -457,29 +454,9 @@ func TestParseLogical_RefsContainingAndOr(t *testing.T) { } } -func TestParseLogical_NoLeakageOnImport(t *testing.T) { - t.Run("import error does not leak keyword names", func(t *testing.T) { - opts := ParserOptions{Capabilities: CapabilitiesForThisVersion()} - input := `package x - import future.keywords.and - ` - _, _, err := ParseStatementsWithOpts("", input, opts) - if err == nil { - t.Fatal("expected parse error for import of and without experimental caps") - } - // Error message is of the form "unexpected keyword, must be one of - // [...]". The bracketed list must not include `and` or `or`. - msg := err.Error() - for _, leaked := range []string{"[and ", " and]", " and ", "[or ", " or]", " or "} { - if strings.Contains(msg, leaked) { - t.Errorf("error leaks internal keyword existence (%q present in %q)", leaked, msg) - } - } - }) - - t.Run("import accepted with experimental caps", func(t *testing.T) { +func TestParseLogical_KeywordImport(t *testing.T) { + t.Run("import accepted", func(t *testing.T) { opts := ParserOptions{ - Capabilities: CapabilitiesForThisVersion(CapabilitiesExperimentalKeywords(true)), FutureKeywords: []string{"and"}, } input := `package x @@ -529,8 +506,7 @@ func TestParseLogical_NoLeakageOnImport(t *testing.T) { }, } opts := ParserOptions{ - RegoVersion: RegoV1, - Capabilities: CapabilitiesForThisVersion(CapabilitiesExperimentalKeywords(true)), + RegoVersion: RegoV1, } for _, tc := range tests { t.Run(tc.note, func(t *testing.T) { @@ -575,8 +551,7 @@ func TestParseLogical_NoLeakageOnImport(t *testing.T) { }, } opts := ParserOptions{ - RegoVersion: RegoV1, - Capabilities: CapabilitiesForThisVersion(CapabilitiesExperimentalKeywords(true)), + RegoVersion: RegoV1, } for _, tc := range tests { t.Run(tc.note, func(t *testing.T) { @@ -591,8 +566,6 @@ func TestParseLogical_NoLeakageOnImport(t *testing.T) { // TestParseLogical_PartialActivation exercises the case where one of `and` / // `or` is enabled in the scanner but the other is not. func TestParseLogical_PartialActivation(t *testing.T) { - caps := CapabilitiesForThisVersion(CapabilitiesExperimentalKeywords(true)) - tests := []struct { note string enable []string @@ -649,7 +622,6 @@ func TestParseLogical_PartialActivation(t *testing.T) { for _, tc := range tests { t.Run(tc.note, func(t *testing.T) { opts := ParserOptions{ - Capabilities: caps, FutureKeywords: tc.enable, } body, err := ParseBodyWithOpts(tc.input, opts) @@ -902,11 +874,7 @@ func TestParseLogical_InnerExprHasLocation(t *testing.T) { } ` - popts := ParserOptions{ - Capabilities: CapabilitiesForThisVersion(CapabilitiesExperimentalKeywords(true)), - } - - mod, err := ParseModuleWithOpts("test.rego", module, popts) + mod, err := ParseModuleWithOpts("test.rego", module, ParserOptions{}) if err != nil { t.Fatalf("unexpected parse error: %v", err) } diff --git a/v1/ast/parser_test.go b/v1/ast/parser_test.go index 108d25782a..6c8a21ab22 100644 --- a/v1/ast/parser_test.go +++ b/v1/ast/parser_test.go @@ -2909,7 +2909,7 @@ func TestImport(t *testing.T) { func TestFutureImports(t *testing.T) { assertParseErrorContains(t, "future", "import future", "invalid import, must be `future.keywords`") assertParseErrorContains(t, "future.a", "import future.a", "invalid import, must be `future.keywords`") - assertParseErrorContains(t, "unknown keyword", "import future.keywords.xyz", "unexpected keyword, must be one of [contains every if in not]") + assertParseErrorContains(t, "unknown keyword", "import future.keywords.xyz", "unexpected keyword, must be one of [and contains every if in not or]") assertParseErrorContains(t, "all keyword import + alias", "import future.keywords as xyz", "`future` imports cannot be aliased") assertParseErrorContains(t, "keyword import + alias", "import future.keywords.in as xyz", "`future` imports cannot be aliased") @@ -2940,11 +2940,10 @@ func TestFutureAndRegoV1ImportsExtraction(t *testing.T) { // These tests assert that "import future..." and "import rego.v1" statements in policies cause // the proper keywords to be added to the parser's list of known keywords, and that they don't add any others. tests := []struct { - note, imp string - regoVersion RegoVersion - capabilities *Capabilities - exp map[string]tokens.Token - absent []string + note, imp string + regoVersion RegoVersion + exp map[string]tokens.Token + absent []string }{ { note: "simple import", @@ -2991,11 +2990,10 @@ func TestFutureAndRegoV1ImportsExtraction(t *testing.T) { absent: []string{"in", "every", "contains", "if"}, }, { - note: "not imported in v0 does not enable experimental future keywords", - regoVersion: RegoV0, - capabilities: CapabilitiesForThisVersion(CapabilitiesExperimentalKeywords(true)), - imp: "import future.keywords.not", - absent: []string{"and", "or"}, + note: "not imported in v0 does not enable the logical future keywords", + regoVersion: RegoV0, + imp: "import future.keywords.not", + absent: []string{"and", "or"}, }, { note: "in imported in v0 does not enable the other v0 future keywords", @@ -3005,24 +3003,21 @@ func TestFutureAndRegoV1ImportsExtraction(t *testing.T) { absent: []string{"every", "contains", "if"}, }, { - note: "not imported does not enable experimental keywords", - capabilities: CapabilitiesForThisVersion(CapabilitiesExperimentalKeywords(true)), - imp: "import future.keywords.not", - absent: []string{"and", "or"}, + note: "not imported does not enable the logical keywords", + imp: "import future.keywords.not", + absent: []string{"and", "or"}, }, { - note: "in imported does not enable experimental keywords", - capabilities: CapabilitiesForThisVersion(CapabilitiesExperimentalKeywords(true)), - imp: "import future.keywords.in", - exp: map[string]tokens.Token{"in": tokens.In}, - absent: []string{"and", "or"}, + note: "in imported does not enable the logical keywords", + imp: "import future.keywords.in", + exp: map[string]tokens.Token{"in": tokens.In}, + absent: []string{"and", "or"}, }, { - note: "and imported does not enable or", - capabilities: CapabilitiesForThisVersion(CapabilitiesExperimentalKeywords(true)), - imp: "import future.keywords.and", - exp: map[string]tokens.Token{"and": tokens.LogicalAnd}, - absent: []string{"or"}, + note: "and imported does not enable or", + imp: "import future.keywords.and", + exp: map[string]tokens.Token{"and": tokens.LogicalAnd}, + absent: []string{"or"}, }, } for _, tc := range tests { @@ -3031,9 +3026,6 @@ func TestFutureAndRegoV1ImportsExtraction(t *testing.T) { if tc.regoVersion != RegoUndefined { parser = parser.WithRegoVersion(tc.regoVersion) } - if tc.capabilities != nil { - parser = parser.WithCapabilities(tc.capabilities) - } _, _, errs := parser.Parse() if exp, act := 0, len(errs); exp != act { t.Fatalf("expected %d errors, got %d: %v", exp, act, errs) diff --git a/v1/ast/policy_appenders_test.go b/v1/ast/policy_appenders_test.go index 21b7ab7105..7ae20a6bf5 100644 --- a/v1/ast/policy_appenders_test.go +++ b/v1/ast/policy_appenders_test.go @@ -160,7 +160,6 @@ r = true`, { name: "and, implicit", node: ast.MustParseExprWithOpts("x and y", ast.ParserOptions{ - Capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesExperimentalKeywords(true)), AllFutureKeywords: true, }), want: `x and y`, @@ -184,7 +183,6 @@ r = true`, { name: "and, explicit", node: ast.MustParseExprWithOpts("{ x } and { y }", ast.ParserOptions{ - Capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesExperimentalKeywords(true)), AllFutureKeywords: true, }), want: `{ x } and { y }`, @@ -192,7 +190,6 @@ r = true`, { name: "or, implicit", node: ast.MustParseExprWithOpts("x or y", ast.ParserOptions{ - Capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesExperimentalKeywords(true)), AllFutureKeywords: true, }), want: `x or y`, @@ -216,7 +213,6 @@ r = true`, { name: "or, explicit", node: ast.MustParseExprWithOpts("{ x } or { y }", ast.ParserOptions{ - Capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesExperimentalKeywords(true)), AllFutureKeywords: true, }), want: `{ x } or { y }`, diff --git a/v1/format/format_test.go b/v1/format/format_test.go index 6f924dc133..31d38b3337 100644 --- a/v1/format/format_test.go +++ b/v1/format/format_test.go @@ -100,13 +100,6 @@ func TestFormatSourceError(t *testing.T) { } } -// TODO: Remove once `and`/`or` are no longer experimental keywords. -func experimentalKeywordCapabilities(v ast.RegoVersion) *ast.Capabilities { - return ast.CapabilitiesForThisVersion( - ast.CapabilitiesRegoVersion(v), - ast.CapabilitiesExperimentalKeywords(true)) -} - func TestFormatV0Source(t *testing.T) { regoFiles, err := filepath.Glob("testfiles/v0/*.rego") if err != nil { @@ -126,8 +119,7 @@ func TestFormatV0Source(t *testing.T) { } popts := ast.ParserOptions{ - RegoVersion: ast.RegoV0, - Capabilities: experimentalKeywordCapabilities(ast.RegoV0), + RegoVersion: ast.RegoV0, } opts := Opts{ RegoVersion: ast.RegoV0, @@ -187,8 +179,7 @@ func TestFormatV1Source(t *testing.T) { } popts := ast.ParserOptions{ - RegoVersion: ast.RegoV1, - Capabilities: experimentalKeywordCapabilities(ast.RegoV1), + RegoVersion: ast.RegoV1, } opts := Opts{ RegoVersion: ast.RegoV1, @@ -931,7 +922,6 @@ p if { }`, ast.ParserOptions{ FutureKeywords: []string{"and"}, - Capabilities: experimentalKeywordCapabilities(ast.RegoV1), }), expected: `package test @@ -950,7 +940,6 @@ p { }`, ast.ParserOptions{ FutureKeywords: []string{"and"}, - Capabilities: experimentalKeywordCapabilities(ast.RegoV1), }), expected: `package test @@ -969,7 +958,6 @@ p if { }`, ast.ParserOptions{ FutureKeywords: []string{"or"}, - Capabilities: experimentalKeywordCapabilities(ast.RegoV1), }), expected: `package test @@ -988,7 +976,6 @@ p { }`, ast.ParserOptions{ FutureKeywords: []string{"or"}, - Capabilities: experimentalKeywordCapabilities(ast.RegoV1), }), expected: `package test @@ -1355,7 +1342,7 @@ func TestFormatKeywordsInRefs(t *testing.T) { t.Fatalf("Failed to read rego source: %v", err) } - caps := experimentalKeywordCapabilities(regoVersion) + caps := ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(regoVersion)) feats := make([]string, 0, len(caps.Features)) for _, f := range caps.Features { if f != ast.FeatureKeywordsInRefs { @@ -1368,7 +1355,7 @@ func TestFormatKeywordsInRefs(t *testing.T) { RegoVersion: regoVersion, // The source is parsed with keywords in refs allowed; it is // only the formatting of refs that drops the feature. - Capabilities: experimentalKeywordCapabilities(regoVersion), + Capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesRegoVersion(regoVersion)), } opts := Opts{ RegoVersion: regoVersion, diff --git a/v1/test/cases/testdata/v1/logic_operators/and_basic.yaml b/v1/test/cases/testdata/v1/logic_operators/and_basic.yaml index e69d488778..19391917b5 100644 --- a/v1/test/cases/testdata/v1/logic_operators/and_basic.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/and_basic.yaml @@ -1,7 +1,6 @@ --- cases: - note: "logic_op/and/basic: both operands true" - experimental_keywords: true query: data.test.p = x modules: - | @@ -14,7 +13,6 @@ cases: want_result: - x: true - note: "logic_op/and/basic: lhs false" - experimental_keywords: true query: data.test.p = x modules: - | @@ -26,7 +24,6 @@ cases: } want_result: [] - note: "logic_op/and/basic: rhs false" - experimental_keywords: true query: data.test.p = x modules: - | @@ -38,7 +35,6 @@ cases: } want_result: [] - note: "logic_op/and/basic: both false" - experimental_keywords: true query: data.test.p = x modules: - | @@ -50,7 +46,6 @@ cases: } want_result: [] - note: "logic_op/and/basic: both succeed via input" - experimental_keywords: true query: data.test.p = x modules: - | @@ -66,7 +61,6 @@ cases: want_result: - x: true - note: "logic_op/and/basic: lhs undefined ref" - experimental_keywords: true query: data.test.p = x modules: - | @@ -80,7 +74,6 @@ cases: "y": 2 want_result: [] - note: "logic_op/and/basic: rhs undefined ref" - experimental_keywords: true query: data.test.p = x modules: - | @@ -94,7 +87,6 @@ cases: x: 1 want_result: [] - note: "logic_op/and/basic: virtual rule refs on both sides" - experimental_keywords: true query: data.test.p = x modules: - | @@ -113,7 +105,6 @@ cases: want_result: - x: true - note: "logic_op/and/basic: virtual rule ref, rhs fails" - experimental_keywords: true query: data.test.p = x modules: - | @@ -131,7 +122,6 @@ cases: "y": 0 want_result: [] - note: "logic_op/and/basic: dotted data refs both defined" - experimental_keywords: true query: data.test.p = x modules: - | @@ -148,7 +138,6 @@ cases: want_result: - x: true - note: "logic_op/and/basic: indexed data refs" - experimental_keywords: true query: data.test.p = x modules: - | diff --git a/v1/test/cases/testdata/v1/logic_operators/and_explicit_body.yaml b/v1/test/cases/testdata/v1/logic_operators/and_explicit_body.yaml index 4a029b808f..933a2605f9 100644 --- a/v1/test/cases/testdata/v1/logic_operators/and_explicit_body.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/and_explicit_body.yaml @@ -1,7 +1,6 @@ --- cases: - note: "logic_op/and/explicit-body: explicit lhs, implicit rhs" - experimental_keywords: true query: data.test.p = x modules: - | @@ -16,7 +15,6 @@ cases: want_result: - x: true - note: "logic_op/and/explicit-body: implicit lhs, explicit rhs" - experimental_keywords: true query: data.test.p = x modules: - | @@ -31,7 +29,6 @@ cases: want_result: - x: true - note: "logic_op/and/explicit-body: both explicit" - experimental_keywords: true query: data.test.p = x modules: - | @@ -44,7 +41,6 @@ cases: want_result: - x: true - note: "logic_op/and/explicit-body: multi-expr operand body" - experimental_keywords: true query: data.test.p = x modules: - | @@ -59,7 +55,6 @@ cases: want_result: - x: true - note: "logic_op/and/explicit-body: multi-expr operand body fails on second expr" - experimental_keywords: true query: data.test.p = x modules: - | @@ -73,7 +68,6 @@ cases: a: 100 want_result: [] - note: "logic_op/and/explicit-body: each operand has its own scope" - experimental_keywords: true query: data.test.p = x modules: - | @@ -87,7 +81,6 @@ cases: want_result: - x: true - note: "logic_op/and/explicit-body: some-binding inside operand body" - experimental_keywords: true query: data.test.p = x modules: - | diff --git a/v1/test/cases/testdata/v1/logic_operators/and_short_circuit.yaml b/v1/test/cases/testdata/v1/logic_operators/and_short_circuit.yaml index 690a751c29..5998332580 100644 --- a/v1/test/cases/testdata/v1/logic_operators/and_short_circuit.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/and_short_circuit.yaml @@ -4,7 +4,6 @@ # *not* evaluated when the LHS already failed. cases: - note: "logic_op/and/short-circuit: lhs fails, rhs builtin not evaluated" - experimental_keywords: true strict_error: true query: data.test.p = x modules: @@ -19,7 +18,6 @@ cases: x: 0 want_result: [] - note: "logic_op/and/short-circuit: lhs succeeds, rhs builtin error surfaces" - experimental_keywords: true strict_error: true query: data.test.p = x modules: @@ -35,7 +33,6 @@ cases: want_error_code: eval_builtin_error want_error: to_number - note: "logic_op/and/short-circuit: guarded type access" - experimental_keywords: true strict_error: true query: data.test.p = x modules: diff --git a/v1/test/cases/testdata/v1/logic_operators/builtin_calls.yaml b/v1/test/cases/testdata/v1/logic_operators/builtin_calls.yaml index 8c6b009ee3..0c808556af 100644 --- a/v1/test/cases/testdata/v1/logic_operators/builtin_calls.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/builtin_calls.yaml @@ -1,7 +1,6 @@ --- cases: - note: "logic_op/builtin_calls: and with builtins on both sides" - experimental_keywords: true query: data.test.p = x modules: - | @@ -17,7 +16,6 @@ cases: want_result: - x: true - note: "logic_op/builtin_calls: and with builtins, rhs fails" - experimental_keywords: true query: data.test.p = x modules: - | @@ -32,7 +30,6 @@ cases: s: "no-leading-slash" want_result: [] - note: "logic_op/builtin_calls: or with builtins on both sides" - experimental_keywords: true query: data.test.p = x modules: - | @@ -48,7 +45,6 @@ cases: want_result: - x: true - note: "logic_op/builtin_calls: nested builtin call, distinct result temporaries" - experimental_keywords: true query: data.test.p = x modules: - | @@ -64,7 +60,6 @@ cases: want_result: - x: true - note: "logic_op/builtin_calls: comprehension in builtin, or branch" - experimental_keywords: true query: data.test.p = x modules: - | diff --git a/v1/test/cases/testdata/v1/logic_operators/chained.yaml b/v1/test/cases/testdata/v1/logic_operators/chained.yaml index afedeeff4e..e8992b72b1 100644 --- a/v1/test/cases/testdata/v1/logic_operators/chained.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/chained.yaml @@ -1,7 +1,6 @@ --- cases: - note: "logic_op/chained: a and b and c, all true" - experimental_keywords: true query: data.test.p = x modules: - | @@ -18,7 +17,6 @@ cases: want_result: - x: true - note: "logic_op/chained: a and b and c, middle false" - experimental_keywords: true query: data.test.p = x modules: - | @@ -34,7 +32,6 @@ cases: c: true want_result: [] - note: "logic_op/chained: a or b or c, only middle true" - experimental_keywords: true query: data.test.p = x modules: - | @@ -51,7 +48,6 @@ cases: want_result: - x: true - note: "logic_op/chained: a or b or c, all false" - experimental_keywords: true query: data.test.p = x modules: - | @@ -67,7 +63,6 @@ cases: c: false want_result: [] - note: "logic_op/chained: a and b or c and d, right side wins" - experimental_keywords: true query: data.test.p = x modules: - | @@ -87,7 +82,6 @@ cases: want_result: - x: true - note: "logic_op/chained: a or b and c or d, b-and-c is the only true conjunct" - experimental_keywords: true query: data.test.p = x modules: - | @@ -107,7 +101,6 @@ cases: want_result: - x: true - note: "logic_op/chained: deeply nested explicit bodies" - experimental_keywords: true query: data.test.p = x modules: - | diff --git a/v1/test/cases/testdata/v1/logic_operators/comprehension.yaml b/v1/test/cases/testdata/v1/logic_operators/comprehension.yaml index 45d13626e3..17ab8f43c7 100644 --- a/v1/test/cases/testdata/v1/logic_operators/comprehension.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/comprehension.yaml @@ -1,7 +1,6 @@ --- cases: - note: "logic_op/comprehension: array, and as filter" - experimental_keywords: true query: data.test.p = x modules: - | @@ -18,7 +17,6 @@ cases: want_result: - x: [5, 3] - note: "logic_op/comprehension: array, or as filter" - experimental_keywords: true query: data.test.p = x modules: - | @@ -34,7 +32,6 @@ cases: want_result: - x: [-1, 12] - note: "logic_op/comprehension: set with and (dedup)" - experimental_keywords: true query: data.test.p = x modules: - | @@ -52,7 +49,6 @@ cases: want_result: - x: [1, 2, 3] - note: "logic_op/comprehension: object with or" - experimental_keywords: true query: data.test.p = x modules: - | @@ -73,7 +69,6 @@ cases: a: 5 c: -20 - note: "logic_op/comprehension: nested comprehension with and" - experimental_keywords: true query: data.test.p = x modules: - | @@ -94,7 +89,6 @@ cases: want_result: - x: [[1, 5], [3]] - note: "logic_op/comprehension: walk-bound iteration with and as filter" - experimental_keywords: true query: data.test.p = x modules: - | @@ -115,7 +109,6 @@ cases: want_result: - x: ["hello", "nested"] - note: "logic_op/comprehension: walk-bound iteration with or as filter" - experimental_keywords: true query: data.test.p = x modules: - | diff --git a/v1/test/cases/testdata/v1/logic_operators/default_and_else.yaml b/v1/test/cases/testdata/v1/logic_operators/default_and_else.yaml index 541890d81b..faab0f7d9b 100644 --- a/v1/test/cases/testdata/v1/logic_operators/default_and_else.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/default_and_else.yaml @@ -1,7 +1,6 @@ --- cases: - note: "logic_op/default: default returns when or body fails" - experimental_keywords: true query: data.test.p = x modules: - | @@ -17,7 +16,6 @@ cases: want_result: - x: false - note: "logic_op/default: default skipped when or body succeeds" - experimental_keywords: true query: data.test.p = x modules: - | @@ -33,7 +31,6 @@ cases: want_result: - x: true - note: "logic_op/default: default returns when and body fails" - experimental_keywords: true query: data.test.p = x modules: - | @@ -49,7 +46,6 @@ cases: want_result: - x: "fallback" - note: "logic_op/else: else fires when and body fails" - experimental_keywords: true query: data.test.p = x modules: - | @@ -64,7 +60,6 @@ cases: want_result: - x: "fallback" - note: "logic_op/else: else skipped when or body succeeds" - experimental_keywords: true query: data.test.p = x modules: - | @@ -79,7 +74,6 @@ cases: want_result: - x: "ok" - note: "logic_op/else: chained else with or in primary branch" - experimental_keywords: true query: data.test.p = x modules: - | diff --git a/v1/test/cases/testdata/v1/logic_operators/every.yaml b/v1/test/cases/testdata/v1/logic_operators/every.yaml index 82c666f110..bdabe162e0 100644 --- a/v1/test/cases/testdata/v1/logic_operators/every.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/every.yaml @@ -1,7 +1,6 @@ --- cases: - note: "logic_op/every: and inside every body, all succeed" - experimental_keywords: true query: data.test.p = x modules: - | @@ -18,7 +17,6 @@ cases: want_result: - x: true - note: "logic_op/every: and inside every body, one fails" - experimental_keywords: true query: data.test.p = x modules: - | @@ -34,7 +32,6 @@ cases: xs: [1, 2, 100] want_result: [] - note: "logic_op/every: or inside every body, all match at least one branch" - experimental_keywords: true query: data.test.p = x modules: - | @@ -51,7 +48,6 @@ cases: want_result: - x: true - note: "logic_op/every: or inside every body, one matches neither branch" - experimental_keywords: true query: data.test.p = x modules: - | @@ -67,7 +63,6 @@ cases: xs: [-3, 0, 5] want_result: [] - note: "logic_op/every: not (or) inside every body" - experimental_keywords: true query: data.test.p = x modules: - | @@ -85,7 +80,6 @@ cases: want_result: - x: true - note: "logic_op/every: scoped local var inside operand body" - experimental_keywords: true query: data.test.p = x modules: - | diff --git a/v1/test/cases/testdata/v1/logic_operators/grouping.yaml b/v1/test/cases/testdata/v1/logic_operators/grouping.yaml index b59bc32483..653ba78ff7 100644 --- a/v1/test/cases/testdata/v1/logic_operators/grouping.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/grouping.yaml @@ -1,7 +1,6 @@ --- cases: - note: "logic_op/grouping: a and (b or c) short-circuits when a is false" - experimental_keywords: true query: data.test.p = x modules: - | @@ -18,7 +17,6 @@ cases: c: true want_result: [] - note: "logic_op/grouping: no parens, a and b or c" - experimental_keywords: true query: data.test.p = x modules: - | @@ -36,7 +34,6 @@ cases: want_result: - x: true - note: "logic_op/grouping: a and (b or c)" - experimental_keywords: true query: data.test.p = x modules: - | @@ -55,7 +52,6 @@ cases: - x: true - note: "logic_op/grouping: (a or b) and c" - experimental_keywords: true query: data.test.p = x modules: - | @@ -73,7 +69,6 @@ cases: want_result: - x: true - note: "logic_op/grouping: (a or b) and c, short-circuit" - experimental_keywords: true query: data.test.p = x modules: - | @@ -91,7 +86,6 @@ cases: want_result: [] - note: "logic_op/grouping: (a or b) and (c or d) parity with braces" - experimental_keywords: true query: data.test.p = x modules: - | @@ -110,7 +104,6 @@ cases: want_result: - x: true - note: "logic_op/grouping: { a or b } and { c or d } parity with parens" - experimental_keywords: true query: data.test.p = x modules: - | @@ -130,7 +123,6 @@ cases: - x: true - note: "logic_op/grouping: (a and (b or c)) and d" - experimental_keywords: true query: data.test.p = x modules: - | @@ -150,7 +142,6 @@ cases: - x: true - note: "logic_op/grouping: redundant parens (((a or b)))" - experimental_keywords: true query: data.test.p = x modules: - | @@ -167,7 +158,6 @@ cases: - x: true - note: "logic_op/grouping: not (a or b), neither true" - experimental_keywords: true query: data.test.p = x modules: - | @@ -184,7 +174,6 @@ cases: want_result: - x: true - note: "logic_op/grouping: not (a or b), one true" - experimental_keywords: true query: data.test.p = x modules: - | @@ -201,7 +190,6 @@ cases: want_result: [] - note: "logic_op/grouping: (a with ...) and b, with scoped to operand" - experimental_keywords: true query: data.test.p = x modules: - | @@ -217,7 +205,6 @@ cases: - x: true - note: "logic_op/grouping: (a or b with ...) binds with to whole group" - experimental_keywords: true query: data.test.p = x modules: - | diff --git a/v1/test/cases/testdata/v1/logic_operators/iteration.yaml b/v1/test/cases/testdata/v1/logic_operators/iteration.yaml index 0685d6c765..a6ddb00fdb 100644 --- a/v1/test/cases/testdata/v1/logic_operators/iteration.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/iteration.yaml @@ -2,7 +2,6 @@ # Operand bodies with internal enumeration. cases: - note: "logic_op/iteration: and" - experimental_keywords: true query: data.test.p = x modules: - | @@ -15,7 +14,6 @@ cases: want_result: - x: true - note: "logic_op/iteration: and lhs would yield multiple successes, comprehension emits once" - experimental_keywords: true query: data.test.p = x modules: - | @@ -28,7 +26,6 @@ cases: want_result: - x: [1] - note: "logic_op/iteration: and rhs would yield multiple successes, comprehension emits once" - experimental_keywords: true query: data.test.p = x modules: - | @@ -41,7 +38,6 @@ cases: want_result: - x: [1] - note: "logic_op/iteration: and lhs+rhs would yield multiple successes, comprehension emits once" - experimental_keywords: true query: data.test.p = x modules: - | @@ -54,7 +50,6 @@ cases: want_result: - x: [1] - note: "logic_op/iteration: or lhs would yield multiple successes, comprehension emits once" - experimental_keywords: true query: data.test.p = x modules: - | @@ -67,7 +62,6 @@ cases: want_result: - x: [1] - note: "logic_op/iteration: or rhs would yield multiple successes, comprehension emits once" - experimental_keywords: true query: data.test.p = x modules: - | @@ -80,7 +74,6 @@ cases: want_result: - x: [1] - note: "logic_op/iteration: enumeration inside operand body, no fail-and-retry" - experimental_keywords: true query: data.test.p = x modules: - | @@ -94,7 +87,6 @@ cases: want_result: - x: [1] - note: "logic_op/iteration: enumeration inside operand body, all fail" - experimental_keywords: true query: data.test.p = x modules: - | @@ -108,7 +100,6 @@ cases: want_result: - x: [] - note: "logic_op/iteration: not around or with iteration" - experimental_keywords: true query: data.test.p = x modules: - | @@ -125,7 +116,6 @@ cases: want_result: - x: true - note: "logic_op/iteration: not around or with iteration, one element matches" - experimental_keywords: true query: data.test.p = x modules: - | @@ -140,7 +130,6 @@ cases: xs: [0, 5, 200] want_result: [] - note: "logic_op/iteration: not around and with iteration" - experimental_keywords: true query: data.test.p = x modules: - | @@ -157,7 +146,6 @@ cases: want_result: - x: true - note: "logic_op/iteration: not around and with iteration, one element matches" - experimental_keywords: true query: data.test.p = x modules: - | diff --git a/v1/test/cases/testdata/v1/logic_operators/local_collision.yaml b/v1/test/cases/testdata/v1/logic_operators/local_collision.yaml index 9ef53a14f2..c9a3c6f7fa 100644 --- a/v1/test/cases/testdata/v1/logic_operators/local_collision.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/local_collision.yaml @@ -1,7 +1,6 @@ --- cases: - note: "logic_op/local_collision: same name in both and-operand bodies" - experimental_keywords: true query: data.test.p = x modules: - | @@ -14,7 +13,6 @@ cases: want_result: - x: true - note: "logic_op/local_collision: same name in both or-operand bodies" - experimental_keywords: true query: data.test.p = x modules: - | @@ -27,7 +25,6 @@ cases: want_result: - x: true - note: "logic_op/local_collision: same name across and-body and outer scope" - experimental_keywords: true query: data.test.p = x modules: - | @@ -43,7 +40,6 @@ cases: want_result: - x: true - note: "logic_op/local_collision: same `some` iter name in both operand bodies" - experimental_keywords: true query: data.test.p = x modules: - | @@ -56,7 +52,6 @@ cases: want_result: - x: true - note: "logic_op/local_collision: nested explicit body re-uses same name" - experimental_keywords: true query: data.test.p = x modules: - | diff --git a/v1/test/cases/testdata/v1/logic_operators/negation.yaml b/v1/test/cases/testdata/v1/logic_operators/negation.yaml index 49490bccfc..7bb2a85753 100644 --- a/v1/test/cases/testdata/v1/logic_operators/negation.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/negation.yaml @@ -1,7 +1,6 @@ --- cases: - note: "logic_op/negation: not {x and y}, de Morgan equivalent" - experimental_keywords: true query: data.test.p = x modules: - | @@ -18,7 +17,6 @@ cases: want_result: - x: true - note: "logic_op/negation: not {x and y}, both true" - experimental_keywords: true query: data.test.p = x modules: - | @@ -34,7 +32,6 @@ cases: "y": true want_result: [] - note: "logic_op/negation: not {x or y}, neither true" - experimental_keywords: true query: data.test.p = x modules: - | @@ -51,7 +48,6 @@ cases: want_result: - x: true - note: "logic_op/negation: not {x or y}, one true" - experimental_keywords: true query: data.test.p = x modules: - | @@ -67,7 +63,6 @@ cases: "y": true want_result: [] - note: "logic_op/negation: not x and not y, inner negation" - experimental_keywords: true query: data.test.p = x modules: - | @@ -83,7 +78,6 @@ cases: want_result: - x: true - note: "logic_op/negation: not x or not y, inner negation" - experimental_keywords: true query: data.test.p = x modules: - | @@ -99,7 +93,6 @@ cases: want_result: - x: true - note: "logic_op/negation: not {not x or not y}, double negation" - experimental_keywords: true query: data.test.p = x modules: - | @@ -116,7 +109,6 @@ cases: want_result: - x: true - note: "logic_op/negation: not {x and not y}, mixed" - experimental_keywords: true query: data.test.p = x modules: - | diff --git a/v1/test/cases/testdata/v1/logic_operators/nesting.yaml b/v1/test/cases/testdata/v1/logic_operators/nesting.yaml index 5642ab4b5f..a39c4b1619 100644 --- a/v1/test/cases/testdata/v1/logic_operators/nesting.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/nesting.yaml @@ -1,7 +1,6 @@ --- cases: - note: "logic_op/nesting: explicit {a or b} and {c or d}, all true" - experimental_keywords: true query: data.test.p = x modules: - | @@ -20,7 +19,6 @@ cases: want_result: - x: true - note: "logic_op/nesting: explicit {a or b} and {c or d}, one branch fails" - experimental_keywords: true query: data.test.p = x modules: - | @@ -38,7 +36,6 @@ cases: d: true want_result: [] - note: "logic_op/nesting: a or {b and {c or d}}, three-deep" - experimental_keywords: true query: data.test.p = x modules: - | @@ -57,7 +54,6 @@ cases: want_result: - x: true - note: "logic_op/nesting: a or {b and {c or d}}, top-level lhs short-circuits" - experimental_keywords: true query: data.test.p = x modules: - | @@ -76,7 +72,6 @@ cases: want_result: - x: true - note: "logic_op/nesting: x and {y or {z and w}}, mixed" - experimental_keywords: true query: data.test.p = x modules: - | diff --git a/v1/test/cases/testdata/v1/logic_operators/or_basic.yaml b/v1/test/cases/testdata/v1/logic_operators/or_basic.yaml index a67aa057fd..8d2b82079c 100644 --- a/v1/test/cases/testdata/v1/logic_operators/or_basic.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/or_basic.yaml @@ -1,7 +1,6 @@ --- cases: - note: "logic_op/or/basic: lhs true" - experimental_keywords: true query: data.test.p = x modules: - | @@ -14,7 +13,6 @@ cases: want_result: - x: true - note: "logic_op/or/basic: rhs true" - experimental_keywords: true query: data.test.p = x modules: - | @@ -27,7 +25,6 @@ cases: want_result: - x: true - note: "logic_op/or/basic: both true" - experimental_keywords: true query: data.test.p = x modules: - | @@ -40,7 +37,6 @@ cases: want_result: - x: true - note: "logic_op/or/basic: both false" - experimental_keywords: true query: data.test.p = x modules: - | @@ -52,7 +48,6 @@ cases: } want_result: [] - note: "logic_op/or/basic: lhs succeeds via input" - experimental_keywords: true query: data.test.p = x modules: - | @@ -67,7 +62,6 @@ cases: want_result: - x: true - note: "logic_op/or/basic: rhs succeeds via input" - experimental_keywords: true query: data.test.p = x modules: - | @@ -82,7 +76,6 @@ cases: want_result: - x: true - note: "logic_op/or/basic: neither side succeeds via input" - experimental_keywords: true query: data.test.p = x modules: - | @@ -96,7 +89,6 @@ cases: role: "guest" want_result: [] - note: "logic_op/or/basic: lhs undefined ref, rhs true" - experimental_keywords: true query: data.test.p = x modules: - | @@ -109,7 +101,6 @@ cases: want_result: - x: true - note: "logic_op/or/basic: virtual rule refs, lhs succeeds" - experimental_keywords: true query: data.test.p = x modules: - | @@ -128,7 +119,6 @@ cases: want_result: - x: true - note: "logic_op/or/basic: virtual rule refs, rhs succeeds" - experimental_keywords: true query: data.test.p = x modules: - | @@ -147,7 +137,6 @@ cases: want_result: - x: true - note: "logic_op/or/basic: virtual rule refs, neither succeeds" - experimental_keywords: true query: data.test.p = x modules: - | @@ -165,7 +154,6 @@ cases: "y": 0 want_result: [] - note: "logic_op/or/basic: dotted data refs, only one defined" - experimental_keywords: true query: data.test.p = x modules: - | @@ -181,7 +169,6 @@ cases: want_result: - x: true - note: "logic_op/or/basic: indexed data refs" - experimental_keywords: true query: data.test.p = x modules: - | diff --git a/v1/test/cases/testdata/v1/logic_operators/or_explicit_body.yaml b/v1/test/cases/testdata/v1/logic_operators/or_explicit_body.yaml index 918a70bc73..f9e0de08b7 100644 --- a/v1/test/cases/testdata/v1/logic_operators/or_explicit_body.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/or_explicit_body.yaml @@ -1,7 +1,6 @@ --- cases: - note: "logic_op/or/explicit-body: explicit lhs, implicit rhs" - experimental_keywords: true query: data.test.p = x modules: - | @@ -16,7 +15,6 @@ cases: want_result: - x: true - note: "logic_op/or/explicit-body: implicit lhs, explicit rhs" - experimental_keywords: true query: data.test.p = x modules: - | @@ -31,7 +29,6 @@ cases: want_result: - x: true - note: "logic_op/or/explicit-body: both explicit, lhs succeeds" - experimental_keywords: true query: data.test.p = x modules: - | @@ -44,7 +41,6 @@ cases: want_result: - x: true - note: "logic_op/or/explicit-body: both explicit, rhs succeeds" - experimental_keywords: true query: data.test.p = x modules: - | @@ -57,7 +53,6 @@ cases: want_result: - x: true - note: "logic_op/or/explicit-body: both explicit, both fail" - experimental_keywords: true query: data.test.p = x modules: - | @@ -69,7 +64,6 @@ cases: } want_result: [] - note: "logic_op/or/explicit-body: multi-expr operand body" - experimental_keywords: true query: data.test.p = x modules: - | @@ -82,7 +76,6 @@ cases: want_result: - x: true - note: "logic_op/or/explicit-body: some-binding inside operand body" - experimental_keywords: true query: data.test.p = x modules: - | diff --git a/v1/test/cases/testdata/v1/logic_operators/or_short_circuit.yaml b/v1/test/cases/testdata/v1/logic_operators/or_short_circuit.yaml index 81414814b9..cbd67221b3 100644 --- a/v1/test/cases/testdata/v1/logic_operators/or_short_circuit.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/or_short_circuit.yaml @@ -4,7 +4,6 @@ # *not* evaluated when the LHS already succeeded. cases: - note: "logic_op/or/short-circuit: lhs succeeds, rhs builtin not evaluated" - experimental_keywords: true strict_error: true query: data.test.p = x modules: @@ -20,7 +19,6 @@ cases: want_result: - x: true - note: "logic_op/or/short-circuit: lhs fails, rhs builtin error surfaces" - experimental_keywords: true strict_error: true query: data.test.p = x modules: @@ -36,7 +34,6 @@ cases: want_error_code: eval_builtin_error want_error: to_number - note: "logic_op/or/short-circuit: lhs fails, rhs evaluates and succeeds" - experimental_keywords: true strict_error: true query: data.test.p = x modules: diff --git a/v1/test/cases/testdata/v1/logic_operators/or_single_result.yaml b/v1/test/cases/testdata/v1/logic_operators/or_single_result.yaml index 8547731a15..278b3595e7 100644 --- a/v1/test/cases/testdata/v1/logic_operators/or_single_result.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/or_single_result.yaml @@ -1,7 +1,6 @@ --- cases: - note: "logic_op/or/single-result: both operands succeed contributes one element" - experimental_keywords: true query: data.test.p = x modules: - | @@ -16,7 +15,6 @@ cases: want_result: - x: [1] - note: "logic_op/or/single-result: nested or, all branches succeed contributes one element" - experimental_keywords: true query: data.test.p = x modules: - | @@ -29,7 +27,6 @@ cases: want_result: - x: [1] - note: "logic_op/or/single-result: inside iterating comprehension, one entry per outer binding" - experimental_keywords: true query: data.test.p = x modules: - | diff --git a/v1/test/cases/testdata/v1/logic_operators/precedence.yaml b/v1/test/cases/testdata/v1/logic_operators/precedence.yaml index 468a85fb1d..0e85f657b1 100644 --- a/v1/test/cases/testdata/v1/logic_operators/precedence.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/precedence.yaml @@ -1,7 +1,6 @@ --- cases: - note: "logic_op/precedence: `not x or y` parses as `(not x) or y`" - experimental_keywords: true query: data.test.p = x modules: - | @@ -20,7 +19,6 @@ cases: want_result: - x: true - note: "logic_op/precedence: `x or y and z` parses as `x or (y and z)`" - experimental_keywords: true query: data.test.p = x modules: - | @@ -41,7 +39,6 @@ cases: want_result: - x: true - note: "logic_op/precedence: `x and y or z and w` parses as `(x and y) or (z and w)`" - experimental_keywords: true query: data.test.p = x modules: - | @@ -63,7 +60,6 @@ cases: want_result: - x: true - note: "logic_op/precedence: `not x and not y or z` parses as `((not x) and (not y)) or z`" - experimental_keywords: true query: data.test.p = x modules: - | diff --git a/v1/test/cases/testdata/v1/logic_operators/rule_head.yaml b/v1/test/cases/testdata/v1/logic_operators/rule_head.yaml index 0f7b4751f4..848a6e0457 100644 --- a/v1/test/cases/testdata/v1/logic_operators/rule_head.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/rule_head.yaml @@ -1,7 +1,6 @@ --- cases: - note: "logic_op/rule_head: and under if-head, both true" - experimental_keywords: true query: data.test.p = x modules: - | @@ -15,7 +14,6 @@ cases: want_result: - x: true - note: "logic_op/rule_head: and under if-head, lhs false" - experimental_keywords: true query: data.test.p = x modules: - | @@ -28,7 +26,6 @@ cases: "y": 2 want_result: [] - note: "logic_op/rule_head: or under if-head, lhs true" - experimental_keywords: true query: data.test.p = x modules: - | @@ -42,7 +39,6 @@ cases: want_result: - x: true - note: "logic_op/rule_head: or under if-head, both false" - experimental_keywords: true query: data.test.p = x modules: - | @@ -55,7 +51,6 @@ cases: "y": 0 want_result: [] - note: "logic_op/rule_head: and under assignment head" - experimental_keywords: true query: data.test.p = x modules: - | @@ -69,7 +64,6 @@ cases: want_result: - x: 1 - note: "logic_op/rule_head: or under assignment head" - experimental_keywords: true query: data.test.p = x modules: - | @@ -83,7 +77,6 @@ cases: want_result: - x: "matched" - note: "logic_op/rule_head: and in function body" - experimental_keywords: true query: data.test.p = x modules: - | @@ -96,7 +89,6 @@ cases: want_result: - x: true - note: "logic_op/rule_head: and in function body, fails" - experimental_keywords: true query: data.test.p = x modules: - | @@ -108,7 +100,6 @@ cases: p if in_range(42) want_result: [] - note: "logic_op/rule_head: or in function body" - experimental_keywords: true query: data.test.p = x modules: - | diff --git a/v1/test/cases/testdata/v1/logic_operators/with_modifier.yaml b/v1/test/cases/testdata/v1/logic_operators/with_modifier.yaml index 9514e5bd45..b19e369392 100644 --- a/v1/test/cases/testdata/v1/logic_operators/with_modifier.yaml +++ b/v1/test/cases/testdata/v1/logic_operators/with_modifier.yaml @@ -1,7 +1,6 @@ --- cases: - note: "logic_op/with: outer with on and propagates to both operand bodies" - experimental_keywords: true query: data.test.p = x modules: - | @@ -14,7 +13,6 @@ cases: want_result: - x: true - note: "logic_op/with: with on lhs only, scoped to lhs" - experimental_keywords: true query: data.test.p = x modules: - | @@ -29,7 +27,6 @@ cases: want_result: - x: true - note: "logic_op/with: with on lhs only, scoped to lhs, but falls through to called scope" - experimental_keywords: true query: data.test.p = x modules: - | @@ -48,7 +45,6 @@ cases: want_result: - x: true - note: "logic_op/with: outer with on or, both operands see modified input" - experimental_keywords: true query: data.test.p = x modules: - | diff --git a/v1/topdown/topdown_logical_test.go b/v1/topdown/topdown_logical_test.go index b1b30c2d2c..b705813cc3 100644 --- a/v1/topdown/topdown_logical_test.go +++ b/v1/topdown/topdown_logical_test.go @@ -44,10 +44,9 @@ func touchCount(label string) int { return int(counter.(*atomic.Int64).Load()) } -// logicalParserOptions opts in to the experimental `and` / `or` keywords. +// logicalParserOptions opts in to the `and` / `or` keywords. func logicalParserOptions() ast.ParserOptions { return ast.ParserOptions{ - Capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesExperimentalKeywords(true)), FutureKeywords: []string{"and", "or"}, } } diff --git a/v1/topdown/topdown_partial_test.go b/v1/topdown/topdown_partial_test.go index fd7d972974..4f96f2e07c 100644 --- a/v1/topdown/topdown_partial_test.go +++ b/v1/topdown/topdown_partial_test.go @@ -41,7 +41,7 @@ func TestTopDownPartialEval(t *testing.T) { wantSupport []string wantSupportASTs []*ast.Module ignoreOrder bool - experimentalKeywords bool // opt in to experimental and/or keywords + logicalKeywords bool // opt in to the and/or keywords }{ { note: "empty", @@ -4353,9 +4353,9 @@ q if { input.x = 7 }`}, // and/or baseline (no unknowns: truth-table) { - note: "and: no unknowns, both true", - experimentalKeywords: true, - query: "data.test.p", + note: "and: no unknowns, both true", + logicalKeywords: true, + query: "data.test.p", modules: []string{`package test p if { true and true @@ -4363,9 +4363,9 @@ q if { input.x = 7 }`}, wantQueries: []string{""}, // unconditionally true }, { - note: "and: no unknowns, lhs false", - experimentalKeywords: true, - query: "data.test.p", + note: "and: no unknowns, lhs false", + logicalKeywords: true, + query: "data.test.p", modules: []string{`package test p if { false and true @@ -4373,9 +4373,9 @@ q if { input.x = 7 }`}, wantQueries: []string{}, // unconditionally false }, { - note: "and: no unknowns, rhs false", - experimentalKeywords: true, - query: "data.test.p", + note: "and: no unknowns, rhs false", + logicalKeywords: true, + query: "data.test.p", modules: []string{`package test p if { true and false @@ -4383,9 +4383,9 @@ q if { input.x = 7 }`}, wantQueries: []string{}, // unconditionally false }, { - note: "or: no unknowns, lhs true", - experimentalKeywords: true, - query: "data.test.p", + note: "or: no unknowns, lhs true", + logicalKeywords: true, + query: "data.test.p", modules: []string{`package test p if { true or false @@ -4393,9 +4393,9 @@ q if { input.x = 7 }`}, wantQueries: []string{""}, // unconditionally true }, { - note: "or: no unknowns, rhs true", - experimentalKeywords: true, - query: "data.test.p", + note: "or: no unknowns, rhs true", + logicalKeywords: true, + query: "data.test.p", modules: []string{`package test p if { false or true @@ -4403,9 +4403,9 @@ q if { input.x = 7 }`}, wantQueries: []string{""}, // unconditionally true }, { - note: "or: no unknowns, both true (single result)", - experimentalKeywords: true, - query: "data.test.p", + note: "or: no unknowns, both true (single result)", + logicalKeywords: true, + query: "data.test.p", modules: []string{`package test p if { true or true @@ -4413,9 +4413,9 @@ q if { input.x = 7 }`}, wantQueries: []string{""}, // unconditionally true }, { - note: "or: no unknowns, both false", - experimentalKeywords: true, - query: "data.test.p", + note: "or: no unknowns, both false", + logicalKeywords: true, + query: "data.test.p", modules: []string{`package test p if { false or false @@ -4427,9 +4427,9 @@ q if { input.x = 7 }`}, // TODO: PE optimization in #8680 { - note: "and: unknown lhs only", - experimentalKeywords: true, - query: "data.test.p = true", + note: "and: unknown lhs only", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { input.x > 0 and true @@ -4437,9 +4437,9 @@ q if { input.x = 7 }`}, wantQueries: []string{`{__local0__1 = input.x; gt(__local0__1, 0)} and true`}, }, { - note: "and: unknown rhs only", - experimentalKeywords: true, - query: "data.test.p = true", + note: "and: unknown rhs only", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { true and input.y > 0 @@ -4447,9 +4447,9 @@ q if { input.x = 7 }`}, wantQueries: []string{`true and {__local0__1 = input.y; gt(__local0__1, 0)}`}, }, { - note: "and: unknowns in both", - experimentalKeywords: true, - query: "data.test.p = true", + note: "and: unknowns in both", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { input.x > 0 and input.y > 0 @@ -4457,9 +4457,9 @@ q if { input.x = 7 }`}, wantQueries: []string{`{__local0__1 = input.x; gt(__local0__1, 0)} and {__local1__1 = input.y; gt(__local1__1, 0)}`}, }, { - note: "or: unknown lhs only", - experimentalKeywords: true, - query: "data.test.p = true", + note: "or: unknown lhs only", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { input.x > 0 or false @@ -4467,9 +4467,9 @@ q if { input.x = 7 }`}, wantQueries: []string{`{__local0__1 = input.x; gt(__local0__1, 0)} or false`}, }, { - note: "or: unknown rhs only", - experimentalKeywords: true, - query: "data.test.p = true", + note: "or: unknown rhs only", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { false or input.y > 0 @@ -4477,9 +4477,9 @@ q if { input.x = 7 }`}, wantQueries: []string{`false or {__local0__1 = input.y; gt(__local0__1, 0)}`}, }, { - note: "or: unknowns in both", - experimentalKeywords: true, - query: "data.test.p = true", + note: "or: unknowns in both", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { input.x > 0 or input.y > 0 @@ -4488,9 +4488,9 @@ q if { input.x = 7 }`}, }, // Baseline does NOT simplify even when one operand is statically true { - note: "or: unknown lhs, rhs known-true (no simplification at this layer)", - experimentalKeywords: true, - query: "data.test.p = true", + note: "or: unknown lhs, rhs known-true (no simplification at this layer)", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { input.x > 0 or true @@ -4498,9 +4498,9 @@ q if { input.x = 7 }`}, wantQueries: []string{`{__local0__1 = input.x; gt(__local0__1, 0)} or true`}, }, { - note: "and: unknown lhs, rhs known-true (no simplification at this layer)", - experimentalKeywords: true, - query: "data.test.p = true", + note: "and: unknown lhs, rhs known-true (no simplification at this layer)", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { input.x > 0 and true @@ -4510,9 +4510,9 @@ q if { input.x = 7 }`}, // nested chains, data-ref unknowns, multi-expr explicit bodies { - note: "and: nested left-leaning, all unknown", - experimentalKeywords: true, - query: "data.test.p = true", + note: "and: nested left-leaning, all unknown", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { input.a > 0 and input.b > 0 and input.c > 0 @@ -4529,9 +4529,9 @@ q if { input.x = 7 }`}, }`}, }, { - note: "or: nested left-leaning, all unknown", - experimentalKeywords: true, - query: "data.test.p = true", + note: "or: nested left-leaning, all unknown", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { input.a > 0 or input.b > 0 or input.c > 0 @@ -4548,10 +4548,10 @@ q if { input.x = 7 }`}, }`}, }, { - note: "and: mixed data and input unknowns", - experimentalKeywords: true, - unknowns: []string{"input", "data.foo"}, - query: "data.test.p = true", + note: "and: mixed data and input unknowns", + logicalKeywords: true, + unknowns: []string{"input", "data.foo"}, + query: "data.test.p = true", modules: []string{`package test p if { data.foo.x > 0 and input.y > 0 @@ -4565,9 +4565,9 @@ q if { input.x = 7 }`}, }`}, }, { - note: "and: explicit body multi-expr, unknowns inside", - experimentalKeywords: true, - query: "data.test.p = true", + note: "and: explicit body multi-expr, unknowns inside", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { { input.x > 0; input.x < 10 } and true @@ -4576,9 +4576,9 @@ q if { input.x = 7 }`}, }, { - note: "and (every): unknown inside", - experimentalKeywords: true, - query: "data.test.p = true", + note: "and (every): unknown inside", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { { x := input.x; every i in x { i in x; i < input.y } } and true @@ -4594,9 +4594,9 @@ q if { input.x = 7 }`}, } and true`}, }, { - note: "or (every): unknown inside", - experimentalKeywords: true, - query: "data.test.p = true", + note: "or (every): unknown inside", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { { x := input.x; every i in x { i in x; i < input.y } } or true @@ -4614,9 +4614,9 @@ q if { input.x = 7 }`}, // comprehensions inside and/or operand bodies { - note: "and: rhs set comp, cross-scope vars", - experimentalKeywords: true, - query: "data.test.p = true", + note: "and: rhs set comp, cross-scope vars", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { x := input.x @@ -4633,9 +4633,9 @@ q if { input.x = 7 }`}, __local0__1 = input.x`}, }, { - note: "and: lhs set comp, cross-scope vars", - experimentalKeywords: true, - query: "data.test.p = true", + note: "and: lhs set comp, cross-scope vars", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { x := input.x @@ -4652,9 +4652,9 @@ q if { input.x = 7 }`}, __local0__1 = input.x`}, }, { - note: "or: rhs set comp, cross-scope vars", - experimentalKeywords: true, - query: "data.test.p = true", + note: "or: rhs set comp, cross-scope vars", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { x := input.x @@ -4671,9 +4671,9 @@ q if { input.x = 7 }`}, __local0__1 = input.x`}, }, { - note: "or: lhs set comp, cross-scope vars", - experimentalKeywords: true, - query: "data.test.p = true", + note: "or: lhs set comp, cross-scope vars", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { x := input.x @@ -4690,9 +4690,9 @@ q if { input.x = 7 }`}, __local0__1 = input.x`}, }, { - note: "and: rhs array comp, cross-scope vars", - experimentalKeywords: true, - query: "data.test.p = true", + note: "and: rhs array comp, cross-scope vars", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { x := input.x @@ -4709,9 +4709,9 @@ q if { input.x = 7 }`}, __local0__1 = input.x`}, }, { - note: "or: lhs object comp, cross-scope vars", - experimentalKeywords: true, - query: "data.test.p = true", + note: "or: lhs object comp, cross-scope vars", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { x := input.x @@ -4730,9 +4730,9 @@ q if { input.x = 7 }`}, // and/or nested inside other outer constructs { - note: "every: body contains and with unknowns", - experimentalKeywords: true, - query: "data.test.p = true", + note: "every: body contains and with unknowns", + logicalKeywords: true, + query: "data.test.p = true", modules: []string{`package test p if { every x in [1, 2, 3] { x > 0 and x > input } @@ -4742,25 +4742,25 @@ q if { input.x = 7 }`}, }`}, }, { - note: "array comprehension: body contains or with unknowns", - experimentalKeywords: true, - query: "data.test.p = x", + note: "array comprehension: body contains or with unknowns", + logicalKeywords: true, + query: "data.test.p = x", modules: []string{`package test p := [n | n = input.xs[i]; n > 0 or n < 0]`}, wantQueries: []string{`x = [n1 | n1 = input.xs[i1]; gt(n1, 0) or lt(n1, 0)]`}, }, { - note: "set comprehension: body contains or with unknowns", - experimentalKeywords: true, - query: "data.test.p = x", + note: "set comprehension: body contains or with unknowns", + logicalKeywords: true, + query: "data.test.p = x", modules: []string{`package test p := {n | n = input.xs[i]; n > 0 or n < 0}`}, wantQueries: []string{`x = {n1 | n1 = input.xs[i1]; gt(n1, 0) or lt(n1, 0)}`}, }, { - note: "object comprehension: body contains and with unknowns", - experimentalKeywords: true, - query: "data.test.p = x", + note: "object comprehension: body contains and with unknowns", + logicalKeywords: true, + query: "data.test.p = x", modules: []string{`package test p := {k: v | v := input[k]; v > 0 and v < 10}`}, wantQueries: []string{`x = {k1: __local0__1 | __local0__1 = input[k1]; gt(__local0__1, 0) and lt(__local0__1, 10)}`}, @@ -4768,9 +4768,9 @@ q if { input.x = 7 }`}, // copy-propagation { - note: "copy propagation: and safety needs extra expr", - experimentalKeywords: true, - query: `data.test.p = true`, + note: "copy propagation: and safety needs extra expr", + logicalKeywords: true, + query: `data.test.p = true`, modules: []string{ `package test @@ -4786,9 +4786,9 @@ q if { input.x = 7 }`}, }, }, { - note: "copy propagation: or safety needs extra expr", - experimentalKeywords: true, - query: `data.test.p = true`, + note: "copy propagation: or safety needs extra expr", + logicalKeywords: true, + query: `data.test.p = true`, modules: []string{ `package test @@ -4807,9 +4807,9 @@ q if { input.x = 7 }`}, }, }, { - note: "copy propagation: and safety needs extra expr - no live var overlap", - experimentalKeywords: true, - query: `data.test.p = true`, + note: "copy propagation: and safety needs extra expr - no live var overlap", + logicalKeywords: true, + query: `data.test.p = true`, modules: []string{ `package test @@ -4830,8 +4830,7 @@ q if { input.x = 7 }`}, for _, tc := range tests { popts := ast.ParserOptions{} - if tc.experimentalKeywords { - popts.Capabilities = ast.CapabilitiesForThisVersion(ast.CapabilitiesExperimentalKeywords(true)) + if tc.logicalKeywords { popts.FutureKeywords = []string{"and", "or"} } @@ -4949,7 +4948,7 @@ func TestTopDownPartialEvalNegation(t *testing.T) { wantSupportASTs []*ast.Module ignoreOrder bool notBodyOnly bool - experimentalKeywords bool // opt in to experimental and/or keywords + logicalKeywords bool // opt in to the and/or keywords }{ { note: "with+builtin+negation: when replacement has no unknowns (args, defs), save negated expr without replacement", @@ -6667,10 +6666,10 @@ func TestTopDownPartialEvalNegation(t *testing.T) { // TODO: PE optimization in #8680 { - note: "not (and): unknown inside", - experimentalKeywords: true, - notBodyOnly: true, - query: "data.test.p = true", + note: "not (and): unknown inside", + logicalKeywords: true, + notBodyOnly: true, + query: "data.test.p = true", modules: []string{`package test p if { not { input.x > 0 and true } @@ -6680,10 +6679,10 @@ func TestTopDownPartialEvalNegation(t *testing.T) { }`}, }, { - note: "not (or): unknowns in both operands", - experimentalKeywords: true, - notBodyOnly: true, - query: "data.test.p = true", + note: "not (or): unknowns in both operands", + logicalKeywords: true, + notBodyOnly: true, + query: "data.test.p = true", modules: []string{`package test p if { not { input.x > 0 or input.y > 0 } @@ -6693,10 +6692,10 @@ func TestTopDownPartialEvalNegation(t *testing.T) { }`}, }, { - note: "not (and): unknown lhs, rhs static false ", - experimentalKeywords: true, - notBodyOnly: true, - query: "data.test.p = true", + note: "not (and): unknown lhs, rhs static false ", + logicalKeywords: true, + notBodyOnly: true, + query: "data.test.p = true", modules: []string{`package test p if { not { input.x > 0 and false } @@ -6706,10 +6705,10 @@ func TestTopDownPartialEvalNegation(t *testing.T) { }`}, }, { - note: "not (or): unknown rhs, lhs static true", - experimentalKeywords: true, - notBodyOnly: true, - query: "data.test.p = true", + note: "not (or): unknown rhs, lhs static true", + logicalKeywords: true, + notBodyOnly: true, + query: "data.test.p = true", modules: []string{`package test p if { not {true or input.y > 0} @@ -6719,10 +6718,10 @@ func TestTopDownPartialEvalNegation(t *testing.T) { }`}, }, { - note: "and (not): unknown inside", - experimentalKeywords: true, - notBodyOnly: true, - query: "data.test.p = true", + note: "and (not): unknown inside", + logicalKeywords: true, + notBodyOnly: true, + query: "data.test.p = true", modules: []string{`package test p if { { x := input.x; not x > 0 } and true @@ -6730,10 +6729,10 @@ func TestTopDownPartialEvalNegation(t *testing.T) { wantQueries: []string{`{ __local0__1 = input.x; not gt(__local0__1, 0) } and true`}, }, { - note: "or (not): unknown inside", - experimentalKeywords: true, - notBodyOnly: true, - query: "data.test.p = true", + note: "or (not): unknown inside", + logicalKeywords: true, + notBodyOnly: true, + query: "data.test.p = true", modules: []string{`package test p if { { x := input.x; not x > 0 } or true @@ -6761,8 +6760,7 @@ func TestTopDownPartialEvalNegation(t *testing.T) { } casePopts := popts - if tc.experimentalKeywords { - casePopts.Capabilities = ast.CapabilitiesForThisVersion(ast.CapabilitiesExperimentalKeywords(true)) + if tc.logicalKeywords { casePopts.FutureKeywords = append(append([]string{}, popts.FutureKeywords...), "and", "or") } @@ -6872,7 +6870,6 @@ func TestTopDownPartialEvalLogicalRoundTrip(t *testing.T) { t.Parallel() popts := ast.ParserOptions{ - Capabilities: ast.CapabilitiesForThisVersion(ast.CapabilitiesExperimentalKeywords(true)), FutureKeywords: []string{"and", "or"}, }