leo_passes/static_analysis/
program.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::{Type, *};
20use leo_errors::{StaticAnalyzerError, StaticAnalyzerWarning};
21
22impl ProgramVisitor for StaticAnalyzingVisitor<'_> {
23    fn visit_program_scope(&mut self, input: &ProgramScope) {
24        // Set the current program name.
25        self.current_program = input.program_id.name.name;
26        // Do the default implementation for visiting the program scope.
27        input.consts.iter().for_each(|(_, c)| self.visit_const(c));
28        input.structs.iter().for_each(|(_, c)| self.visit_struct(c));
29        input.mappings.iter().for_each(|(_, c)| self.visit_mapping(c));
30        input.storage_variables.iter().for_each(|(_, c)| self.visit_storage_variable(c));
31        input.functions.iter().for_each(|(_, c)| self.visit_function(c));
32        if let Some(c) = input.constructor.as_ref() {
33            self.visit_constructor(c);
34        }
35    }
36
37    fn visit_function(&mut self, function: &Function) {
38        function.const_parameters.iter().for_each(|input| self.visit_type(&input.type_));
39        function.input.iter().for_each(|input| self.visit_type(&input.type_));
40        function.output.iter().for_each(|output| self.visit_type(&output.type_));
41        self.visit_type(&function.output_type);
42
43        // Set the function name and variant.
44        self.variant = Some(function.variant);
45
46        // Set `non_async_external_call_seen` to false.
47        self.non_async_external_call_seen = false;
48
49        if matches!(self.variant, Some(Variant::AsyncFunction) | Some(Variant::AsyncTransition)) {
50            super::future_checker::future_check_function(function, &self.state.type_table, &self.state.handler);
51        }
52
53        // If the function is an async function, initialize the await checker.
54        if self.variant == Some(Variant::AsyncFunction) {
55            // Initialize the list of input futures. Each one must be awaited before the end of the function.
56            self.await_checker.set_futures(
57                function
58                    .input
59                    .iter()
60                    .filter_map(|input| {
61                        if let Type::Future(_) = input.type_.clone() { Some(input.identifier.name) } else { None }
62                    })
63                    .collect(),
64            );
65        }
66
67        self.visit_block(&function.block);
68
69        // Check that all futures were awaited exactly once.
70        if self.variant == Some(Variant::AsyncFunction) {
71            // Throw error if not all futures awaits even appear once.
72            if !self.await_checker.static_to_await.is_empty() {
73                self.emit_err(StaticAnalyzerError::future_awaits_missing(
74                    self.await_checker
75                        .static_to_await
76                        .clone()
77                        .iter()
78                        .map(|f| f.to_string())
79                        .collect::<Vec<String>>()
80                        .join(", "),
81                    function.span(),
82                ));
83            } else if !self.await_checker.to_await.is_empty() {
84                // Tally up number of paths that are unawaited and number of paths that are awaited more than once.
85                let (num_paths_unawaited, num_paths_duplicate_awaited, num_perfect) =
86                    self.await_checker.to_await.iter().fold((0, 0, 0), |(unawaited, duplicate, perfect), path| {
87                        (
88                            unawaited + if !path.elements.is_empty() { 1 } else { 0 },
89                            duplicate + if path.counter > 0 { 1 } else { 0 },
90                            perfect + if path.counter > 0 || !path.elements.is_empty() { 0 } else { 1 },
91                        )
92                    });
93
94                // Throw error if there does not exist a path in which all futures are awaited exactly once.
95                if num_perfect == 0 {
96                    self.emit_err(StaticAnalyzerError::no_path_awaits_all_futures_exactly_once(
97                        self.await_checker.to_await.len(),
98                        function.span(),
99                    ));
100                }
101
102                // Throw warning if not all futures are awaited in some paths.
103                if num_paths_unawaited > 0 {
104                    self.emit_warning(StaticAnalyzerWarning::some_paths_do_not_await_all_futures(
105                        self.await_checker.to_await.len(),
106                        num_paths_unawaited,
107                        function.span(),
108                    ));
109                }
110
111                // Throw warning if some futures are awaited more than once in some paths.
112                if num_paths_duplicate_awaited > 0 {
113                    self.emit_warning(StaticAnalyzerWarning::some_paths_contain_duplicate_future_awaits(
114                        self.await_checker.to_await.len(),
115                        num_paths_duplicate_awaited,
116                        function.span(),
117                    ));
118                }
119            }
120        }
121    }
122
123    fn visit_constructor(&mut self, _: &Constructor) {
124        // Do nothing, since constructors do not have awaits or futures.
125    }
126}