diff --git a/v1/format/format.go b/v1/format/format.go index 1f25938bae..6fe8b431fa 100644 --- a/v1/format/format.go +++ b/v1/format/format.go @@ -644,8 +644,12 @@ func (w *writer) writeRule(rule *ast.Rule, isElse bool, comments []*ast.Comment) // same line as the end of the head. Comparing against the head's // start row would wrongly expand the condition into a block whenever // the head value spans multiple lines (e.g. a multi-line call). + // + // Additionally, a single set term must not be stripped of the outer body + // braces, as that would semantically change the inner set to a body: + // `p if { { x } }` -> p if { x } headEndRow := rule.Head.Location.Row + strings.Count(string(rule.Head.Location.Text), "\n") - if rule.Body[0].Location.Row == headEndRow { + if rule.Body[0].Location.Row == headEndRow && !isSetTerm(rule.Body[0]) { w.write(" ") var err error comments, err = w.writeExpr(rule.Body[0], comments) @@ -1029,6 +1033,21 @@ func exprTermsEndRow(expr *ast.Expr) int { return loc.Row + bytes.Count(text, []byte{'\n'}) } +// isSetTerm reports whether expr is a non-negated set term. +func isSetTerm(expr *ast.Expr) bool { + if expr.IsNegated() { + return false + } + + term, ok := expr.Terms.(*ast.Term) + if !ok { + return false + } + + _, ok = term.Value.(ast.Set) + return ok +} + func (w *writer) writeSomeDecl(decl *ast.SomeDecl, comments []*ast.Comment) ([]*ast.Comment, error) { var err error comments, err = w.insertComments(comments, decl.Location) diff --git a/v1/format/testfiles/v1/test_lone_set_term_body.rego b/v1/format/testfiles/v1/test_lone_set_term_body.rego new file mode 100644 index 0000000000..00e3302f9f --- /dev/null +++ b/v1/format/testfiles/v1/test_lone_set_term_body.rego @@ -0,0 +1,35 @@ +package test + +multiple_elements if { {1, 2} } + +ref_element if { {input.x} } + +scalar_element if { {false} } + +set_element if { {{1}} } + +array_element if { {[1]} } + +parenthesized_scalar_element if { ({false}) } + +parenthesized_parenthesized if { ({1, 2}) } + +fn(x) if { {x, 2} } + +collection contains 1 if { {1, 2} } + +a.b.c if { {1, 2} } + +with_else := 1 if { {1, 2} } else := 2 if { true } + +already_multiline if { + {1, 2} +} + +ref_term if { input.x } + +object_term if { {"a": 1} } + +set_comprehension if { {y | y := input.x} } + +set_comparison if { {1, 2} == {1, 2} } diff --git a/v1/format/testfiles/v1/test_lone_set_term_body.rego.formatted b/v1/format/testfiles/v1/test_lone_set_term_body.rego.formatted new file mode 100644 index 0000000000..b4e0fa8d45 --- /dev/null +++ b/v1/format/testfiles/v1/test_lone_set_term_body.rego.formatted @@ -0,0 +1,59 @@ +package test + +multiple_elements if { + {1, 2} +} + +ref_element if { + {input.x} +} + +scalar_element if { + {false} +} + +set_element if { + {{1}} +} + +array_element if { + {[1]} +} + +parenthesized_scalar_element if { + {false} +} + +parenthesized_parenthesized if { + {1, 2} +} + +fn(x) if { + {x, 2} +} + +collection contains 1 if { + {1, 2} +} + +a.b.c if { + {1, 2} +} + +with_else := 1 if { + {1, 2} +} + +else := 2 + +already_multiline if { + {1, 2} +} + +ref_term if input.x + +object_term if {"a": 1} + +set_comprehension if {y | y := input.x} + +set_comparison if {1, 2} == {1, 2} diff --git a/v1/format/testfiles/v1/test_not_future_import.rego b/v1/format/testfiles/v1/test_not_future_import.rego index ffb173c6a9..17eb09c4c4 100644 --- a/v1/format/testfiles/v1/test_not_future_import.rego +++ b/v1/format/testfiles/v1/test_not_future_import.rego @@ -36,4 +36,6 @@ b if { } with input.y as 5 with input.y2 as 6 with input.y3 as 7 with input.y4 as 8 -} \ No newline at end of file +} + +c if { not {input.x} } diff --git a/v1/format/testfiles/v1/test_not_future_import.rego.formatted b/v1/format/testfiles/v1/test_not_future_import.rego.formatted index 2406b68386..19f9c56ebd 100644 --- a/v1/format/testfiles/v1/test_not_future_import.rego.formatted +++ b/v1/format/testfiles/v1/test_not_future_import.rego.formatted @@ -37,3 +37,5 @@ b if { with input.y3 as 7 with input.y4 as 8 } + +c if not { input.x }