bump dependencies
Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
+1
-1
@@ -520,7 +520,7 @@ func attachRuleAnnotations(mod *Module) {
|
||||
var j int
|
||||
var found bool
|
||||
for i, a := range cpy {
|
||||
if rule.Ref().Equal(a.GetTargetPath()) {
|
||||
if rule.Ref().GroundPrefix().Equal(a.GetTargetPath()) {
|
||||
if a.Scope == annotationScopeDocument {
|
||||
rule.Annotations = append(rule.Annotations, a)
|
||||
} else if a.Scope == annotationScopeRule && rule.Loc().Row > a.Location.Row {
|
||||
|
||||
+36
-11
@@ -33,15 +33,18 @@ type typeChecker struct {
|
||||
allowNet []string
|
||||
input types.Type
|
||||
allowUndefinedFuncs bool
|
||||
schemaTypes map[string]types.Type
|
||||
}
|
||||
|
||||
// newTypeChecker returns a new typeChecker object that has no errors.
|
||||
func newTypeChecker() *typeChecker {
|
||||
tc := &typeChecker{}
|
||||
tc.exprCheckers = map[string]exprChecker{
|
||||
"eq": tc.checkExprEq,
|
||||
return &typeChecker{
|
||||
builtins: make(map[string]*Builtin),
|
||||
schemaTypes: make(map[string]types.Type),
|
||||
exprCheckers: map[string]exprChecker{
|
||||
"eq": checkExprEq,
|
||||
},
|
||||
}
|
||||
return tc
|
||||
}
|
||||
|
||||
func (tc *typeChecker) newEnv(exist *TypeEnv) *TypeEnv {
|
||||
@@ -196,20 +199,42 @@ func (tc *typeChecker) checkClosures(env *TypeEnv, expr *Expr) Errors {
|
||||
return result
|
||||
}
|
||||
|
||||
func (tc *typeChecker) getSchemaType(schemaAnnot *SchemaAnnotation, rule *Rule) (types.Type, *Error) {
|
||||
if refType, exists := tc.schemaTypes[schemaAnnot.Schema.String()]; exists {
|
||||
return refType, nil
|
||||
}
|
||||
|
||||
refType, err := processAnnotation(tc.ss, schemaAnnot, rule, tc.allowNet)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if refType == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
tc.schemaTypes[schemaAnnot.Schema.String()] = refType
|
||||
return refType, nil
|
||||
|
||||
}
|
||||
|
||||
func (tc *typeChecker) checkRule(env *TypeEnv, as *AnnotationSet, rule *Rule) {
|
||||
|
||||
env = env.wrap()
|
||||
|
||||
schemaAnnots := getRuleAnnotation(as, rule)
|
||||
for _, schemaAnnot := range schemaAnnots {
|
||||
ref, refType, err := processAnnotation(tc.ss, schemaAnnot, rule, tc.allowNet)
|
||||
refType, err := tc.getSchemaType(schemaAnnot, rule)
|
||||
if err != nil {
|
||||
tc.err([]*Error{err})
|
||||
continue
|
||||
}
|
||||
|
||||
ref := schemaAnnot.Path
|
||||
if ref == nil && refType == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
prefixRef, t := getPrefix(env, ref)
|
||||
if t == nil || len(prefixRef) == len(ref) {
|
||||
env.tree.Put(ref, refType)
|
||||
@@ -404,7 +429,7 @@ func (tc *typeChecker) checkExprBuiltin(env *TypeEnv, expr *Expr) *Error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tc *typeChecker) checkExprEq(env *TypeEnv, expr *Expr) *Error {
|
||||
func checkExprEq(env *TypeEnv, expr *Expr) *Error {
|
||||
|
||||
pre := getArgTypes(env, expr.Operands())
|
||||
exp := Equality.Decl.FuncArgs()
|
||||
@@ -1266,17 +1291,17 @@ func getRuleAnnotation(as *AnnotationSet, rule *Rule) (result []*SchemaAnnotatio
|
||||
return result
|
||||
}
|
||||
|
||||
func processAnnotation(ss *SchemaSet, annot *SchemaAnnotation, rule *Rule, allowNet []string) (Ref, types.Type, *Error) {
|
||||
func processAnnotation(ss *SchemaSet, annot *SchemaAnnotation, rule *Rule, allowNet []string) (types.Type, *Error) {
|
||||
|
||||
var schema interface{}
|
||||
|
||||
if annot.Schema != nil {
|
||||
if ss == nil {
|
||||
return nil, nil, nil
|
||||
return nil, nil
|
||||
}
|
||||
schema = ss.Get(annot.Schema)
|
||||
if schema == nil {
|
||||
return nil, nil, NewError(TypeErr, rule.Location, "undefined schema: %v", annot.Schema)
|
||||
return nil, NewError(TypeErr, rule.Location, "undefined schema: %v", annot.Schema)
|
||||
}
|
||||
} else if annot.Definition != nil {
|
||||
schema = *annot.Definition
|
||||
@@ -1284,10 +1309,10 @@ func processAnnotation(ss *SchemaSet, annot *SchemaAnnotation, rule *Rule, allow
|
||||
|
||||
tpe, err := loadSchema(schema, allowNet)
|
||||
if err != nil {
|
||||
return nil, nil, NewError(TypeErr, rule.Location, err.Error())
|
||||
return nil, NewError(TypeErr, rule.Location, err.Error())
|
||||
}
|
||||
|
||||
return annot.Path, tpe, nil
|
||||
return tpe, nil
|
||||
}
|
||||
|
||||
func errAnnotationRedeclared(a *Annotations, other *Location) *Error {
|
||||
|
||||
+27
-10
@@ -5332,8 +5332,7 @@ func rewriteDeclaredVarsInExpr(g *localVarGenerator, stack *localDeclaredVars, e
|
||||
case *Term:
|
||||
stop, errs = rewriteDeclaredVarsInTerm(g, stack, x, errs, strict)
|
||||
case *With:
|
||||
errs = rewriteDeclaredVarsInTermRecursive(g, stack, x.Value, errs, strict)
|
||||
stop = true
|
||||
stop, errs = true, rewriteDeclaredVarsInWithRecursive(g, stack, x, errs, strict)
|
||||
}
|
||||
return stop
|
||||
})
|
||||
@@ -5466,20 +5465,38 @@ func rewriteDeclaredVarsInTerm(g *localVarGenerator, stack *localDeclaredVars, t
|
||||
}
|
||||
|
||||
func rewriteDeclaredVarsInTermRecursive(g *localVarGenerator, stack *localDeclaredVars, term *Term, errs Errors, strict bool) Errors {
|
||||
WalkNodes(term, func(n Node) bool {
|
||||
WalkTerms(term, func(t *Term) bool {
|
||||
var stop bool
|
||||
switch n := n.(type) {
|
||||
case *With:
|
||||
errs = rewriteDeclaredVarsInTermRecursive(g, stack, n.Value, errs, strict)
|
||||
stop = true
|
||||
case *Term:
|
||||
stop, errs = rewriteDeclaredVarsInTerm(g, stack, n, errs, strict)
|
||||
}
|
||||
stop, errs = rewriteDeclaredVarsInTerm(g, stack, t, errs, strict)
|
||||
return stop
|
||||
})
|
||||
return errs
|
||||
}
|
||||
|
||||
func rewriteDeclaredVarsInWithRecursive(g *localVarGenerator, stack *localDeclaredVars, w *With, errs Errors, strict bool) Errors {
|
||||
// NOTE(sr): `with input as` and `with input.a.b.c as` are deliberately skipped here: `input` could
|
||||
// have been shadowed by a local variable/argument but should NOT be replaced in the `with` target.
|
||||
//
|
||||
// We cannot drop `input` from the stack since it's conceivable to do `with input[input] as` where
|
||||
// the second input is meant to be the local var. It's a terrible idea, but when you're shadowing
|
||||
// `input` those might be your thing.
|
||||
errs = rewriteDeclaredVarsInTermRecursive(g, stack, w.Target, errs, strict)
|
||||
if sdwInput, ok := stack.Declared(InputRootDocument.Value.(Var)); ok { // Was "input" shadowed...
|
||||
switch value := w.Target.Value.(type) {
|
||||
case Var:
|
||||
if sdwInput.Equal(value) { // ...and replaced? If so, fix it
|
||||
w.Target.Value = InputRootRef
|
||||
}
|
||||
case Ref:
|
||||
if sdwInput.Equal(value[0].Value.(Var)) {
|
||||
w.Target.Value.(Ref)[0].Value = InputRootDocument.Value
|
||||
}
|
||||
}
|
||||
}
|
||||
// No special handling of the `with` value
|
||||
return rewriteDeclaredVarsInTermRecursive(g, stack, w.Value, errs, strict)
|
||||
}
|
||||
|
||||
func rewriteDeclaredVarsInArrayComprehension(g *localVarGenerator, stack *localDeclaredVars, v *ArrayComprehension, errs Errors, strict bool) Errors {
|
||||
used := NewVarSet()
|
||||
used.Update(v.Term.Vars())
|
||||
|
||||
+38
-6
@@ -30,6 +30,8 @@ var RegoV1CompatibleRef = Ref{VarTerm("rego"), StringTerm("v1")}
|
||||
// RegoVersion defines the Rego syntax requirements for a module.
|
||||
type RegoVersion int
|
||||
|
||||
const DefaultRegoVersion = RegoVersion(0)
|
||||
|
||||
const (
|
||||
// RegoV0 is the default, original Rego syntax.
|
||||
RegoV0 RegoVersion = iota
|
||||
@@ -317,6 +319,31 @@ func (p *Parser) Parse() ([]Statement, []*Comment, Errors) {
|
||||
for k, v := range futureKeywords {
|
||||
allowedFutureKeywords[k] = v
|
||||
}
|
||||
|
||||
// For sake of error reporting, we still need to check that keywords in capabilities are known,
|
||||
for _, kw := range p.po.Capabilities.FutureKeywords {
|
||||
if _, ok := futureKeywords[kw]; !ok {
|
||||
return nil, nil, Errors{
|
||||
&Error{
|
||||
Code: ParseErr,
|
||||
Message: fmt.Sprintf("illegal capabilities: unknown keyword: %v", kw),
|
||||
Location: nil,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
// and that explicitly requested future keywords are known.
|
||||
for _, kw := range p.po.FutureKeywords {
|
||||
if _, ok := allowedFutureKeywords[kw]; !ok {
|
||||
return nil, nil, Errors{
|
||||
&Error{
|
||||
Code: ParseErr,
|
||||
Message: fmt.Sprintf("unknown future keyword: %v", kw),
|
||||
Location: nil,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for _, kw := range p.po.Capabilities.FutureKeywords {
|
||||
var ok bool
|
||||
@@ -686,6 +713,10 @@ func (p *Parser) parseRules() []*Rule {
|
||||
|
||||
// p[x] if ... becomes a single-value rule p[x]
|
||||
if hasIf && !usesContains && len(rule.Head.Ref()) == 2 {
|
||||
if !rule.Head.Ref()[1].IsGround() && len(rule.Head.Args) == 0 {
|
||||
rule.Head.Key = rule.Head.Ref()[1]
|
||||
}
|
||||
|
||||
if rule.Head.Value == nil {
|
||||
rule.Head.generatedValue = true
|
||||
rule.Head.Value = BooleanTerm(true).SetLocation(rule.Head.Location)
|
||||
@@ -2665,15 +2696,16 @@ func (p *Parser) regoV1Import(imp *Import) {
|
||||
return
|
||||
}
|
||||
|
||||
if p.po.RegoVersion == RegoV1 {
|
||||
// We're parsing for Rego v1, where the 'rego.v1' import is a no-op.
|
||||
path := imp.Path.Value.(Ref)
|
||||
|
||||
// v1 is only valid option
|
||||
if len(path) == 1 || !path[1].Equal(RegoV1CompatibleRef[1]) || len(path) > 2 {
|
||||
p.errorf(imp.Path.Location, "invalid import `%s`, must be `%s`", path, RegoV1CompatibleRef)
|
||||
return
|
||||
}
|
||||
|
||||
path := imp.Path.Value.(Ref)
|
||||
|
||||
if len(path) == 1 || !path[1].Equal(RegoV1CompatibleRef[1]) || len(path) > 2 {
|
||||
p.errorf(imp.Path.Location, "invalid import `%s`, must be `%s`", path, RegoV1CompatibleRef)
|
||||
if p.po.RegoVersion == RegoV1 {
|
||||
// We're parsing for Rego v1, where the 'rego.v1' import is a no-op.
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
+35
-5
@@ -103,6 +103,14 @@ func MustParseStatement(input string) Statement {
|
||||
return parsed
|
||||
}
|
||||
|
||||
func MustParseStatementWithOpts(input string, popts ParserOptions) Statement {
|
||||
parsed, err := ParseStatementWithOpts(input, popts)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
|
||||
// MustParseRef returns a parsed reference.
|
||||
// If an error occurs during parsing, panic.
|
||||
func MustParseRef(input string) Ref {
|
||||
@@ -123,6 +131,16 @@ func MustParseRule(input string) *Rule {
|
||||
return parsed
|
||||
}
|
||||
|
||||
// MustParseRuleWithOpts returns a parsed rule.
|
||||
// If an error occurs during parsing, panic.
|
||||
func MustParseRuleWithOpts(input string, opts ParserOptions) *Rule {
|
||||
parsed, err := ParseRuleWithOpts(input, opts)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return parsed
|
||||
}
|
||||
|
||||
// MustParseTerm returns a parsed term.
|
||||
// If an error occurs during parsing, panic.
|
||||
func MustParseTerm(input string) *Term {
|
||||
@@ -269,11 +287,12 @@ func ParseCompleteDocRuleFromEqExpr(module *Module, lhs, rhs *Term) (*Rule, erro
|
||||
setJSONOptions(body, &rhs.jsonOptions)
|
||||
|
||||
return &Rule{
|
||||
Location: lhs.Location,
|
||||
Head: head,
|
||||
Body: body,
|
||||
Module: module,
|
||||
jsonOptions: lhs.jsonOptions,
|
||||
Location: lhs.Location,
|
||||
Head: head,
|
||||
Body: body,
|
||||
Module: module,
|
||||
jsonOptions: lhs.jsonOptions,
|
||||
generatedBody: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -608,6 +627,17 @@ func ParseStatement(input string) (Statement, error) {
|
||||
return stmts[0], nil
|
||||
}
|
||||
|
||||
func ParseStatementWithOpts(input string, popts ParserOptions) (Statement, error) {
|
||||
stmts, _, err := ParseStatementsWithOpts("", input, popts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(stmts) != 1 {
|
||||
return nil, fmt.Errorf("expected exactly one statement")
|
||||
}
|
||||
return stmts[0], nil
|
||||
}
|
||||
|
||||
// ParseStatements is deprecated. Use ParseStatementWithOpts instead.
|
||||
func ParseStatements(filename, input string) ([]Statement, []*Comment, error) {
|
||||
return ParseStatementsWithOpts(filename, input, ParserOptions{})
|
||||
|
||||
+54
-11
@@ -100,7 +100,7 @@ var Wildcard = &Term{Value: Var("_")}
|
||||
var WildcardPrefix = "$"
|
||||
|
||||
// Keywords contains strings that map to language keywords.
|
||||
var Keywords = KeywordsV0
|
||||
var Keywords = KeywordsForRegoVersion(DefaultRegoVersion)
|
||||
|
||||
var KeywordsV0 = [...]string{
|
||||
"not",
|
||||
@@ -134,9 +134,23 @@ var KeywordsV1 = [...]string{
|
||||
"every",
|
||||
}
|
||||
|
||||
func KeywordsForRegoVersion(v RegoVersion) []string {
|
||||
switch v {
|
||||
case RegoV0:
|
||||
return KeywordsV0[:]
|
||||
case RegoV1, RegoV0CompatV1:
|
||||
return KeywordsV1[:]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// IsKeyword returns true if s is a language keyword.
|
||||
func IsKeyword(s string) bool {
|
||||
for _, x := range Keywords {
|
||||
return IsInKeywords(s, Keywords)
|
||||
}
|
||||
|
||||
func IsInKeywords(s string, keywords []string) bool {
|
||||
for _, x := range keywords {
|
||||
if x == s {
|
||||
return true
|
||||
}
|
||||
@@ -405,7 +419,7 @@ func (mod *Module) String() string {
|
||||
buf = append(buf, "")
|
||||
for _, rule := range mod.Rules {
|
||||
buf = appendAnnotationStrings(buf, rule)
|
||||
buf = append(buf, rule.String())
|
||||
buf = append(buf, rule.stringWithOpts(toStringOpts{regoVersion: mod.regoVersion}))
|
||||
}
|
||||
}
|
||||
return strings.Join(buf, "\n")
|
||||
@@ -770,18 +784,30 @@ func (rule *Rule) Ref() Ref {
|
||||
}
|
||||
|
||||
func (rule *Rule) String() string {
|
||||
return rule.stringWithOpts(toStringOpts{})
|
||||
}
|
||||
|
||||
type toStringOpts struct {
|
||||
regoVersion RegoVersion
|
||||
}
|
||||
|
||||
func (rule *Rule) stringWithOpts(opts toStringOpts) string {
|
||||
buf := []string{}
|
||||
if rule.Default {
|
||||
buf = append(buf, "default")
|
||||
}
|
||||
buf = append(buf, rule.Head.String())
|
||||
buf = append(buf, rule.Head.stringWithOpts(opts))
|
||||
if !rule.Default {
|
||||
switch opts.regoVersion {
|
||||
case RegoV1, RegoV0CompatV1:
|
||||
buf = append(buf, "if")
|
||||
}
|
||||
buf = append(buf, "{")
|
||||
buf = append(buf, rule.Body.String())
|
||||
buf = append(buf, "}")
|
||||
}
|
||||
if rule.Else != nil {
|
||||
buf = append(buf, rule.Else.elseString())
|
||||
buf = append(buf, rule.Else.elseString(opts))
|
||||
}
|
||||
return strings.Join(buf, " ")
|
||||
}
|
||||
@@ -824,7 +850,7 @@ func (rule *Rule) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(data)
|
||||
}
|
||||
|
||||
func (rule *Rule) elseString() string {
|
||||
func (rule *Rule) elseString(opts toStringOpts) string {
|
||||
var buf []string
|
||||
|
||||
buf = append(buf, "else")
|
||||
@@ -835,12 +861,17 @@ func (rule *Rule) elseString() string {
|
||||
buf = append(buf, value.String())
|
||||
}
|
||||
|
||||
switch opts.regoVersion {
|
||||
case RegoV1, RegoV0CompatV1:
|
||||
buf = append(buf, "if")
|
||||
}
|
||||
|
||||
buf = append(buf, "{")
|
||||
buf = append(buf, rule.Body.String())
|
||||
buf = append(buf, "}")
|
||||
|
||||
if rule.Else != nil {
|
||||
buf = append(buf, rule.Else.elseString())
|
||||
buf = append(buf, rule.Else.elseString(opts))
|
||||
}
|
||||
|
||||
return strings.Join(buf, " ")
|
||||
@@ -1000,16 +1031,28 @@ func (head *Head) Equal(other *Head) bool {
|
||||
}
|
||||
|
||||
func (head *Head) String() string {
|
||||
return head.stringWithOpts(toStringOpts{})
|
||||
}
|
||||
|
||||
func (head *Head) stringWithOpts(opts toStringOpts) string {
|
||||
buf := strings.Builder{}
|
||||
buf.WriteString(head.Ref().String())
|
||||
containsAdded := false
|
||||
|
||||
switch {
|
||||
case len(head.Args) != 0:
|
||||
buf.WriteString(head.Args.String())
|
||||
case len(head.Reference) == 1 && head.Key != nil:
|
||||
buf.WriteRune('[')
|
||||
buf.WriteString(head.Key.String())
|
||||
buf.WriteRune(']')
|
||||
switch opts.regoVersion {
|
||||
case RegoV0:
|
||||
buf.WriteRune('[')
|
||||
buf.WriteString(head.Key.String())
|
||||
buf.WriteRune(']')
|
||||
default:
|
||||
containsAdded = true
|
||||
buf.WriteString(" contains ")
|
||||
buf.WriteString(head.Key.String())
|
||||
}
|
||||
}
|
||||
if head.Value != nil {
|
||||
if head.Assign {
|
||||
@@ -1018,7 +1061,7 @@ func (head *Head) String() string {
|
||||
buf.WriteString(" = ")
|
||||
}
|
||||
buf.WriteString(head.Value.String())
|
||||
} else if head.Name == "" && head.Key != nil {
|
||||
} else if !containsAdded && head.Name == "" && head.Key != nil {
|
||||
buf.WriteString(" contains ")
|
||||
buf.WriteString(head.Key.String())
|
||||
}
|
||||
|
||||
+6
-3
@@ -50,9 +50,12 @@ func checkRootDocumentOverrides(node interface{}) Errors {
|
||||
|
||||
WalkExprs(node, func(expr *Expr) bool {
|
||||
if expr.IsAssignment() {
|
||||
name := expr.Operand(0).String()
|
||||
if RootDocumentRefs.Contains(RefTerm(VarTerm(name))) {
|
||||
errors = append(errors, NewError(CompileErr, expr.Location, "variables must not shadow %v (use a different variable name)", name))
|
||||
// assign() can be called directly, so we need to assert its given first operand exists before checking its name.
|
||||
if nameOp := expr.Operand(0); nameOp != nil {
|
||||
name := nameOp.String()
|
||||
if RootDocumentRefs.Contains(RefTerm(VarTerm(name))) {
|
||||
errors = append(errors, NewError(CompileErr, expr.Location, "variables must not shadow %v (use a different variable name)", name))
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
|
||||
Reference in New Issue
Block a user