diff --git a/rust-code-analysis-cli/src/web/server.rs b/rust-code-analysis-cli/src/web/server.rs index c636d44d0..f5e68b8ab 100644 --- a/rust-code-analysis-cli/src/web/server.rs +++ b/rust-code-analysis-cli/src/web/server.rs @@ -634,7 +634,7 @@ mod tests { "metrics": {"cyclomatic": {"sum": 2.0, "average": 1.0}, "cognitive": {"sum": 0.0, "average": 0.0}, "nargs": 0., - "nexits": 0., + "nexits": {"sum": 0.0, "average": 0.0}, "halstead": {"bugs": 0.000_942_552_557_372_941_4, "difficulty": 1.0, "effort": 4.754_887_502_163_468, @@ -661,7 +661,7 @@ mod tests { "metrics": {"cyclomatic": {"sum": 1.0, "average": 1.0}, "cognitive": {"sum": 0.0, "average": 0.0}, "nargs": 0., - "nexits": 0., + "nexits": {"sum": 0.0, "average": 0.0}, "halstead": {"bugs": 0.000_942_552_557_372_941_4, "difficulty": 1.0, "effort": 4.754_887_502_163_468, @@ -714,7 +714,7 @@ mod tests { "metrics": {"cyclomatic": {"sum": 2.0, "average": 1.0}, "cognitive": {"sum": 0.0, "average": 0.0}, "nargs": 0., - "nexits": 0., + "nexits": {"sum": 0.0, "average": 0.0}, "halstead": {"bugs": 0.000_942_552_557_372_941_4, "difficulty": 1.0, "effort": 4.754_887_502_163_468, @@ -763,7 +763,7 @@ mod tests { "metrics": {"cyclomatic": {"sum": 2.0, "average": 1.0}, "cognitive": {"sum": 0.0, "average": 0.0}, "nargs": 0., - "nexits": 0., + "nexits": {"sum": 0.0, "average": 0.0}, "halstead": {"bugs": 0.000_942_552_557_372_941_4, "difficulty": 1.0, "effort": 4.754_887_502_163_468, @@ -790,7 +790,7 @@ mod tests { "metrics": {"cyclomatic": {"sum": 1.0, "average": 1.0}, "cognitive": {"sum": 0.0, "average": 0.0}, "nargs": 0., - "nexits": 0., + "nexits": {"sum": 0.0, "average": 0.0}, "halstead": {"bugs": 0.000_942_552_557_372_941_4, "difficulty": 1.0, "effort": 4.754_887_502_163_468, diff --git a/src/metrics/exit.rs b/src/metrics/exit.rs index 4ed046d0f..9340df8e3 100644 --- a/src/metrics/exit.rs +++ b/src/metrics/exit.rs @@ -1,4 +1,4 @@ -use serde::ser::Serializer; +use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use std::fmt; @@ -12,11 +12,15 @@ use crate::*; #[derive(Debug, Clone)] pub struct Stats { exit: usize, + total_space_functions: usize, } impl Default for Stats { fn default() -> Self { - Self { exit: 0 } + Self { + exit: 0, + total_space_functions: 1, + } } } @@ -25,13 +29,16 @@ impl Serialize for Stats { where S: Serializer, { - serializer.serialize_f64(self.exit()) + let mut st = serializer.serialize_struct("nexits", 2)?; + st.serialize_field("sum", &self.exit())?; + st.serialize_field("average", &self.exit_average())?; + st.end() } } impl fmt::Display for Stats { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.exit) + write!(f, "sum: {}, average: {}", self.exit(), self.exit_average()) } } @@ -45,6 +52,18 @@ impl Stats { pub fn exit(&self) -> f64 { self.exit as f64 } + + /// Returns the `NExit` metric average value + /// + /// This value is computed dividing the `NExit` value + /// for the total number of functions/closures in a space. + pub fn exit_average(&self) -> f64 { + self.exit() / self.total_space_functions as f64 + } + + pub(crate) fn finalize(&mut self, total_space_functions: usize) { + self.total_space_functions = total_space_functions; + } } #[doc(hidden)] @@ -121,3 +140,58 @@ impl Exit for JavaCode {} impl Exit for GoCode {} impl Exit for CssCode {} impl Exit for HtmlCode {} + +#[cfg(test)] +mod tests { + use std::path::PathBuf; + + use super::*; + + #[test] + fn test_function_exit() { + check_metrics!( + "def f(a, b): + if a: + return a", + "foo.py", + PythonParser, + nexits, + [(exit, 1, usize)], + [(exit_average, 1.0)] // 1 function + ); + } + + #[test] + fn test_functions_exit() { + check_metrics!( + "def f(a, b): + if a: + return a + def f(a, b): + if b: + return b", + "foo.py", + PythonParser, + nexits, + [(exit, 2, usize)], + [(exit_average, 1.0)] // 2 functions + ); + } + + #[test] + fn test_nested_functions_exit() { + check_metrics!( + "def f(a, b): + def foo(a): + if a: + return 1 + bar = lambda a: lambda b: b or True or True + return bar(foo(a))(a)", + "foo.py", + PythonParser, + nexits, + [(exit, 2, usize)], + [(exit_average, 0.5)] // 2 functions + 2 lambdas = 4 + ); + } +} diff --git a/src/spaces.rs b/src/spaces.rs index eed789e9e..2befe9f62 100644 --- a/src/spaces.rs +++ b/src/spaces.rs @@ -180,12 +180,12 @@ fn compute_halstead_and_mi<'a, T: ParserTrait>(state: &mut State<'a>) { } #[inline(always)] -fn compute_average<'a>(state: &mut State<'a>) { - state - .space - .metrics - .cognitive - .finalize(state.space.metrics.nom.total() as usize); +fn compute_averages<'a>(state: &mut State<'a>) { + let nom_total = state.space.metrics.nom.total() as usize; + // Cognitive average + state.space.metrics.cognitive.finalize(nom_total); + // Nexit average + state.space.metrics.nexits.finalize(nom_total); } fn finalize<'a, T: ParserTrait>(state_stack: &mut Vec>, diff_level: usize) { @@ -196,12 +196,12 @@ fn finalize<'a, T: ParserTrait>(state_stack: &mut Vec>, diff_level: us if state_stack.len() == 1 { let mut last_state = state_stack.last_mut().unwrap(); compute_halstead_and_mi::(&mut last_state); - compute_average(&mut last_state); + compute_averages(&mut last_state); break; } else { let mut state = state_stack.pop().unwrap(); compute_halstead_and_mi::(&mut state); - compute_average(&mut state); + compute_averages(&mut state); let mut last_state = state_stack.last_mut().unwrap(); last_state.halstead_maps.merge(&state.halstead_maps);