From 2c46ab8744abd1c148625eb75a92cdecd6755b34 Mon Sep 17 00:00:00 2001 From: giovannitangredi Date: Tue, 23 Nov 2021 13:16:14 +0100 Subject: [PATCH 1/2] Add cyclomatic_sum field, fix issues related to this change --- src/metrics/cyclomatic.rs | 113 +++++++++++++++++++++++++++++++++----- src/metrics/mi.rs | 2 +- src/spaces.rs | 6 ++ 3 files changed, 105 insertions(+), 16 deletions(-) diff --git a/src/metrics/cyclomatic.rs b/src/metrics/cyclomatic.rs index 93182e248..91ad89959 100644 --- a/src/metrics/cyclomatic.rs +++ b/src/metrics/cyclomatic.rs @@ -8,6 +8,7 @@ use crate::*; /// The `Cyclomatic` metric. #[derive(Debug, Clone)] pub struct Stats { + cyclomatic_sum: f64, cyclomatic: f64, n: usize, } @@ -15,6 +16,7 @@ pub struct Stats { impl Default for Stats { fn default() -> Self { Self { + cyclomatic_sum: 0., cyclomatic: 1., n: 1, } @@ -27,7 +29,7 @@ impl Serialize for Stats { S: Serializer, { let mut st = serializer.serialize_struct("cyclomatic", 2)?; - st.serialize_field("sum", &self.cyclomatic())?; + st.serialize_field("sum", &self.cyclomatic_sum())?; st.serialize_field("average", &self.cyclomatic_average())?; st.end() } @@ -38,8 +40,8 @@ impl fmt::Display for Stats { write!( f, "sum: {}, average: {}", - self.cyclomatic(), - self.cyclomatic_average() + self.cyclomatic_sum(), + self.cyclomatic_average(), ) } } @@ -47,7 +49,7 @@ impl fmt::Display for Stats { impl Stats { /// Merges a second `Cyclomatic` metric into the first one pub fn merge(&mut self, other: &Stats) { - self.cyclomatic += other.cyclomatic; + self.cyclomatic_sum += other.cyclomatic_sum; self.n += other.n; } @@ -55,13 +57,22 @@ impl Stats { pub fn cyclomatic(&self) -> f64 { self.cyclomatic } + /// Returns the sum + pub fn cyclomatic_sum(&self) -> f64 { + self.cyclomatic_sum + } /// Returns the `Cyclomatic` metric average value /// /// This value is computed dividing the `Cyclomatic` value for the /// number of spaces. pub fn cyclomatic_average(&self) -> f64 { - self.cyclomatic() / self.n as f64 + self.cyclomatic_sum() / self.n as f64 + } + + /// Last step for updating cyclomatic_sum + pub fn compute_sum(&mut self) { + self.cyclomatic_sum += self.cyclomatic; } } @@ -190,9 +201,9 @@ mod tests { "foo.py", PythonParser, cyclomatic, - [(cyclomatic, 6, usize)], + [(cyclomatic_sum, 6, usize)], [ - (cyclomatic_average, 3.0) // nspace = 2 (func and unit) + (cyclomatic_average, 3.0), // nspace = 2 (func and unit) ] ); } @@ -207,9 +218,9 @@ mod tests { "foo.py", PythonParser, cyclomatic, - [(cyclomatic, 4, usize)], + [(cyclomatic_sum, 4, usize)], [ - (cyclomatic_average, 2.0) // nspace = 2 (func and unit) + (cyclomatic_average, 2.0), // nspace = 2 (func and unit) ] ); } @@ -228,9 +239,9 @@ mod tests { "foo.rs", RustParser, cyclomatic, - [(cyclomatic, 5, usize)], + [(cyclomatic_sum, 5, usize)], [ - (cyclomatic_average, 2.5) // nspace = 2 (func and unit) + (cyclomatic_average, 2.5), // nspace = 2 (func and unit) ] ); } @@ -257,9 +268,9 @@ mod tests { "foo.c", CppParser, cyclomatic, - [(cyclomatic, 5, usize)], + [(cyclomatic_sum, 5, usize)], [ - (cyclomatic_average, 2.5) // nspace = 2 (func and unit) + (cyclomatic_average, 2.5), // nspace = 2 (func and unit) ] ); } @@ -282,9 +293,81 @@ mod tests { "foo.c", CppParser, cyclomatic, - [(cyclomatic, 5, usize)], + [(cyclomatic_sum, 5, usize)], + [ + (cyclomatic_average, 2.5), // nspace = 2 (func and unit) + ] + ); + } + #[test] + fn c_unit_before() { + check_metrics!( + " + int a=42; + if(a==42) //+2(+1 unit space) + { + + } + if(a==34) //+1 + { + + } + int sumOfPrimes(int max) { // +1 + int total = 0; + OUT: for (int i = 1; i <= max; ++i) { // +1 + for (int j = 2; j < i; ++j) { // +1 + if (i % j == 0) { // +1 + continue OUT; + } + } + total += i; + } + return total; + }", + "foo.c", + CppParser, + cyclomatic, + [(cyclomatic_sum, 7, usize)], + [ + (cyclomatic_average, 3.5), // nspace = 2 (func and unit) + ] + ); + } + /// Test to handle the case of min and max when merge happen before the final value of one module are setted. + /// In this case the min value should be 3 because the unit space has 2 branches and a complexity of 3 + /// while the function sumOfPrimes has a complexity of 4. + #[test] + fn c_unit_after() { + check_metrics!( + " + int sumOfPrimes(int max) { // +1 + int total = 0; + OUT: for (int i = 1; i <= max; ++i) { // +1 + for (int j = 2; j < i; ++j) { // +1 + if (i % j == 0) { // +1 + continue OUT; + } + } + total += i; + } + return total; + } + + int a=42; + if(a==42) //+2(+1 unit space) + { + + } + if(a==34) //+1 + { + + }", + "foo.c", + CppParser, + cyclomatic, + [(cyclomatic_sum, 7, usize)], [ - (cyclomatic_average, 2.5) // nspace = 2 (func and unit) + (cyclomatic_average, 3.5), // nspace = 2 (func and unit) ] ); } diff --git a/src/metrics/mi.rs b/src/metrics/mi.rs index 0bd45a88f..0a8e2da9a 100644 --- a/src/metrics/mi.rs +++ b/src/metrics/mi.rs @@ -96,7 +96,7 @@ where stats.halstead_length = halstead.length(); stats.halstead_vocabulary = halstead.vocabulary(); stats.halstead_volume = halstead.volume(); - stats.cyclomatic = cyclomatic.cyclomatic(); + stats.cyclomatic = cyclomatic.cyclomatic_sum(); stats.sloc = loc.sloc(); stats.comments_percentage = loc.cloc() / stats.sloc; } diff --git a/src/spaces.rs b/src/spaces.rs index 2f9455623..ccbd14aaf 100644 --- a/src/spaces.rs +++ b/src/spaces.rs @@ -195,6 +195,10 @@ fn compute_averages(state: &mut State) { .nargs .finalize(nom_functions, nom_closures); } +#[inline(always)] +fn compute_sums(state: &mut State) { + state.space.metrics.cyclomatic.compute_sum(); +} fn finalize(state_stack: &mut Vec, diff_level: usize) { if state_stack.is_empty() { @@ -203,11 +207,13 @@ fn finalize(state_stack: &mut Vec, diff_level: usize) { for _ in 0..diff_level { if state_stack.len() == 1 { let mut last_state = state_stack.last_mut().unwrap(); + compute_sums(&mut last_state); compute_halstead_and_mi::(&mut last_state); compute_averages(&mut last_state); break; } else { let mut state = state_stack.pop().unwrap(); + compute_sums(&mut state); compute_halstead_and_mi::(&mut state); compute_averages(&mut state); From 9b11d67929994ebcb04a1a2bb14dd009fae8cf97 Mon Sep 17 00:00:00 2001 From: giovannitangredi Date: Tue, 23 Nov 2021 13:20:44 +0100 Subject: [PATCH 2/2] Add implementation for min and max in cyclomatic metric --- rust-code-analysis-web/src/web/server.rs | 10 +++--- src/metrics/cyclomatic.rs | 43 +++++++++++++++++++++--- src/spaces.rs | 8 ++--- 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/rust-code-analysis-web/src/web/server.rs b/rust-code-analysis-web/src/web/server.rs index 77f4db012..9039983fa 100644 --- a/rust-code-analysis-web/src/web/server.rs +++ b/rust-code-analysis-web/src/web/server.rs @@ -631,7 +631,7 @@ mod tests { "spaces": {"kind": "unit", "start_line": 1, "end_line": 4, - "metrics": {"cyclomatic": {"sum": 2.0, "average": 1.0}, + "metrics": {"cyclomatic": {"sum": 2.0, "average": 1.0, "min":1.0, "max":1.0}, "cognitive": {"sum": 0.0, "average": 0.0}, "nargs": {"total_functions": 0.0, "average_functions": 0.0, "total_closures": 0.0, "average_closures": 0.0, "total": 0.0, "average": 0.0}, "nexits": {"sum": 0.0, "average": 0.0}, @@ -658,7 +658,7 @@ mod tests { "spaces": [{"kind": "function", "start_line": 3, "end_line": 4, - "metrics": {"cyclomatic": {"sum": 1.0, "average": 1.0}, + "metrics": {"cyclomatic": {"sum": 1.0, "average": 1.0, "min":1.0, "max":1.0}, "cognitive": {"sum": 0.0, "average": 0.0}, "nargs": {"total_functions": 0.0, "average_functions": 0.0, "total_closures": 0.0, "average_closures": 0.0, "total": 0.0, "average": 0.0}, "nexits": {"sum": 0.0, "average": 0.0}, @@ -711,7 +711,7 @@ mod tests { "spaces": {"kind": "unit", "start_line": 1, "end_line": 2, - "metrics": {"cyclomatic": {"sum": 2.0, "average": 1.0}, + "metrics": {"cyclomatic": {"sum": 2.0, "average": 1.0, "min":1.0, "max":1.0}, "cognitive": {"sum": 0.0, "average": 0.0}, "nargs": {"total_functions": 0.0, "average_functions": 0.0, "total_closures": 0.0, "average_closures": 0.0, "total": 0.0, "average": 0.0}, "nexits": {"sum": 0.0, "average": 0.0}, @@ -760,7 +760,7 @@ mod tests { "spaces": {"kind": "unit", "start_line": 1, "end_line": 2, - "metrics": {"cyclomatic": {"sum": 2.0, "average": 1.0}, + "metrics": {"cyclomatic": {"sum": 2.0, "average": 1.0, "min": 1.0,"max": 1.0}, "cognitive": {"sum": 0.0, "average": 0.0}, "nargs": {"total_functions": 0.0, "average_functions": 0.0, "total_closures": 0.0, "average_closures": 0.0, "total": 0.0, "average": 0.0}, "nexits": {"sum": 0.0, "average": 0.0}, @@ -787,7 +787,7 @@ mod tests { "spaces": [{"kind": "function", "start_line": 1, "end_line": 2, - "metrics": {"cyclomatic": {"sum": 1.0, "average": 1.0}, + "metrics": {"cyclomatic": {"sum": 1.0, "average": 1.0, "min": 1.0,"max": 1.0}, "cognitive": {"sum": 0.0, "average": 0.0}, "nargs": {"total_functions": 0.0, "average_functions": 0.0, "total_closures": 0.0, "average_closures": 0.0, "total": 0.0, "average": 0.0}, "nexits": {"sum": 0.0, "average": 0.0}, diff --git a/src/metrics/cyclomatic.rs b/src/metrics/cyclomatic.rs index 91ad89959..1e5922972 100644 --- a/src/metrics/cyclomatic.rs +++ b/src/metrics/cyclomatic.rs @@ -11,6 +11,8 @@ pub struct Stats { cyclomatic_sum: f64, cyclomatic: f64, n: usize, + cyclomatic_max: f64, + cyclomatic_min: f64, } impl Default for Stats { @@ -19,6 +21,8 @@ impl Default for Stats { cyclomatic_sum: 0., cyclomatic: 1., n: 1, + cyclomatic_max: 0., + cyclomatic_min: f64::MAX, } } } @@ -31,6 +35,8 @@ impl Serialize for Stats { let mut st = serializer.serialize_struct("cyclomatic", 2)?; st.serialize_field("sum", &self.cyclomatic_sum())?; st.serialize_field("average", &self.cyclomatic_average())?; + st.serialize_field("min", &self.cyclomatic_min())?; + st.serialize_field("max", &self.cyclomatic_max())?; st.end() } } @@ -39,9 +45,11 @@ impl fmt::Display for Stats { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, - "sum: {}, average: {}", + "sum: {}, average: {}, min: {}, max: {}", self.cyclomatic_sum(), self.cyclomatic_average(), + self.cyclomatic_min(), + self.cyclomatic_max() ) } } @@ -49,6 +57,10 @@ impl fmt::Display for Stats { impl Stats { /// Merges a second `Cyclomatic` metric into the first one pub fn merge(&mut self, other: &Stats) { + //Calculate minimum and maximum values + self.cyclomatic_max = self.cyclomatic_max.max(other.cyclomatic_max); + self.cyclomatic_min = self.cyclomatic_min.min(other.cyclomatic_min); + self.cyclomatic_sum += other.cyclomatic_sum; self.n += other.n; } @@ -69,9 +81,18 @@ impl Stats { pub fn cyclomatic_average(&self) -> f64 { self.cyclomatic_sum() / self.n as f64 } - - /// Last step for updating cyclomatic_sum - pub fn compute_sum(&mut self) { + /// Returns the `Cyclomatic` maximum value + pub fn cyclomatic_max(&self) -> f64 { + self.cyclomatic_max + } + /// Returns the `Cyclomatic` minimum value + pub fn cyclomatic_min(&self) -> f64 { + self.cyclomatic_min + } + /// Last step for computing minimum and maximum value and update cyclomatic_sum + pub fn compute_minmax(&mut self) { + self.cyclomatic_max = self.cyclomatic_max.max(self.cyclomatic); + self.cyclomatic_min = self.cyclomatic_min.min(self.cyclomatic); self.cyclomatic_sum += self.cyclomatic; } } @@ -204,6 +225,8 @@ mod tests { [(cyclomatic_sum, 6, usize)], [ (cyclomatic_average, 3.0), // nspace = 2 (func and unit) + (cyclomatic_max, 5.0), + (cyclomatic_min, 1.0) ] ); } @@ -221,6 +244,8 @@ mod tests { [(cyclomatic_sum, 4, usize)], [ (cyclomatic_average, 2.0), // nspace = 2 (func and unit) + (cyclomatic_max, 3.0), + (cyclomatic_min, 1.0) ] ); } @@ -242,6 +267,8 @@ mod tests { [(cyclomatic_sum, 5, usize)], [ (cyclomatic_average, 2.5), // nspace = 2 (func and unit) + (cyclomatic_max, 4.0), + (cyclomatic_min, 1.0) ] ); } @@ -271,6 +298,8 @@ mod tests { [(cyclomatic_sum, 5, usize)], [ (cyclomatic_average, 2.5), // nspace = 2 (func and unit) + (cyclomatic_max, 4.0), + (cyclomatic_min, 1.0) ] ); } @@ -296,6 +325,8 @@ mod tests { [(cyclomatic_sum, 5, usize)], [ (cyclomatic_average, 2.5), // nspace = 2 (func and unit) + (cyclomatic_max, 4.0), + (cyclomatic_min, 1.0) ] ); } @@ -330,6 +361,8 @@ mod tests { [(cyclomatic_sum, 7, usize)], [ (cyclomatic_average, 3.5), // nspace = 2 (func and unit) + (cyclomatic_max, 4.0), + (cyclomatic_min, 3.0) ] ); } @@ -368,6 +401,8 @@ mod tests { [(cyclomatic_sum, 7, usize)], [ (cyclomatic_average, 3.5), // nspace = 2 (func and unit) + (cyclomatic_max, 4.0), + (cyclomatic_min, 3.0) ] ); } diff --git a/src/spaces.rs b/src/spaces.rs index ccbd14aaf..e69349b8d 100644 --- a/src/spaces.rs +++ b/src/spaces.rs @@ -196,8 +196,8 @@ fn compute_averages(state: &mut State) { .finalize(nom_functions, nom_closures); } #[inline(always)] -fn compute_sums(state: &mut State) { - state.space.metrics.cyclomatic.compute_sum(); +fn compute_minmax(state: &mut State) { + state.space.metrics.cyclomatic.compute_minmax(); } fn finalize(state_stack: &mut Vec, diff_level: usize) { @@ -207,13 +207,13 @@ fn finalize(state_stack: &mut Vec, diff_level: usize) { for _ in 0..diff_level { if state_stack.len() == 1 { let mut last_state = state_stack.last_mut().unwrap(); - compute_sums(&mut last_state); + compute_minmax(&mut last_state); compute_halstead_and_mi::(&mut last_state); compute_averages(&mut last_state); break; } else { let mut state = state_stack.pop().unwrap(); - compute_sums(&mut state); + compute_minmax(&mut state); compute_halstead_and_mi::(&mut state); compute_averages(&mut state);