#[cfg(debug_assertions)]
use super::*;
#[cfg(debug_assertions)]
pub(super) fn debug_check_invariants(query: &CypherQuery, after_pass_name: &str) {
if let Err(msg) = check_match_patterns_non_empty(query) {
panic!("Pass `{after_pass_name}` produced invalid IR: {msg}");
}
if let Err(msg) = check_return_with_items_non_empty(query) {
panic!("Pass `{after_pass_name}` produced invalid IR: {msg}");
}
if let Err(msg) = check_limit_skip_nonnegative(query) {
panic!("Pass `{after_pass_name}` produced invalid IR: {msg}");
}
}
#[cfg(debug_assertions)]
fn check_match_patterns_non_empty(query: &CypherQuery) -> Result<(), String> {
for (idx, clause) in query.clauses.iter().enumerate() {
let mc = match clause {
Clause::Match(m) | Clause::OptionalMatch(m) => m,
_ => continue,
};
if mc.patterns.is_empty() {
return Err(format!("Match clause at index {idx} has no patterns"));
}
for (pi, p) in mc.patterns.iter().enumerate() {
if p.elements.is_empty() {
return Err(format!(
"Match clause at index {idx}, pattern {pi} has no elements"
));
}
}
}
Ok(())
}
#[cfg(debug_assertions)]
fn check_return_with_items_non_empty(query: &CypherQuery) -> Result<(), String> {
for (idx, clause) in query.clauses.iter().enumerate() {
match clause {
Clause::Return(r) if r.items.is_empty() => {
return Err(format!("Return clause at index {idx} has no items"));
}
Clause::With(w) if w.items.is_empty() => {
return Err(format!("With clause at index {idx} has no items"));
}
_ => {}
}
}
Ok(())
}
#[cfg(debug_assertions)]
fn check_limit_skip_nonnegative(query: &CypherQuery) -> Result<(), String> {
for (idx, clause) in query.clauses.iter().enumerate() {
match clause {
Clause::Limit(l) => {
if let Expression::Literal(Value::Int64(n)) = &l.count {
if *n < 0 {
return Err(format!(
"Limit clause at index {idx} has negative literal {n}"
));
}
}
}
Clause::Skip(s) => {
if let Expression::Literal(Value::Int64(n)) = &s.count {
if *n < 0 {
return Err(format!(
"Skip clause at index {idx} has negative literal {n}"
));
}
}
}
_ => {}
}
}
Ok(())
}