1use serde::ser::{SerializeStruct, Serializer};
2use serde::Serialize;
3use std::fmt;
4
5use crate::checker::Checker;
6use crate::*;
7
8#[derive(Debug, Clone)]
13pub struct Stats {
14 exit: usize,
15 exit_sum: usize,
16 total_space_functions: usize,
17 exit_min: usize,
18 exit_max: usize,
19}
20
21impl Default for Stats {
22 fn default() -> Self {
23 Self {
24 exit: 0,
25 exit_sum: 0,
26 total_space_functions: 1,
27 exit_min: usize::MAX,
28 exit_max: 0,
29 }
30 }
31}
32
33impl Serialize for Stats {
34 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
35 where
36 S: Serializer,
37 {
38 let mut st = serializer.serialize_struct("nexits", 4)?;
39 st.serialize_field("sum", &self.exit())?;
40 st.serialize_field("average", &self.exit_average())?;
41 st.serialize_field("min", &self.exit_min())?;
42 st.serialize_field("max", &self.exit_max())?;
43 st.end()
44 }
45}
46
47impl fmt::Display for Stats {
48 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
49 write!(
50 f,
51 "sum: {}, average: {} min: {}, max: {}",
52 self.exit_sum(),
53 self.exit_average(),
54 self.exit_min(),
55 self.exit_max()
56 )
57 }
58}
59
60impl Stats {
61 pub fn merge(&mut self, other: &Stats) {
63 self.exit_max = self.exit_max.max(other.exit_max);
64 self.exit_min = self.exit_min.min(other.exit_min);
65 self.exit_sum += other.exit_sum;
66 }
67
68 pub fn exit(&self) -> f64 {
70 self.exit as f64
71 }
72 pub fn exit_sum(&self) -> f64 {
74 self.exit_sum as f64
75 }
76 pub fn exit_min(&self) -> f64 {
78 self.exit_min as f64
79 }
80 pub fn exit_max(&self) -> f64 {
82 self.exit_max as f64
83 }
84
85 pub fn exit_average(&self) -> f64 {
92 self.exit_sum() / self.total_space_functions as f64
93 }
94 #[inline(always)]
95 pub(crate) fn compute_sum(&mut self) {
96 self.exit_sum += self.exit;
97 }
98 #[inline(always)]
99 pub(crate) fn compute_minmax(&mut self) {
100 self.exit_max = self.exit_max.max(self.exit);
101 self.exit_min = self.exit_min.min(self.exit);
102 self.compute_sum();
103 }
104 pub(crate) fn finalize(&mut self, total_space_functions: usize) {
105 self.total_space_functions = total_space_functions;
106 }
107}
108
109#[doc(hidden)]
110pub trait Exit
111where
112 Self: Checker,
113{
114 fn compute(_node: &Node, _stats: &mut Stats) {}
115}
116
117impl Exit for PythonCode {
118 fn compute(node: &Node, stats: &mut Stats) {
119 if matches!(node.object().kind_id().into(), Python::ReturnStatement) {
120 stats.exit += 1;
121 }
122 }
123}
124
125impl Exit for MozjsCode {
126 fn compute(node: &Node, stats: &mut Stats) {
127 if matches!(node.object().kind_id().into(), Mozjs::ReturnStatement) {
128 stats.exit += 1;
129 }
130 }
131}
132
133impl Exit for JavascriptCode {
134 fn compute(node: &Node, stats: &mut Stats) {
135 if matches!(node.object().kind_id().into(), Javascript::ReturnStatement) {
136 stats.exit += 1;
137 }
138 }
139}
140
141impl Exit for TypescriptCode {
142 fn compute(node: &Node, stats: &mut Stats) {
143 if matches!(node.object().kind_id().into(), Typescript::ReturnStatement) {
144 stats.exit += 1;
145 }
146 }
147}
148
149impl Exit for TsxCode {
150 fn compute(node: &Node, stats: &mut Stats) {
151 if matches!(node.object().kind_id().into(), Tsx::ReturnStatement) {
152 stats.exit += 1;
153 }
154 }
155}
156
157impl Exit for RustCode {
158 fn compute(node: &Node, stats: &mut Stats) {
159 if matches!(node.object().kind_id().into(), Rust::ReturnExpression)
160 || Self::is_func(node) && node.object().child_by_field_name("return_type").is_some()
161 {
162 stats.exit += 1;
163 }
164 }
165}
166
167impl Exit for CppCode {
168 fn compute(node: &Node, stats: &mut Stats) {
169 if matches!(node.object().kind_id().into(), Cpp::ReturnStatement) {
170 stats.exit += 1;
171 }
172 }
173}
174
175impl Exit for JavaCode {
176 fn compute(node: &Node, stats: &mut Stats) {
177 if matches!(node.object().kind_id().into(), Java::ReturnStatement) {
178 stats.exit += 1;
179 }
180 }
181}
182
183impl Exit for PreprocCode {}
184impl Exit for CcommentCode {}
185
186#[cfg(test)]
187mod tests {
188 use std::path::PathBuf;
189
190 use super::*;
191
192 #[test]
193 fn python_no_exit() {
194 check_metrics!(
195 "a = 42",
196 "foo.py",
197 PythonParser,
198 nexits,
199 [
200 (exit_sum, 0, usize),
201 (exit_min, 0, usize),
202 (exit_max, 0, usize)
203 ],
204 [(exit_average, f64::NAN)] );
206 }
207
208 #[test]
209 fn rust_no_exit() {
210 check_metrics!(
211 "let a = 42;",
212 "foo.rs",
213 RustParser,
214 nexits,
215 [
216 (exit_sum, 0, usize),
217 (exit_min, 0, usize),
218 (exit_max, 0, usize)
219 ],
220 [(exit_average, f64::NAN)] );
222 }
223
224 #[test]
225 fn c_no_exit() {
226 check_metrics!(
227 "int a = 42;",
228 "foo.c",
229 CppParser,
230 nexits,
231 [
232 (exit_sum, 0, usize),
233 (exit_min, 0, usize),
234 (exit_max, 0, usize)
235 ],
236 [(exit_average, f64::NAN)] );
238 }
239
240 #[test]
241 fn javascript_no_exit() {
242 check_metrics!(
243 "var a = 42;",
244 "foo.js",
245 JavascriptParser,
246 nexits,
247 [
248 (exit_sum, 0, usize),
249 (exit_min, 0, usize),
250 (exit_max, 0, usize)
251 ],
252 [(exit_average, f64::NAN)] );
254 }
255
256 #[test]
257 fn python_simple_function() {
258 check_metrics!(
259 "def f(a, b):
260 if a:
261 return a",
262 "foo.py",
263 PythonParser,
264 nexits,
265 [
266 (exit_sum, 1, usize),
267 (exit_min, 0, usize),
268 (exit_max, 1, usize)
269 ],
270 [(exit_average, 1.0)] );
272 }
273
274 #[test]
275 fn python_more_functions() {
276 check_metrics!(
277 "def f(a, b):
278 if a:
279 return a
280 def f(a, b):
281 if b:
282 return b",
283 "foo.py",
284 PythonParser,
285 nexits,
286 [
287 (exit_sum, 2, usize),
288 (exit_min, 0, usize),
289 (exit_max, 1, usize)
290 ],
291 [(exit_average, 1.0)] );
293 }
294
295 #[test]
296 fn python_nested_functions() {
297 check_metrics!(
298 "def f(a, b):
299 def foo(a):
300 if a:
301 return 1
302 bar = lambda a: lambda b: b or True or True
303 return bar(foo(a))(a)",
304 "foo.py",
305 PythonParser,
306 nexits,
307 [
308 (exit_sum, 2, usize),
309 (exit_min, 0, usize),
310 (exit_max, 1, usize)
311 ],
312 [(exit_average, 0.5)] );
314 }
315
316 #[test]
317 fn java_no_exit() {
318 check_metrics!(
319 "int a = 42;",
320 "foo.java",
321 JavaParser,
322 nexits,
323 [
324 (exit_sum, 0, usize),
325 (exit_min, 0, usize),
326 (exit_max, 0, usize)
327 ],
328 [(exit_average, f64::NAN)] );
330 }
331
332 #[test]
333 fn java_simple_function() {
334 check_metrics!(
335 "class A {
336 public int sum(int x, int y) {
337 return x + y;
338 }
339 }",
340 "foo.java",
341 JavaParser,
342 nexits,
343 [
344 (exit_sum, 1, usize),
345 (exit_min, 0, usize),
346 (exit_max, 1, usize)
347 ],
348 [(exit_average, 1.0)] );
350 }
351
352 #[test]
353 fn java_split_function() {
354 check_metrics!(
355 "class A {
356 public int multiply(int x, int y) {
357 if(x == 0 || y == 0){
358 return 0;
359 }
360 return x * y;
361 }
362 }",
363 "foo.java",
364 JavaParser,
365 nexits,
366 [
367 (exit_sum, 2, usize),
368 (exit_min, 0, usize),
369 (exit_max, 2, usize)
370 ],
371 [(exit_average, 2.0)] );
373 }
374}