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