use crate::{SymbolTable, TypeTable, static_analysis::await_checker::AwaitChecker};
use leo_ast::*;
use leo_errors::{StaticAnalyzerError, StaticAnalyzerWarning, emitter::Handler};
use leo_span::{Span, Symbol};
use snarkvm::console::network::Network;
use std::marker::PhantomData;
pub struct StaticAnalyzer<'a, N: Network> {
pub(crate) symbol_table: &'a SymbolTable,
pub(crate) type_table: &'a TypeTable,
pub(crate) handler: &'a Handler,
pub(crate) await_checker: AwaitChecker,
pub(crate) current_program: Option<Symbol>,
pub(crate) variant: Option<Variant>,
pub(crate) non_async_external_call_seen: bool,
phantom: PhantomData<N>,
}
impl<'a, N: Network> StaticAnalyzer<'a, N> {
pub fn new(
symbol_table: &'a SymbolTable,
_type_table: &'a TypeTable,
handler: &'a Handler,
max_depth: usize,
disabled: bool,
) -> Self {
Self {
symbol_table,
type_table: _type_table,
handler,
await_checker: AwaitChecker::new(max_depth, !disabled),
current_program: None,
variant: None,
non_async_external_call_seen: false,
phantom: Default::default(),
}
}
pub(crate) fn emit_err(&self, err: StaticAnalyzerError) {
self.handler.emit_err(err);
}
pub fn emit_warning(&self, warning: StaticAnalyzerWarning) {
self.handler.emit_warning(warning.into());
}
pub(crate) fn assert_future_await(&mut self, future: &Option<&Expression>, span: Span) {
let future_variable = match future {
Some(Expression::Identifier(name)) => name,
_ => {
return self.emit_err(StaticAnalyzerError::invalid_await_call(span));
}
};
match self.type_table.get(&future_variable.id) {
Some(type_) => {
if !matches!(type_, Type::Future(_)) {
self.emit_err(StaticAnalyzerError::expected_future(future_variable.name, future_variable.span()));
}
if self.await_checker.remove(future_variable) {
self.emit_warning(StaticAnalyzerWarning::future_not_awaited_in_order(
future_variable.name,
future_variable.span(),
));
}
}
None => {
self.emit_err(StaticAnalyzerError::expected_future(future_variable.name, future_variable.span()));
}
}
}
pub(crate) fn assert_simple_async_transition_call(&self, program: Symbol, function_name: Symbol, span: Span) {
let func_symbol = self
.symbol_table
.lookup_function(Location::new(program, function_name))
.expect("Type checking guarantees functions are present.");
if func_symbol.function.variant != Variant::AsyncTransition {
return;
}
let finalizer = func_symbol
.finalizer
.as_ref()
.expect("Typechecking guarantees that all async transitions have an associated `finalize` field.");
let async_function = self
.symbol_table
.lookup_function(finalizer.location)
.expect("Type checking guarantees functions are present.");
if async_function.function.input.iter().any(|input| matches!(input.type_(), Type::Future(..))) {
self.emit_err(StaticAnalyzerError::async_transition_call_with_future_argument(function_name, span));
}
}
}