use super::StaticAnalyzingVisitor;
use leo_ast::{Type, *};
use leo_errors::{StaticAnalyzerError, StaticAnalyzerWarning};
impl ProgramVisitor for StaticAnalyzingVisitor<'_> {
fn visit_program_scope(&mut self, input: &ProgramScope) {
self.current_program = input.program_id.as_symbol();
input.consts.iter().for_each(|(_, c)| self.visit_const(c));
input.composites.iter().for_each(|(_, c)| self.visit_composite(c));
input.interfaces.iter().for_each(|(_, c)| self.visit_interface(c));
input.mappings.iter().for_each(|(_, c)| self.visit_mapping(c));
input.storage_variables.iter().for_each(|(_, c)| self.visit_storage_variable(c));
input.functions.iter().for_each(|(_, c)| self.visit_function(c));
if let Some(c) = input.constructor.as_ref() {
self.visit_constructor(c);
}
}
fn visit_function(&mut self, function: &Function) {
function.const_parameters.iter().for_each(|input| self.visit_type(&input.type_));
function.input.iter().for_each(|input| self.visit_type(&input.type_));
function.output.iter().for_each(|output| self.visit_type(&output.type_));
self.visit_type(&function.output_type);
self.variant = Some(function.variant);
self.non_async_external_call_seen = false;
if self.variant.is_some_and(|v| v.is_onchain()) | function.has_final_output() {
super::future_checker::future_check_function(function, &self.state.type_table, &self.state.handler);
}
if self.variant.is_some_and(|v| v.is_onchain()) {
self.await_checker.set_futures(
function
.input
.iter()
.filter_map(|input| {
if let Type::Future(_) = input.type_.clone() { Some(input.identifier.name) } else { None }
})
.collect(),
);
}
self.visit_block(&function.block);
if self.variant.is_some_and(|v| v.is_onchain()) {
if !self.await_checker.static_to_await.is_empty() {
self.emit_err(StaticAnalyzerError::final_runs_missing(
self.await_checker
.static_to_await
.clone()
.iter()
.map(|f| f.to_string())
.collect::<Vec<String>>()
.join(", "),
function.span(),
));
} else if !self.await_checker.to_await.is_empty() {
let (num_paths_unawaited, num_paths_duplicate_awaited, num_perfect) =
self.await_checker.to_await.iter().fold((0, 0, 0), |(unawaited, duplicate, perfect), path| {
(
unawaited + if !path.elements.is_empty() { 1 } else { 0 },
duplicate + if path.counter > 0 { 1 } else { 0 },
perfect + if path.counter > 0 || !path.elements.is_empty() { 0 } else { 1 },
)
});
if num_perfect == 0 {
self.emit_err(StaticAnalyzerError::no_path_runs_all_finals_exactly_once(
self.await_checker.to_await.len(),
function.span(),
));
}
if num_paths_unawaited > 0 {
self.emit_warning(StaticAnalyzerWarning::some_paths_do_not_run_all_finals(
self.await_checker.to_await.len(),
num_paths_unawaited,
function.span(),
));
}
if num_paths_duplicate_awaited > 0 {
self.emit_warning(StaticAnalyzerWarning::some_paths_contain_duplicate_final_runs(
self.await_checker.to_await.len(),
num_paths_duplicate_awaited,
function.span(),
));
}
}
}
}
fn visit_constructor(&mut self, _: &Constructor) {
}
fn visit_library(&mut self, input: &leo_ast::Library) {
self.current_program = input.name;
input.structs.iter().for_each(|(_, s)| self.visit_composite(s));
input.consts.iter().for_each(|(_, c)| self.visit_const(c));
input.functions.iter().for_each(|(_, f)| self.visit_function(f));
input.modules.values().for_each(|m| self.visit_module(m));
}
}