rust_code_analysis_code_split/metrics/
exit.rs1use serde::Serialize;
2use serde::ser::{SerializeStruct, Serializer};
3use std::fmt;
4
5use crate::checker::Checker;
6use crate::macros::implement_metric_trait;
7use crate::*;
8
9#[derive(Debug, Clone)]
14pub struct Stats {
15 exit: usize,
16 exit_sum: usize,
17 total_space_functions: usize,
18 exit_min: usize,
19 exit_max: usize,
20}
21
22impl Default for Stats {
23 fn default() -> Self {
24 Self {
25 exit: 0,
26 exit_sum: 0,
27 total_space_functions: 1,
28 exit_min: usize::MAX,
29 exit_max: 0,
30 }
31 }
32}
33
34impl Serialize for Stats {
35 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
36 where
37 S: Serializer,
38 {
39 let mut st = serializer.serialize_struct("nexits", 4)?;
40 st.serialize_field("sum", &self.exit_sum())?;
41 st.serialize_field("average", &self.exit_average())?;
42 st.serialize_field("min", &self.exit_min())?;
43 st.serialize_field("max", &self.exit_max())?;
44 st.end()
45 }
46}
47
48impl fmt::Display for Stats {
49 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
50 write!(
51 f,
52 "sum: {}, average: {} min: {}, max: {}",
53 self.exit_sum(),
54 self.exit_average(),
55 self.exit_min(),
56 self.exit_max()
57 )
58 }
59}
60
61impl Stats {
62 pub fn merge(&mut self, other: &Stats) {
64 self.exit_max = self.exit_max.max(other.exit_max);
65 self.exit_min = self.exit_min.min(other.exit_min);
66 self.exit_sum += other.exit_sum;
67 }
68
69 pub fn exit(&self) -> f64 {
71 self.exit as f64
72 }
73 pub fn exit_sum(&self) -> f64 {
75 self.exit_sum as f64
76 }
77 pub fn exit_min(&self) -> f64 {
79 self.exit_min as f64
80 }
81 pub fn exit_max(&self) -> f64 {
83 self.exit_max as f64
84 }
85
86 pub fn exit_average(&self) -> f64 {
93 self.exit_sum() / self.total_space_functions as f64
94 }
95 #[inline(always)]
96 pub(crate) fn compute_sum(&mut self) {
97 self.exit_sum += self.exit;
98 }
99 #[inline(always)]
100 pub(crate) fn compute_minmax(&mut self) {
101 self.exit_max = self.exit_max.max(self.exit);
102 self.exit_min = self.exit_min.min(self.exit);
103 self.compute_sum();
104 }
105 pub(crate) fn finalize(&mut self, total_space_functions: usize) {
106 self.total_space_functions = total_space_functions;
107 }
108}
109
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.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.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.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.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.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!(
160 node.kind_id().into(),
161 Rust::ReturnExpression | Rust::TryExpression
162 ) || Self::is_func(node) && node.child_by_field_name("return_type").is_some()
163 {
164 stats.exit += 1;
165 }
166 }
167}
168
169impl Exit for CppCode {
170 fn compute(node: &Node, stats: &mut Stats) {
171 if matches!(node.kind_id().into(), Cpp::ReturnStatement) {
172 stats.exit += 1;
173 }
174 }
175}
176
177impl Exit for JavaCode {
178 fn compute(node: &Node, stats: &mut Stats) {
179 if matches!(node.kind_id().into(), Java::ReturnStatement) {
180 stats.exit += 1;
181 }
182 }
183}
184
185implement_metric_trait!(Exit, KotlinCode, PreprocCode, CcommentCode);
186
187#[cfg(test)]
188mod tests {
189 use crate::tools::check_metrics;
190
191 use super::*;
192
193 #[test]
194 fn python_no_exit() {
195 check_metrics::<PythonParser>("a = 42", "foo.py", |metric| {
196 insta::assert_json_snapshot!(
198 metric.nexits,
199 @r###"
200 {
201 "sum": 0.0,
202 "average": null,
203 "min": 0.0,
204 "max": 0.0
205 }"###
206 );
207 });
208 }
209
210 #[test]
211 fn rust_no_exit() {
212 check_metrics::<RustParser>("let a = 42;", "foo.rs", |metric| {
213 insta::assert_json_snapshot!(
215 metric.nexits,
216 @r###"
217 {
218 "sum": 0.0,
219 "average": null,
220 "min": 0.0,
221 "max": 0.0
222 }"###
223 );
224 });
225 }
226
227 #[test]
228 fn rust_question_mark() {
229 check_metrics::<RustParser>("let _ = a? + b? + c?;", "foo.rs", |metric| {
230 insta::assert_json_snapshot!(
232 metric.nexits,
233 @r###"
234 {
235 "sum": 3.0,
236 "average": null,
237 "min": 3.0,
238 "max": 3.0
239 }"###
240 );
241 });
242 }
243
244 #[test]
245 fn c_no_exit() {
246 check_metrics::<CppParser>("int a = 42;", "foo.c", |metric| {
247 insta::assert_json_snapshot!(
249 metric.nexits,
250 @r###"
251 {
252 "sum": 0.0,
253 "average": null,
254 "min": 0.0,
255 "max": 0.0
256 }"###
257 );
258 });
259 }
260
261 #[test]
262 fn javascript_no_exit() {
263 check_metrics::<JavascriptParser>("var a = 42;", "foo.js", |metric| {
264 insta::assert_json_snapshot!(
266 metric.nexits,
267 @r###"
268 {
269 "sum": 0.0,
270 "average": null,
271 "min": 0.0,
272 "max": 0.0
273 }"###
274 );
275 });
276 }
277
278 #[test]
279 fn python_simple_function() {
280 check_metrics::<PythonParser>(
281 "def f(a, b):
282 if a:
283 return a",
284 "foo.py",
285 |metric| {
286 println!("{:?}", metric.nexits);
287 insta::assert_json_snapshot!(
289 metric.nexits,
290 @r###"
291 {
292 "sum": 1.0,
293 "average": 1.0,
294 "min": 0.0,
295 "max": 1.0
296 }"###
297 );
298 },
299 );
300 }
301
302 #[test]
303 fn python_more_functions() {
304 check_metrics::<PythonParser>(
305 "def f(a, b):
306 if a:
307 return a
308 def f(a, b):
309 if b:
310 return b",
311 "foo.py",
312 |metric| {
313 insta::assert_json_snapshot!(
315 metric.nexits,
316 @r#"
317 {
318 "sum": 2.0,
319 "average": 1.0,
320 "min": 0.0,
321 "max": 2.0
322 }
323 "#
324 );
325 },
326 );
327 }
328
329 #[test]
330 fn python_nested_functions() {
331 check_metrics::<PythonParser>(
332 "def f(a, b):
333 def foo(a):
334 if a:
335 return 1
336 bar = lambda a: lambda b: b or True or True
337 return bar(foo(a))(a)",
338 "foo.py",
339 |metric| {
340 insta::assert_json_snapshot!(
342 metric.nexits,
343 @r#"
344 {
345 "sum": 2.0,
346 "average": 1.0,
347 "min": 0.0,
348 "max": 2.0
349 }
350 "#
351 );
352 },
353 );
354 }
355
356 #[test]
357 fn java_no_exit() {
358 check_metrics::<JavaParser>("int a = 42;", "foo.java", |metric| {
359 insta::assert_json_snapshot!(
361 metric.nexits,
362 @r###"
363 {
364 "sum": 0.0,
365 "average": null,
366 "min": 0.0,
367 "max": 0.0
368 }"###
369 );
370 });
371 }
372
373 #[test]
374 fn java_simple_function() {
375 check_metrics::<JavaParser>(
376 "class A {
377 public int sum(int x, int y) {
378 return x + y;
379 }
380 }",
381 "foo.java",
382 |metric| {
383 insta::assert_json_snapshot!(
385 metric.nexits,
386 @r###"
387 {
388 "sum": 1.0,
389 "average": 1.0,
390 "min": 0.0,
391 "max": 1.0
392 }"###
393 );
394 },
395 );
396 }
397
398 #[test]
399 fn java_split_function() {
400 check_metrics::<JavaParser>(
401 "class A {
402 public int multiply(int x, int y) {
403 if(x == 0 || y == 0){
404 return 0;
405 }
406 return x * y;
407 }
408 }",
409 "foo.java",
410 |metric| {
411 insta::assert_json_snapshot!(
413 metric.nexits,
414 @r###"
415 {
416 "sum": 2.0,
417 "average": 2.0,
418 "min": 0.0,
419 "max": 2.0
420 }"###
421 );
422 },
423 );
424 }
425}