diff --git a/Cargo.toml b/Cargo.toml index cfb6137..8f09cf5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,3 +24,7 @@ walkdir = "2.3.2" [build-dependencies] anyhow = "1.0.66" + +[profile.release] +debug = true + diff --git a/src/ast.rs b/src/ast.rs index 9023b2f..e7f2f49 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -3,13 +3,13 @@ use crate::lexer::*; -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum BinOp { And, Or, } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum ArithOp { Add, Sub, @@ -18,7 +18,7 @@ pub enum ArithOp { Mod, } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum BoolOp { Lt, Le, @@ -28,13 +28,13 @@ pub enum BoolOp { Ne, } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum AssignOp { Eq, ColEq, } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Debug, Clone)] pub enum Expr<'source> { // Simple items that only have a span as content. String(Span<'source>), @@ -166,7 +166,7 @@ impl<'source> Expr<'source> { } } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Debug, Clone)] pub enum Literal<'source> { SomeVars { span: Span<'source>, @@ -195,41 +195,41 @@ pub enum Literal<'source> { }, } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Debug, Clone)] pub struct WithModifier<'source> { pub span: Span<'source>, pub refr: Expr<'source>, pub r#as: Expr<'source>, } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Debug, Clone)] pub struct LiteralStmt<'source> { pub span: Span<'source>, pub literal: Literal<'source>, pub with_mods: Vec>, } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Debug, Clone)] pub struct Query<'source> { pub span: Span<'source>, pub stmts: Vec>, } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Debug, Clone)] pub struct RuleAssign<'source> { pub span: Span<'source>, pub op: AssignOp, pub value: Expr<'source>, } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Debug, Clone)] pub struct RuleBody<'source> { pub span: Span<'source>, pub assign: Option>, pub query: Query<'source>, } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Debug, Clone)] pub enum RuleHead<'source> { Compr { span: Span<'source>, @@ -249,7 +249,7 @@ pub enum RuleHead<'source> { }, } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Debug, Clone)] pub enum Rule<'source> { Spec { span: Span<'source>, @@ -264,22 +264,58 @@ pub enum Rule<'source> { }, } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Debug, Clone)] pub struct Package<'source> { pub span: Span<'source>, pub refr: Expr<'source>, } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Debug, Clone)] pub struct Import<'source> { pub span: Span<'source>, pub refr: Expr<'source>, pub r#as: Option>, } -#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Debug, Clone)] pub struct Module<'source> { pub package: Package<'source>, pub imports: Vec>, pub policy: Vec>, } + +#[derive(Debug, Clone)] +pub struct Ref<'a, T> { + r: &'a T, +} + +impl<'a, T> Ref<'a, T> { + pub fn make(r: &'a T) -> Self { + Self { r } + } + + pub fn inner(&self) -> &'a T { + self.r + } +} + +impl<'a, T> Eq for Ref<'a, T> {} + +impl<'a, T> PartialEq for Ref<'a, T> { + fn eq(&self, other: &Self) -> bool { + std::ptr::eq(self.r, other.r) + } +} + +impl<'a, T> PartialOrd for Ref<'a, T> { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +impl<'a, T> Ord for Ref<'a, T> { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + //std::ptr::from_ref(self.r).partial_cmp(std::ptr::from_ref(other.r)) + (self.r as *const T).cmp(&(other.r as *const T)) + } +} diff --git a/src/interpreter.rs b/src/interpreter.rs index e04d8d4..77958c2 100644 --- a/src/interpreter.rs +++ b/src/interpreter.rs @@ -29,12 +29,12 @@ pub struct Interpreter<'source> { with_document: Value, scopes: Vec, // TODO: handle recursive calls where same expr could have different values. - loop_var_values: BTreeMap<&'source Expr<'source>, Value>, + loop_var_values: BTreeMap>, Value>, contexts: Vec>, functions: FunctionTable<'source>, rules: HashMap>>, default_rules: HashMap, Option)>>, - processed: BTreeSet<&'source Rule<'source>>, + processed: BTreeSet>>, active_rules: Vec<&'source Rule<'source>>, builtins_cache: BTreeMap<(&'static str, Vec), Value>, no_rules_lookup: bool, @@ -205,7 +205,7 @@ impl<'source> Interpreter<'source> { // Collect a chaing of '.field' or '["field"]' let mut path = vec![]; loop { - if let Some(v) = self.loop_var_values.get(expr) { + if let Some(v) = self.loop_var_values.get(&Ref::make(expr)) { path.reverse(); return Ok(Self::get_value_chained(v.clone(), &path[..])); } @@ -444,7 +444,7 @@ impl<'source> Interpreter<'source> { // TODO: Check this // Allow variable overwritten inside a loop if !matches!(var, Value::Undefined) - && self.loop_var_values.get(rhs).is_none() + && self.loop_var_values.get(&Ref::make(rhs)).is_none() { return self.eval_bool_expr(&BoolOp::Eq, lhs, rhs); } @@ -457,7 +457,7 @@ impl<'source> Interpreter<'source> { // TODO: Check this // Allow variable overwritten inside a loop if !matches!(var, Value::Undefined) - && self.loop_var_values.get(lhs).is_none() + && self.loop_var_values.get(&Ref::make(lhs)).is_none() { return self.eval_bool_expr(&BoolOp::Eq, lhs, rhs); } @@ -477,7 +477,8 @@ impl<'source> Interpreter<'source> { // TODO: Check this // Allow variable overwritten inside a loop - if self.lookup_local_var(name).is_some() && self.loop_var_values.get(rhs).is_none() + if self.lookup_local_var(name).is_some() + && self.loop_var_values.get(&Ref::make(rhs)).is_none() { bail!(rhs .span() @@ -573,14 +574,14 @@ impl<'source> Interpreter<'source> { fn lookup_or_eval_expr( &mut self, - cache: &mut BTreeMap<&'source Expr<'source>, Value>, + cache: &mut BTreeMap>, Value>, expr: &'source Expr<'source>, ) -> Result { - match cache.get(expr) { + match cache.get(&Ref::make(expr)) { Some(v) => Ok(v.clone()), _ => { let v = self.eval_expr(expr)?; - cache.insert(expr, v.clone()); + cache.insert(Ref::make(expr), v.clone()); Ok(v) } } @@ -589,8 +590,8 @@ impl<'source> Interpreter<'source> { fn make_bindings_impl( &mut self, is_last: bool, - type_match: &mut BTreeSet<&'source Expr<'source>>, - cache: &mut BTreeMap<&'source Expr<'source>, Value>, + type_match: &mut BTreeSet>>, + cache: &mut BTreeMap>, Value>, expr: &'source Expr<'source>, value: &Value, ) -> Result { @@ -599,7 +600,7 @@ impl<'source> Interpreter<'source> { return Ok(false); } let span = expr.span(); - let raise_error = is_last && type_match.get(expr).is_none(); + let raise_error = is_last && type_match.get(&Ref::make(expr)).is_none(); match (expr, value) { (Expr::Var(ident), _) => { @@ -622,7 +623,7 @@ impl<'source> Interpreter<'source> { } return Ok(false); } - type_match.insert(expr); + type_match.insert(Ref::make(expr)); let mut r = false; for (idx, item) in items.iter().enumerate() { @@ -658,7 +659,7 @@ impl<'source> Interpreter<'source> { field_value, )?; } - type_match.insert(expr); + type_match.insert(Ref::make(expr)); Ok(r) } @@ -677,7 +678,7 @@ impl<'source> Interpreter<'source> { format!("Cannot bind pattern of type `{expr_t}` with value of type `{value_t}`. Value is {value}.").as_str())); } } - type_match.insert(expr); + type_match.insert(Ref::make(expr)); Ok(&expr_value == value) } @@ -687,8 +688,8 @@ impl<'source> Interpreter<'source> { fn make_bindings( &mut self, is_last: bool, - type_match: &mut BTreeSet<&'source Expr<'source>>, - cache: &mut BTreeMap<&'source Expr<'source>, Value>, + type_match: &mut BTreeSet>>, + cache: &mut BTreeMap>, Value>, expr: &'source Expr<'source>, value: &Value, ) -> Result { @@ -702,8 +703,8 @@ impl<'source> Interpreter<'source> { fn make_key_value_bindings( &mut self, is_last: bool, - type_match: &mut BTreeSet<&'source Expr<'source>>, - cache: &mut BTreeMap<&'source Expr<'source>, Value>, + type_match: &mut BTreeSet>>, + cache: &mut BTreeMap>, Value>, exprs: (&'source Option>, &'source Expr<'source>), values: (&Value, &Value), ) -> Result { @@ -1027,11 +1028,12 @@ impl<'source> Interpreter<'source> { match loop_expr_value { Value::Array(items) => { for (idx, v) in items.iter().enumerate() { - self.loop_var_values.insert(loop_expr.expr, v.clone()); + self.loop_var_values + .insert(Ref::make(loop_expr.expr), v.clone()); self.add_variable(loop_expr.index, Value::from_float(idx as Float))?; result = self.eval_stmts_in_loop(stmts, &loops[1..])? || result; - self.loop_var_values.remove(loop_expr.expr); + self.loop_var_values.remove(&Ref::make(loop_expr.expr)); *self.current_scope_mut()? = scope_saved.clone(); if let Some(ctx) = self.contexts.last_mut() { ctx.result = query_result.clone(); @@ -1040,11 +1042,12 @@ impl<'source> Interpreter<'source> { } Value::Set(items) => { for v in items.iter() { - self.loop_var_values.insert(loop_expr.expr, v.clone()); + self.loop_var_values + .insert(Ref::make(loop_expr.expr), v.clone()); // For sets, index is also the value. self.add_variable(loop_expr.index, v.clone())?; result = self.eval_stmts_in_loop(stmts, &loops[1..])? || result; - self.loop_var_values.remove(loop_expr.expr); + self.loop_var_values.remove(&Ref::make(loop_expr.expr)); *self.current_scope_mut()? = scope_saved.clone(); if let Some(ctx) = self.contexts.last_mut() { ctx.result = query_result.clone(); @@ -1053,17 +1056,21 @@ impl<'source> Interpreter<'source> { } Value::Object(obj) => { for (k, v) in obj.iter() { - self.loop_var_values.insert(loop_expr.expr, v.clone()); + self.loop_var_values + .insert(Ref::make(loop_expr.expr), v.clone()); // For objects, index is key. self.add_variable(loop_expr.index, k.clone())?; result = self.eval_stmts_in_loop(stmts, &loops[1..])? || result; - self.loop_var_values.remove(loop_expr.expr); + self.loop_var_values.remove(&Ref::make(loop_expr.expr)); *self.current_scope_mut()? = scope_saved.clone(); if let Some(ctx) = self.contexts.last_mut() { ctx.result = query_result.clone(); } } } + Value::Undefined => { + result = false; + } _ => { return Err(loop_expr.span.source.error( loop_expr.span.line, @@ -1163,19 +1170,22 @@ impl<'source> Interpreter<'source> { match self.eval_expr(loop_expr.value)? { Value::Array(items) => { for v in items.iter() { - self.loop_var_values.insert(loop_expr.expr, v.clone()); + self.loop_var_values + .insert(Ref::make(loop_expr.expr), v.clone()); result = self.eval_output_expr_in_loop(&loops[1..])? || result; } } Value::Set(items) => { for v in items.iter() { - self.loop_var_values.insert(loop_expr.expr, v.clone()); + self.loop_var_values + .insert(Ref::make(loop_expr.expr), v.clone()); result = self.eval_output_expr_in_loop(&loops[1..])? || result; } } Value::Object(obj) => { for (_, v) in obj.iter() { - self.loop_var_values.insert(loop_expr.expr, v.clone()); + self.loop_var_values + .insert(Ref::make(loop_expr.expr), v.clone()); result = self.eval_output_expr_in_loop(&loops[1..])? || result; } } @@ -1187,7 +1197,7 @@ impl<'source> Interpreter<'source> { )); } } - self.loop_var_values.remove(loop_expr.expr); + self.loop_var_values.remove(&Ref::make(loop_expr.expr)); Ok(result) } @@ -1265,7 +1275,7 @@ impl<'source> Interpreter<'source> { self.scopes.push(Scope::new()); let ordered_stmts: Vec<&'source LiteralStmt<'source>> = if let Some(schedule) = &self.schedule { - match schedule.order.get(query) { + match schedule.order.get(&Ref::make(query)) { Some(ord) => ord.iter().map(|i| &query.stmts[*i as usize]).collect(), // TODO _ => bail!(query @@ -1550,7 +1560,8 @@ impl<'source> Interpreter<'source> { for (idx, a) in args.iter().enumerate() { let a = match a { Expr::Var(s) => s.text(), - _ => unimplemented!("destructuring function arguments"), + _ => continue, + // _ => unimplemented!("destructuring function arguments"), }; //TODO: check call in params args_scope.insert(a.to_string(), self.eval_expr(¶ms[idx])?); @@ -1665,7 +1676,7 @@ impl<'source> Interpreter<'source> { fn ensure_rule_evaluated(&mut self, path: String) -> Result<()> { if let Some(rules) = self.rules.get(&path) { for r in rules.clone() { - if !self.processed.contains(r) { + if !self.processed.contains(&Ref::make(r)) { let module = self.get_rule_module(r)?; self.eval_rule(module, r)?; } @@ -1674,7 +1685,7 @@ impl<'source> Interpreter<'source> { // Evaluate the associated default rules after non-default rules if let Some(rules) = self.default_rules.get(&path) { for (r, _) in rules.clone() { - if !self.processed.contains(r) { + if !self.processed.contains(&Ref::make(r)) { let module = self.get_rule_module(r)?; let prev_module = self.set_current_module(Some(module))?; self.eval_default_rule(r)?; @@ -1852,7 +1863,7 @@ impl<'source> Interpreter<'source> { fn get_rule_module(&self, rule: &'source Rule<'source>) -> Result<&'source Module<'source>> { for m in &self.modules { - if m.policy.contains(rule) { + if m.policy.iter().any(|r| Ref::make(r) == Ref::make(rule)) { return Ok(m); } } @@ -2013,46 +2024,6 @@ impl<'source> Interpreter<'source> { Ok(m) } - /* pub fn update_function_table(&mut self) -> Result<()> { - for module in self.modules.clone() { - let prev_module = self.set_current_module(Some(module))?; - let module_path = - Self::get_path_string(&self.current_module()?.package.refr, Some("data"))?; - for rule in &module.policy { - if let Rule::Spec { - head: RuleHead::Func { refr, .. }, - .. - } = rule - { - let mut path = - Parser::get_path_ref_components(&self.current_module()?.package.refr)?; - - Parser::get_path_ref_components_into(refr, &mut path)?; - let path: Vec<&str> = path.iter().map(|s| s.text()).collect(); - - if path.len() > 1 { - let value = - Self::make_or_get_value_mut(&mut self.data, &path[0..path.len() - 1])?; - if value == &Value::Undefined { - *value = Value::new_object(); - } - } - - let full_path = Self::get_path_string(refr, Some(module_path.as_str()))?; - - if let Some(functions) = self.functions.get_mut(&full_path) { - // TODO: check function arity. - functions.push(rule); - } else { - self.functions.insert(full_path, vec![rule]); - } - } - } - self.set_current_module(prev_module)?; - } - Ok(()) - }*/ - fn get_rule_refr(rule: &'source Rule<'source>) -> &'source Expr<'source> { match rule { Rule::Spec { head, .. } => match &head { @@ -2122,7 +2093,7 @@ impl<'source> Interpreter<'source> { fn eval_default_rule(&mut self, rule: &'source Rule<'source>) -> Result<()> { // Skip reprocessing rule. - if self.processed.contains(rule) { + if self.processed.contains(&Ref::make(rule)) { return Ok(()); } @@ -2172,7 +2143,7 @@ impl<'source> Interpreter<'source> { } }; - self.processed.insert(rule); + self.processed.insert(Ref::make(rule)); } Ok(()) @@ -2184,7 +2155,7 @@ impl<'source> Interpreter<'source> { rule: &'source Rule<'source>, ) -> Result<()> { // Skip reprocessing rule - if self.processed.contains(rule) { + if self.processed.contains(&Ref::make(rule)) { return Ok(()); } @@ -2194,7 +2165,13 @@ impl<'source> Interpreter<'source> { } self.active_rules.push(rule); - if self.active_rules.iter().filter(|&r| r == &rule).count() == 2 { + if self + .active_rules + .iter() + .filter(|&r| Ref::make(*r) == Ref::make(rule)) + .count() + == 2 + { let mut msg = String::default(); for r in &self.active_rules { let refr = Self::get_rule_refr(r); @@ -2215,6 +2192,7 @@ impl<'source> Interpreter<'source> { } let prev_module = self.set_current_module(Some(module))?; + match rule { Rule::Spec { span, @@ -2241,7 +2219,7 @@ impl<'source> Interpreter<'source> { Self::merge_value(span, vref, value)?; } - self.processed.insert(rule); + self.processed.insert(Ref::make(rule)); } else if let RuleHead::Func { refr, .. } = rule_head { let mut path = Parser::get_path_ref_components(&self.current_module()?.package.refr)?; @@ -2264,7 +2242,7 @@ impl<'source> Interpreter<'source> { } self.set_current_module(prev_module)?; match self.active_rules.pop() { - Some(r) if r == rule => Ok(()), + Some(r) if Ref::make(r) == Ref::make(rule) => Ok(()), _ => bail!("internal error: current rule not active"), } } @@ -2349,7 +2327,6 @@ impl<'source> Interpreter<'source> { self.eval_rule(module, rule)?; } } - // Defer the evaluation of the default rules to here for module in self.modules.clone() { let prev_module = self.set_current_module(Some(module))?; @@ -2387,7 +2364,7 @@ impl<'source> Interpreter<'source> { // Add schedules for queries. if let Some(self_schedule) = &mut self.schedule { for (k, v) in schedule.order.iter() { - self_schedule.order.insert(k, v.clone()); + self_schedule.order.insert(k.clone(), v.clone()); } } @@ -2414,7 +2391,7 @@ impl<'source> Interpreter<'source> { // Restore schedules. if let Some(self_schedule) = &mut self.schedule { for (k, ord) in schedule.order.iter() { - if k == &query { + if k == &Ref::make(query) { for idx in 0..results.result.len() { let mut ordered_expressions = vec![Value::Undefined; ord.len()]; for (expr_idx, value) in results.result[idx].expressions.iter().enumerate() diff --git a/src/lexer.rs b/src/lexer.rs index 616628f..93a8137 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -8,7 +8,7 @@ use core::str::CharIndices; use crate::value::Value; use anyhow::{anyhow, bail, Result}; -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Clone)] pub struct Source<'source> { pub file: &'source str, pub contents: &'source str, @@ -48,7 +48,7 @@ impl<'source> Source<'source> { } } -#[derive(PartialEq, Eq, PartialOrd, Ord, Clone)] +#[derive(Clone)] pub struct Span<'source> { pub source: &'source Source<'source>, pub line: u16, @@ -98,7 +98,7 @@ pub enum TokenKind { Eof, } -#[derive(Debug, PartialEq, Eq, Clone)] +#[derive(Debug, Clone)] pub struct Token<'source>(pub TokenKind, pub Span<'source>); #[derive(Clone)] diff --git a/src/scheduler.rs b/src/scheduler.rs index 1f16a52..a7e6a91 100644 --- a/src/scheduler.rs +++ b/src/scheduler.rs @@ -377,16 +377,16 @@ fn get_rule_prefix<'a>(expr: &Expr<'a>) -> Result<&'a str> { pub struct Analyzer<'a> { packages: BTreeMap>, - locals: BTreeMap<&'a Query<'a>, Scope<'a>>, + locals: BTreeMap>, Scope<'a>>, scopes: Vec>, - order: BTreeMap<&'a Query<'a>, Vec>, + order: BTreeMap>, Vec>, functions: FunctionTable<'a>, } #[derive(Clone)] pub struct Schedule<'a> { - pub scopes: BTreeMap<&'a Query<'a>, Scope<'a>>, - pub order: BTreeMap<&'a Query<'a>, Vec>, + pub scopes: BTreeMap>, Scope<'a>>, + pub order: BTreeMap>, Vec>, } impl<'a> Default for Analyzer<'a> { @@ -535,11 +535,8 @@ impl<'a> Analyzer<'a> { RuleHead::Set { key, .. } => (key.as_ref(), None, scope), RuleHead::Func { args, assign, .. } => { for a in args.iter() { - match a { - Var(v) => { - scope.locals.insert(v.text()); - } - _ => unimplemented!("non var arguments"), + if let Var(v) = a { + scope.locals.insert(v.text()); } } (None, assign.as_ref().map(|a| &a.value), scope) @@ -672,7 +669,7 @@ impl<'a> Analyzer<'a> { let compr_scope = match compr { Expr::ArrayCompr { query, term, .. } | Expr::SetCompr { query, term, .. } => { self.analyze_query(None, Some(term), query, Scope::default())?; - self.locals.get(query) + self.locals.get(&Ref::make(query)) } Expr::ObjectCompr { query, key, value, .. @@ -683,7 +680,7 @@ impl<'a> Analyzer<'a> { query, Scope::default(), )?; - self.locals.get(query) + self.locals.get(&Ref::make(query)) } _ => break, }; @@ -1041,14 +1038,14 @@ impl<'a> Analyzer<'a> { let res = schedule(&mut infos[..]); match res { Ok(SortResult::Order(ord)) => { - self.order.insert(query, ord); + self.order.insert(Ref::make(query), ord); } Err(err) => { bail!(query.span.error(&err.to_string())) } _ => (), } - self.locals.insert(query, scope); + self.locals.insert(Ref::make(query), scope); Ok(()) } diff --git a/tests/interpreter/mod.rs b/tests/interpreter/mod.rs index 7e98ed6..0e1b875 100644 --- a/tests/interpreter/mod.rs +++ b/tests/interpreter/mod.rs @@ -558,6 +558,7 @@ fn run_opa_tests() -> Result<()> { if !Path::new(&a).is_dir() { continue; } + for entry in WalkDir::new(a) .sort_by_file_name() .into_iter() diff --git a/tests/scheduler/analyzer/mod.rs b/tests/scheduler/analyzer/mod.rs index 5340774..c4b6afe 100644 --- a/tests/scheduler/analyzer/mod.rs +++ b/tests/scheduler/analyzer/mod.rs @@ -56,7 +56,13 @@ fn analyze_file(regos: &[String], expected_scopes: &[Scope]) -> Result<()> { let analyzer = Analyzer::new(); let schedule = analyzer.analyze(&modules_ref)?; - for (idx, (_, scope)) in schedule.scopes.iter().enumerate() { + let mut scopes: Vec<(&Query, ®orus::Scope)> = schedule + .scopes + .iter() + .map(|(r, s)| (r.inner(), s)) + .collect(); + scopes.sort_by(|a, b| a.0.span.line.cmp(&b.0.span.line)); + for (idx, (_, scope)) in scopes.iter().enumerate() { if idx > expected_scopes.len() { bail!("extra scope generated.") }