Add min and max implementation for Cyclomatic (#699)
* Add cyclomatic_sum field, fix issues related to this change * Add implementation for min and max in cyclomatic metric
This commit is contained in:
Родитель
a507c7dba1
Коммит
a6fb78bc9e
|
@ -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},
|
||||
|
|
|
@ -8,15 +8,21 @@ use crate::*;
|
|||
/// The `Cyclomatic` metric.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Stats {
|
||||
cyclomatic_sum: f64,
|
||||
cyclomatic: f64,
|
||||
n: usize,
|
||||
cyclomatic_max: f64,
|
||||
cyclomatic_min: f64,
|
||||
}
|
||||
|
||||
impl Default for Stats {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
cyclomatic_sum: 0.,
|
||||
cyclomatic: 1.,
|
||||
n: 1,
|
||||
cyclomatic_max: 0.,
|
||||
cyclomatic_min: f64::MAX,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,8 +33,10 @@ 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.serialize_field("min", &self.cyclomatic_min())?;
|
||||
st.serialize_field("max", &self.cyclomatic_max())?;
|
||||
st.end()
|
||||
}
|
||||
}
|
||||
|
@ -37,9 +45,11 @@ impl fmt::Display for Stats {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"sum: {}, average: {}",
|
||||
self.cyclomatic(),
|
||||
self.cyclomatic_average()
|
||||
"sum: {}, average: {}, min: {}, max: {}",
|
||||
self.cyclomatic_sum(),
|
||||
self.cyclomatic_average(),
|
||||
self.cyclomatic_min(),
|
||||
self.cyclomatic_max()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -47,7 +57,11 @@ 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;
|
||||
//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;
|
||||
}
|
||||
|
||||
|
@ -55,13 +69,31 @@ 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
|
||||
}
|
||||
/// 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -190,9 +222,11 @@ 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)
|
||||
(cyclomatic_max, 5.0),
|
||||
(cyclomatic_min, 1.0)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -207,9 +241,11 @@ 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)
|
||||
(cyclomatic_max, 3.0),
|
||||
(cyclomatic_min, 1.0)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -228,9 +264,11 @@ 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)
|
||||
(cyclomatic_max, 4.0),
|
||||
(cyclomatic_min, 1.0)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -257,9 +295,11 @@ 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)
|
||||
(cyclomatic_max, 4.0),
|
||||
(cyclomatic_min, 1.0)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
@ -282,9 +322,87 @@ 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)
|
||||
(cyclomatic_max, 4.0),
|
||||
(cyclomatic_min, 1.0)
|
||||
]
|
||||
);
|
||||
}
|
||||
#[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)
|
||||
(cyclomatic_max, 4.0),
|
||||
(cyclomatic_min, 3.0)
|
||||
]
|
||||
);
|
||||
}
|
||||
/// 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, 3.5), // nspace = 2 (func and unit)
|
||||
(cyclomatic_max, 4.0),
|
||||
(cyclomatic_min, 3.0)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -195,6 +195,10 @@ fn compute_averages(state: &mut State) {
|
|||
.nargs
|
||||
.finalize(nom_functions, nom_closures);
|
||||
}
|
||||
#[inline(always)]
|
||||
fn compute_minmax(state: &mut State) {
|
||||
state.space.metrics.cyclomatic.compute_minmax();
|
||||
}
|
||||
|
||||
fn finalize<T: ParserTrait>(state_stack: &mut Vec<State>, diff_level: usize) {
|
||||
if state_stack.is_empty() {
|
||||
|
@ -203,11 +207,13 @@ fn finalize<T: ParserTrait>(state_stack: &mut Vec<State>, diff_level: usize) {
|
|||
for _ in 0..diff_level {
|
||||
if state_stack.len() == 1 {
|
||||
let mut last_state = state_stack.last_mut().unwrap();
|
||||
compute_minmax(&mut last_state);
|
||||
compute_halstead_and_mi::<T>(&mut last_state);
|
||||
compute_averages(&mut last_state);
|
||||
break;
|
||||
} else {
|
||||
let mut state = state_stack.pop().unwrap();
|
||||
compute_minmax(&mut state);
|
||||
compute_halstead_and_mi::<T>(&mut state);
|
||||
compute_averages(&mut state);
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче