diff --git a/internal/schema/parser.go b/internal/schema/parser.go index 513a49808..3795d15b1 100644 --- a/internal/schema/parser.go +++ b/internal/schema/parser.go @@ -321,6 +321,12 @@ func (p *parser) parsePermissionExpressions(finalToken itemType, depth int) *ast case item.Typ == itemOperatorAnd, item.Typ == itemOperatorOr: p.next() // consume operator + + // A nil root means that we saw a binary expression before the first + // expression. + if root == nil { + return nil + } newRoot := &ast.SubjectSetRewrite{ Operation: setOperation(item.Typ), Children: []ast.Child{root}, @@ -491,7 +497,7 @@ func simplifyExpression(root *ast.SubjectSetRewrite) *ast.SubjectSetRewrite { } var newChildren []ast.Child for _, child := range root.Children { - if ch, ok := child.(*ast.SubjectSetRewrite); ok && ch.Operation == root.Operation { + if ch, ok := child.(*ast.SubjectSetRewrite); ok && ch != nil && ch.Operation == root.Operation { // merge child and root simplifyExpression(ch) newChildren = append(newChildren, ch.Children...) diff --git a/internal/schema/parser_test.go b/internal/schema/parser_test.go index 56c0b6645..40ee9fe3f 100644 --- a/internal/schema/parser_test.go +++ b/internal/schema/parser_test.go @@ -47,6 +47,13 @@ var parserErrorTestCases = []struct{ name, input string }{ this.related.siblings.traverse(s => s.permits.edit(ctx)), } } +`}, + {"parser error", ` +class Resource implements Namespace { + permits = { + update: (ctx: Context) => || + this.related.annotators.traverse((role) => role.related.member.includes(ctx.subject)) || + this.related.supervisors.traverse((role) => role.related.member.includes(ctx.subject)), `}, }