use super::StaticAnalyzingVisitor;
use crate::errors::static_analyzer;
use leo_ast::{TypeKind, *};
use leo_errors::Label;
use leo_span::Symbol;
impl UnitVisitor for StaticAnalyzingVisitor<'_> {
fn visit_program_scope(&mut self, input: &ProgramScope) {
self.current_unit = 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_.kind()));
function.input.iter().for_each(|input| self.visit_type(input.type_.kind()));
function.output.iter().for_each(|output| self.visit_type(output.type_.kind()));
self.visit_type(&function.output_type);
self.variant = Some(function.variant);
self.non_async_external_call_seen = false;
let external_inputs = function.input.iter().filter_map(|input| {
record_program(self.state, self.current_unit, input.type_.kind())
.filter(|program| *program != self.current_unit)
.map(|program| (input, program))
});
let record_outputs = function.output.iter().filter_map(|output| {
record_program(self.state, self.current_unit, output.type_.kind()).map(|program| (output, program))
});
let matching_inputs = external_inputs
.clone()
.filter(|(_, program)| record_outputs.clone().any(|(_, output_program)| *program != output_program));
if matching_inputs.clone().next().is_some() {
let input_labels = matching_inputs.map(|(input, _)| {
Label::new(input.span())
.with_message(format!("external input `{}` of type `{}`", input.identifier, input.type_))
});
let output_labels = record_outputs
.clone()
.filter(|(_, program)| external_inputs.clone().any(|(_, input_program)| *program != input_program))
.map(|(output, _)| {
Label::new(output.span()).with_message(format!("output record of type `{}`", output.type_))
});
self.emit_warning(
static_analyzer::cross_program_record_output(function.identifier.span())
.with_labels(input_labels.chain(output_labels)),
);
}
if self.variant.is_some_and(|v| v.is_finalize_context()) | function.has_final_output() {
super::future_checker::future_check_function(
function,
&self.state.type_table,
&self.state.types,
&self.state.handler,
);
}
if self.variant.is_some_and(|v| v.is_finalize_context()) {
self.await_checker.set_futures(
function
.input
.iter()
.filter_map(|input| {
if let TypeKind::Future(_) = input.type_.kind().clone() {
Some(input.identifier.name)
} else {
None
}
})
.collect(),
);
}
self.visit_block(&function.block);
if self.variant.is_some_and(|v| v.is_finalize_context()) {
if !self.await_checker.static_to_await.is_empty() {
self.emit_err(static_analyzer::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(static_analyzer::no_path_runs_all_finals_exactly_once(
self.await_checker.to_await.len(),
function.span(),
));
}
if num_paths_unawaited > 0 {
self.emit_warning(static_analyzer::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(static_analyzer::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_unit = 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));
input.stubs.values().for_each(|stub| self.visit_stub(stub));
}
}
fn record_program(state: &crate::CompilerState, current_unit: Symbol, type_: &TypeKind) -> Option<Symbol> {
let TypeKind::Composite(composite) = type_ else { return None };
let location = composite.path.expect_global_location();
state.symbol_table.lookup_record(current_unit, location).map(|_| location.program)
}