leo_passes/static_analysis/
expression.rs

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