diff --git a/src/interpreter.rs b/src/interpreter.rs index 664b773..f012346 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -1400,7 +1400,7 @@ impl<'source> Interpreter<'source> { } } - fn check_default_value(&self, expr: &'source Expr<'source>) -> Result<()> { + fn check_default_value(expr: &'source Expr<'source>) -> Result<()> { use Expr::*; let (kind, span) = match expr { // Scalars are supported @@ -1409,12 +1409,13 @@ impl<'source> Interpreter<'source> { // Uminus of number is treated as a single expression, UnaryExpr { expr, .. } if matches!(expr.as_ref(), Number(_)) => return Ok(()), - Var(span) => ("var", span), + // Comprehensions are supported since they won't evaluate to undefined. + ArrayCompr { .. } | SetCompr { .. } | ObjectCompr { .. } => return Ok(()), // Check each item in array/set. Array { items, .. } | Set { items, .. } => { for item in items { - self.check_default_value(item)?; + Self::check_default_value(item)?; } return Ok(()); } @@ -1422,32 +1423,14 @@ impl<'source> Interpreter<'source> { // Check each field in object Object { fields, .. } => { for (_, key, value) in fields { - self.check_default_value(key)?; - self.check_default_value(value)?; - } - return Ok(()); - } - - // Check each statement in comprehensions - ArrayCompr { term, query, .. } | SetCompr { term, query, .. } => { - self.check_default_value(term)?; - for stmt in &query.stmts { - self.check_default_value_in_stmt(stmt)?; - } - return Ok(()); - } - - ObjectCompr { - key, value, query, .. - } => { - self.check_default_value(key)?; - self.check_default_value(value)?; - for stmt in &query.stmts { - self.check_default_value_in_stmt(stmt)?; + Self::check_default_value(key)?; + Self::check_default_value(value)?; } return Ok(()); } + // The following may evaluate to undefined. + Var(span) => ("var", span), Call { span, .. } => ("call", span), UnaryExpr { span, .. } => ("unaryexpr", span), RefDot { span, .. } => ("ref", span), @@ -1462,40 +1445,11 @@ impl<'source> Interpreter<'source> { Err(span.error(format!("invalid `{kind}` in default value").as_str())) } - fn check_default_value_in_stmt(&self, stmt: &'source LiteralStmt<'source>) -> Result<()> { - for m in &stmt.with_mods { - self.check_default_value(&m.refr)?; - self.check_default_value(&m.r#as)?; - } - - match &stmt.literal { - Literal::SomeVars { span, .. } => { - Err(span.error("invalid `some vars` in default value")) - } - Literal::SomeIn { - key, - value, - collection, - .. - } => { - self.check_default_value(key)?; - if let Some(value) = &value { - self.check_default_value(value)?; - } - self.check_default_value(collection) - } - Literal::Expr { expr, .. } | Literal::NotExpr { expr, .. } => { - self.check_default_value(expr) - } - Literal::Every { span, .. } => Err(span.error("invalid `every` in default value")), - } - } - fn check_default_rules(&self) -> Result<()> { for module in &self.modules { for rule in &module.policy { if let Rule::Default { value, .. } = rule { - self.check_default_value(value)?; + Self::check_default_value(value)?; } } } @@ -1523,7 +1477,7 @@ impl<'source> Interpreter<'source> { Parser::get_path_ref_components_into(refr, &mut path)?; let paths: Vec<&str> = path.iter().map(|s| s.text()).collect(); - self.check_default_value(value)?; + Self::check_default_value(value)?; let value = self.eval_expr(value)?; // Assume at this point that all the non-default rules have been evaluated. diff --git a/tests/interpreter/cases/default/basic.yaml b/tests/interpreter/cases/default/basic.yaml index 7899e77..641b87a 100644 --- a/tests/interpreter/cases/default/basic.yaml +++ b/tests/interpreter/cases/default/basic.yaml @@ -131,68 +131,80 @@ cases: query: data error: invalid `var` in default value - - note: invalid-var-in-array-compr-output + - note: valid-var-in-array-compr-output data: {} modules: - | package t + x = 5 default a = [ x | 1 ] - query: data - error: invalid `var` in default value + query: data.t.a + want_result: [5] - - note: invalid-var-in-array-compr-query + - note: valid-var-in-array-compr-query data: {} modules: - | package t + x = 5 default a = [ 1 | true; x ] - query: data - error: invalid `var` in default value + query: data.t.a + want_result: [1] - - note: invalid-var-in-set-compr-output + - note: valid-var-in-set-compr-output data: {} modules: - | package t + x = 5 default a = { x | 1 } - query: data - error: invalid `var` in default value + query: data.t.a + want_result: + set!: [5] - - note: invalid-var-in-array-compr-query + - note: valid-var-in-array-compr-query data: {} modules: - | package t + x = 5 default a = { 1 | true; x } - query: data - error: invalid `var` in default value + query: data.t.a + want_result: + set!: [1] - - note: invalid-var-in-object-compr-key + - note: valid-var-in-object-compr-key data: {} modules: - | package t + x = "a" default a = { x : 5 | true } - query: data - error: invalid `var` in default value + query: data.t.a + want_result: + a: 5 - - note: invalid-var-in-object-compr-value + - note: valid-var-in-object-compr-value data: {} modules: - | package t + x = 5 default a = { "x" : x | true } - query: data - error: invalid `var` in default value + query: data.t.a + want_result: + x: 5 - - note: invalid-var-in-object-compr-query + - note: valid-var-in-object-compr-query data: {} modules: - | package t + x = 5 default a = { "a" : 5 | x } - query: data - error: invalid `var` in default value + query: data.t.a + want_result: + a: 5 - note: invalid-call data: {} @@ -278,54 +290,48 @@ cases: query: data error: unexpected keyword `in` - - note: invalid-some-vars + - note: valid-some-vars data: {} modules: - | package t - default a = [5 | some a] - query: data - error: invalid `some vars` in default value + default a = [5 | some a; a = 5] + query: data.t.a + want_result: [5] - - note: invalid-some-vars - data: {} - modules: - - | - package t - default a = [5 | some a] - query: data - error: invalid `some vars` in default value - - - note: invalid-every + - note: valid-every data: {} modules: - | package t import future.keywords default a = [5 | every x in [1,2,3] { true }] - query: data - error: invalid `every` in default value + query: data.t.a + want_result: [5] + skip: true - - note: invalid-var-in-some-in-key + - note: valid-var-in-some-in-key data: {} modules: - | package t import future.keywords default a = [5 | some x in {5}] - query: data - error: invalid `var` in default value + query: data.t.a + skip: true + want_result: [5] - - note: invalid-var-in-some-in-value + - note: valid-var-in-some-in-value data: {} modules: - | package t import future.keywords default a = [5 | some 5, x in {5}] - query: data - error: invalid `var` in default value - + query: data.t.a + want_result: [5] + skip: true + - note: invalid-var-in-some-in-collection data: {} modules: @@ -333,26 +339,27 @@ cases: package t import future.keywords default a = [5 | some 5, 5 in x] - query: data - error: invalid `var` in default value + query: data.t.a + want_result: [] + skip: true - - note: invalid-var-in-not-expr + - note: valid-var-in-not-expr data: {} modules: - | package t import future.keywords default a = [5 | not x] - query: data - error: invalid `var` in default value + query: data.t.a + want_result: [] - - note: invalid-var-in-expr + - note: valid-var-in-expr data: {} modules: - | package t import future.keywords + x = true default a = [5 | x] - query: data - error: invalid `var` in default value - + query: data.t.a + want_result: [5]