use std::collections::BTreeSet;
use crate::expr::operator::PostfixOperator;
use crate::expr::visit::{Visit as _, Visitor};
use crate::expr::{Block, Expr, Function, Part};
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct FunctionFacts {
pub direct_writes: bool,
pub calls: BTreeSet<String>,
pub opaque_effects: bool,
}
struct FactsScanner {
facts: FunctionFacts,
}
impl Visitor for FactsScanner {
type Error = std::convert::Infallible;
fn visit_expr(&mut self, expr: &Expr) -> Result<(), Self::Error> {
match expr {
Expr::Create(_)
| Expr::Update(_)
| Expr::Upsert(_)
| Expr::Delete(_)
| Expr::Relate(_)
| Expr::Insert(_)
| Expr::Define(_)
| Expr::Remove(_)
| Expr::Rebuild(_)
| Expr::Alter(_) => {
self.facts.direct_writes = true;
}
Expr::Match(plan) => {
if plan.has_mutations() {
self.facts.direct_writes = true;
}
}
Expr::Postfix {
expr: target,
op: PostfixOperator::Call(_),
} => {
if !matches!(&**target, Expr::Closure(_)) {
self.facts.opaque_effects = true;
}
}
Expr::Postfix {
op: PostfixOperator::MethodCall(name, _),
..
} => {
if !crate::expr::method::is_builtin_method(name) {
self.facts.opaque_effects = true;
}
}
Expr::Literal(_)
| Expr::Param(_)
| Expr::Idiom(_)
| Expr::Table(_)
| Expr::Mock(_)
| Expr::Block(_)
| Expr::Constant(_)
| Expr::Prefix {
..
}
| Expr::Postfix {
..
}
| Expr::Binary {
..
}
| Expr::FunctionCall(_)
| Expr::Closure(_)
| Expr::Break
| Expr::Continue
| Expr::Return(_)
| Expr::Throw(_)
| Expr::IfElse(_)
| Expr::Select(_)
| Expr::Info(_)
| Expr::Foreach(_)
| Expr::Let(_)
| Expr::Sleep(_)
| Expr::Explain {
..
} => {}
}
expr.visit(self)
}
fn visit_part(&mut self, part: &Part) -> Result<(), Self::Error> {
if let Part::Method(name, _) = part
&& !crate::expr::method::is_builtin_method(name)
{
self.facts.opaque_effects = true;
}
part.visit(self)
}
fn visit_function(&mut self, f: &Function) -> Result<(), Self::Error> {
match f {
Function::Custom(name) => {
self.facts.calls.insert(name.clone());
}
other => {
if !other.read_only() {
self.facts.opaque_effects = true;
}
}
}
Ok(())
}
}
impl Expr {
pub fn function_facts(&self) -> FunctionFacts {
let mut scanner = FactsScanner {
facts: FunctionFacts::default(),
};
let Ok(()) = scanner.visit_expr(self);
scanner.facts
}
}
impl Block {
pub fn function_facts(&self) -> FunctionFacts {
let mut scanner = FactsScanner {
facts: FunctionFacts::default(),
};
let Ok(()) = scanner.visit_block(self);
scanner.facts
}
}