leo_passes/static_analysis/analyze_expression.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
// Copyright (C) 2019-2025 Provable Inc.
// This file is part of the Leo library.
// The Leo library is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// The Leo library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
use crate::StaticAnalyzer;
use leo_ast::*;
use snarkvm::console::network::Network;
impl<N: Network> ExpressionVisitor for StaticAnalyzer<'_, N> {
type AdditionalInput = ();
type Output = ();
fn visit_access(&mut self, input: &AccessExpression, _: &Self::AdditionalInput) -> Self::Output {
if let AccessExpression::AssociatedFunction(access) = input {
// Get the core function.
let core_function = match CoreFunction::from_symbols(access.variant.name, access.name.name) {
Some(core_function) => core_function,
None => unreachable!("Typechecking guarantees that this function exists."),
};
// Check that the future was awaited correctly.
if core_function == CoreFunction::FutureAwait {
self.assert_future_await(&access.arguments.first(), input.span());
}
}
}
fn visit_call(&mut self, input: &CallExpression, _: &Self::AdditionalInput) -> Self::Output {
let Expression::Identifier(ident) = &*input.function else {
unreachable!("Parsing guarantees that a function name is always an identifier.");
};
let caller_program = self.current_program.unwrap();
let callee_program = input.program.unwrap_or(caller_program);
// If the function call is an external async transition, then for all async calls that follow a non-async call,
// we must check that the async call is not an async function that takes a future as an argument.
if self.non_async_external_call_seen
&& self.variant == Some(Variant::AsyncTransition)
&& callee_program != caller_program
{
self.assert_simple_async_transition_call(callee_program, ident.name, input.span());
}
// Look up the function and check if it is a non-async call.
let function_program = input.program.unwrap_or(self.current_program.unwrap());
let func_symbol = self
.symbol_table
.lookup_function(Location::new(function_program, ident.name))
.expect("Type checking guarantees functions exist.");
if func_symbol.function.variant == Variant::Transition {
self.non_async_external_call_seen = true;
}
}
}