neo_solidity/solidity/validate/
returns.rs1pub fn extract_return_expression(body: &Option<Statement>) -> Option<Expression> {
2 let statement = body.as_ref()?;
3
4 match statement {
5 Statement::Block { statements, .. } => {
6 if statements.len() == 1 {
7 if let Statement::Return(_, expr) = &statements[0] {
8 expr.clone()
9 } else {
10 None
11 }
12 } else {
13 None
14 }
15 }
16 Statement::Return(_, expr) => expr.clone(),
17 _ => None,
18 }
19}
20
21fn check_return_statements(
22 statement: &Statement,
23 expected_count: usize,
24 function_name: &str,
25 return_arities: &std::collections::HashMap<(String, usize), usize>,
26 diagnostics: &mut Vec<Diagnostic>,
27) {
28 match statement {
29 Statement::Block { statements, .. } => {
30 for stmt in statements {
31 check_return_statements(
32 stmt,
33 expected_count,
34 function_name,
35 return_arities,
36 diagnostics,
37 );
38 }
39 }
40 Statement::If(_, _, then_stmt, else_stmt) => {
41 check_return_statements(
42 then_stmt,
43 expected_count,
44 function_name,
45 return_arities,
46 diagnostics,
47 );
48 if let Some(else_stmt) = else_stmt {
49 check_return_statements(
50 else_stmt,
51 expected_count,
52 function_name,
53 return_arities,
54 diagnostics,
55 );
56 }
57 }
58 Statement::While(_, _, body) => {
59 check_return_statements(body, expected_count, function_name, return_arities, diagnostics);
60 }
61 Statement::DoWhile(_, body, _) => {
62 check_return_statements(body, expected_count, function_name, return_arities, diagnostics);
63 }
64 Statement::For(_, init, _, _, body) => {
65 if let Some(init_stmt) = init {
66 check_return_statements(
67 init_stmt,
68 expected_count,
69 function_name,
70 return_arities,
71 diagnostics,
72 );
73 }
74 if let Some(body_stmt) = body {
75 check_return_statements(
76 body_stmt,
77 expected_count,
78 function_name,
79 return_arities,
80 diagnostics,
81 );
82 }
83 }
84 Statement::Try(_, _, handler, catches) => {
85 if let Some((_, handler_stmt)) = handler {
86 check_return_statements(
87 handler_stmt,
88 expected_count,
89 function_name,
90 return_arities,
91 diagnostics,
92 );
93 }
94 for catch in catches {
95 match catch {
96 CatchClause::Simple(_, _, stmt) => {
97 check_return_statements(
98 stmt,
99 expected_count,
100 function_name,
101 return_arities,
102 diagnostics,
103 )
104 }
105 CatchClause::Named(_, _, _, stmt) => {
106 check_return_statements(
107 stmt,
108 expected_count,
109 function_name,
110 return_arities,
111 diagnostics,
112 )
113 }
114 }
115 }
116 }
117 Statement::Return(_, expr) => match (expected_count, expr) {
118 (0, Some(_)) => diagnostics.push(Diagnostic::warning(format!(
119 "function '{}' returns a value but is declared without return type",
120 function_name
121 ))),
122 (0, None) => {}
123 (1, None) => diagnostics.push(Diagnostic::warning(format!(
124 "function '{}' declares a return value but returns without one",
125 function_name
126 ))),
127 (1, Some(_)) => {}
128 (expected, Some(expr)) => {
129 if let Expression::List(_, list) = expr {
130 let actual = list.len();
131 if actual != expected {
132 diagnostics.push(Diagnostic::warning(format!(
133 "function '{}' expected {} return values but found {}",
134 function_name, expected, actual
135 )));
136 }
137 } else {
138 let inferred = match expr {
139 Expression::FunctionCall(_, callee, args) => {
140 let name = match callee.as_ref() {
141 Expression::Variable(id) => Some(id.name.as_str()),
142 Expression::MemberAccess(_, _, member) => Some(member.name.as_str()),
143 _ => None,
144 };
145 name.and_then(|name| {
146 return_arities
147 .get(&(name.to_string(), args.len()))
148 .copied()
149 })
150 }
151 _ => None,
152 };
153
154 if let Some(actual) = inferred {
155 if actual != expected {
156 diagnostics.push(Diagnostic::warning(format!(
157 "function '{}' expected {} return values but call returns {}",
158 function_name, expected, actual
159 )));
160 }
161 } else {
162 diagnostics.push(Diagnostic::warning(format!(
163 "function '{}' should return {} values but expression does not match tuple",
164 function_name, expected
165 )));
166 }
167 }
168 }
169 (expected, None) => diagnostics.push(Diagnostic::warning(format!(
170 "function '{}' declares {} return values but returns without one",
171 function_name, expected
172 ))),
173 },
174 _ => {}
175 }
176}