Skip to content

Commit cd64237

Browse files
Add implementation of min and max for Cyclomatic
1 parent a507c7d commit cd64237

File tree

2 files changed

+140
-10
lines changed

2 files changed

+140
-10
lines changed

src/metrics/cyclomatic.rs

Lines changed: 135 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,27 @@
1+
use crate::checker::Checker;
2+
use crate::*;
13
use serde::ser::{SerializeStruct, Serializer};
24
use serde::Serialize;
35
use std::fmt;
46

5-
use crate::checker::Checker;
6-
use crate::*;
7-
87
/// The `Cyclomatic` metric.
98
#[derive(Debug, Clone)]
109
pub struct Stats {
10+
self_cyclomatic: f64,
1111
cyclomatic: f64,
1212
n: usize,
13+
cyclomatic_max: f64,
14+
cyclomatic_min: f64,
1315
}
1416

1517
impl Default for Stats {
1618
fn default() -> Self {
1719
Self {
20+
self_cyclomatic: 1.,
1821
cyclomatic: 1.,
1922
n: 1,
23+
cyclomatic_max: 0.,
24+
cyclomatic_min: f64::MAX,
2025
}
2126
}
2227
}
@@ -29,6 +34,8 @@ impl Serialize for Stats {
2934
let mut st = serializer.serialize_struct("cyclomatic", 2)?;
3035
st.serialize_field("sum", &self.cyclomatic())?;
3136
st.serialize_field("average", &self.cyclomatic_average())?;
37+
st.serialize_field("min", &self.cyclomatic_min())?;
38+
st.serialize_field("max", &self.cyclomatic_max())?;
3239
st.end()
3340
}
3441
}
@@ -37,9 +44,11 @@ impl fmt::Display for Stats {
3744
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
3845
write!(
3946
f,
40-
"sum: {}, average: {}",
47+
"sum: {}, average: {}, min: {}, max: {}",
4148
self.cyclomatic(),
42-
self.cyclomatic_average()
49+
self.cyclomatic_average(),
50+
self.cyclomatic_min(),
51+
self.cyclomatic_max()
4352
)
4453
}
4554
}
@@ -49,6 +58,17 @@ impl Stats {
4958
pub fn merge(&mut self, other: &Stats) {
5059
self.cyclomatic += other.cyclomatic;
5160
self.n += other.n;
61+
62+
//Calculate minimum and maximum values
63+
if other.n == 1 {
64+
self.cyclomatic_max = self.cyclomatic_max.max(other.cyclomatic);
65+
self.cyclomatic_min = self.cyclomatic_min.min(other.cyclomatic);
66+
} else {
67+
self.cyclomatic_max = self.cyclomatic_max.max(other.cyclomatic_max);
68+
self.cyclomatic_min = self.cyclomatic_min.min(other.cyclomatic_min);
69+
self.cyclomatic_max = self.cyclomatic_max.max(other.self_cyclomatic);
70+
self.cyclomatic_min = self.cyclomatic_min.min(other.self_cyclomatic);
71+
}
5272
}
5373

5474
/// Returns the `Cyclomatic` metric value
@@ -63,6 +83,19 @@ impl Stats {
6383
pub fn cyclomatic_average(&self) -> f64 {
6484
self.cyclomatic() / self.n as f64
6585
}
86+
/// Returns the `Cyclomatic` maximum value
87+
pub fn cyclomatic_max(&self) -> f64 {
88+
self.cyclomatic_max
89+
}
90+
/// Returns the `Cyclomatic` minimum value
91+
pub fn cyclomatic_min(&self) -> f64 {
92+
self.cyclomatic_min
93+
}
94+
/// Last step for computing minimum and maximum valus
95+
pub fn compute_minmax(&mut self) {
96+
self.cyclomatic_max = self.cyclomatic_max.max(self.self_cyclomatic);
97+
self.cyclomatic_min = self.cyclomatic_min.min(self.self_cyclomatic);
98+
}
6699
}
67100

68101
#[doc(hidden)]
@@ -80,10 +113,12 @@ impl Cyclomatic for PythonCode {
80113
match node.object().kind_id().into() {
81114
If | Elif | For | While | Except | With | Assert | And | Or => {
82115
stats.cyclomatic += 1.;
116+
stats.self_cyclomatic += 1.;
83117
}
84118
Else => {
85119
if has_ancestors!(node, ForStatement | WhileStatement, ElseClause) {
86120
stats.cyclomatic += 1.;
121+
stats.self_cyclomatic += 1.;
87122
}
88123
}
89124
_ => {}
@@ -98,6 +133,7 @@ impl Cyclomatic for MozjsCode {
98133
match node.object().kind_id().into() {
99134
If | For | While | Case | Catch | TernaryExpression | AMPAMP | PIPEPIPE => {
100135
stats.cyclomatic += 1.;
136+
stats.self_cyclomatic += 1.;
101137
}
102138
_ => {}
103139
}
@@ -111,6 +147,7 @@ impl Cyclomatic for JavascriptCode {
111147
match node.object().kind_id().into() {
112148
If | For | While | Case | Catch | TernaryExpression | AMPAMP | PIPEPIPE => {
113149
stats.cyclomatic += 1.;
150+
stats.self_cyclomatic += 1.;
114151
}
115152
_ => {}
116153
}
@@ -124,6 +161,7 @@ impl Cyclomatic for TypescriptCode {
124161
match node.object().kind_id().into() {
125162
If | For | While | Case | Catch | TernaryExpression | AMPAMP | PIPEPIPE => {
126163
stats.cyclomatic += 1.;
164+
stats.self_cyclomatic += 1.;
127165
}
128166
_ => {}
129167
}
@@ -137,6 +175,7 @@ impl Cyclomatic for TsxCode {
137175
match node.object().kind_id().into() {
138176
If | For | While | Case | Catch | TernaryExpression | AMPAMP | PIPEPIPE => {
139177
stats.cyclomatic += 1.;
178+
stats.self_cyclomatic += 1.;
140179
}
141180
_ => {}
142181
}
@@ -150,6 +189,7 @@ impl Cyclomatic for RustCode {
150189
match node.object().kind_id().into() {
151190
If | For | While | Loop | MatchArm | MatchArm2 | QMARK | AMPAMP | PIPEPIPE => {
152191
stats.cyclomatic += 1.;
192+
stats.self_cyclomatic += 1.;
153193
}
154194
_ => {}
155195
}
@@ -163,6 +203,7 @@ impl Cyclomatic for CppCode {
163203
match node.object().kind_id().into() {
164204
If | For | While | Case | Catch | ConditionalExpression | AMPAMP | PIPEPIPE => {
165205
stats.cyclomatic += 1.;
206+
stats.self_cyclomatic += 1.;
166207
}
167208
_ => {}
168209
}
@@ -192,7 +233,9 @@ mod tests {
192233
cyclomatic,
193234
[(cyclomatic, 6, usize)],
194235
[
195-
(cyclomatic_average, 3.0) // nspace = 2 (func and unit)
236+
(cyclomatic_average, 3.0), // nspace = 2 (func and unit)
237+
(cyclomatic_max, 5.0),
238+
(cyclomatic_min, 1.0)
196239
]
197240
);
198241
}
@@ -209,7 +252,9 @@ mod tests {
209252
cyclomatic,
210253
[(cyclomatic, 4, usize)],
211254
[
212-
(cyclomatic_average, 2.0) // nspace = 2 (func and unit)
255+
(cyclomatic_average, 2.0), // nspace = 2 (func and unit)
256+
(cyclomatic_max, 3.0),
257+
(cyclomatic_min, 1.0)
213258
]
214259
);
215260
}
@@ -230,7 +275,9 @@ mod tests {
230275
cyclomatic,
231276
[(cyclomatic, 5, usize)],
232277
[
233-
(cyclomatic_average, 2.5) // nspace = 2 (func and unit)
278+
(cyclomatic_average, 2.5), // nspace = 2 (func and unit)
279+
(cyclomatic_max, 4.0),
280+
(cyclomatic_min, 1.0)
234281
]
235282
);
236283
}
@@ -259,7 +306,9 @@ mod tests {
259306
cyclomatic,
260307
[(cyclomatic, 5, usize)],
261308
[
262-
(cyclomatic_average, 2.5) // nspace = 2 (func and unit)
309+
(cyclomatic_average, 2.5), // nspace = 2 (func and unit)
310+
(cyclomatic_max, 4.0),
311+
(cyclomatic_min, 1.0)
263312
]
264313
);
265314
}
@@ -284,7 +333,83 @@ mod tests {
284333
cyclomatic,
285334
[(cyclomatic, 5, usize)],
286335
[
287-
(cyclomatic_average, 2.5) // nspace = 2 (func and unit)
336+
(cyclomatic_average, 2.5), // nspace = 2 (func and unit)
337+
(cyclomatic_max, 4.0),
338+
(cyclomatic_min, 1.0)
339+
]
340+
);
341+
}
342+
#[test]
343+
fn c_unit_before() {
344+
check_metrics!(
345+
"
346+
int a=42;
347+
if(a==42) //+2(+1 unit space)
348+
{
349+
350+
}
351+
if(a==34) //+1
352+
{
353+
354+
}
355+
int sumOfPrimes(int max) { // +1
356+
int total = 0;
357+
OUT: for (int i = 1; i <= max; ++i) { // +1
358+
for (int j = 2; j < i; ++j) { // +1
359+
if (i % j == 0) { // +1
360+
continue OUT;
361+
}
362+
}
363+
total += i;
364+
}
365+
return total;
366+
}",
367+
"foo.c",
368+
CppParser,
369+
cyclomatic,
370+
[(cyclomatic, 7, usize)],
371+
[
372+
(cyclomatic_average, 3.5), // nspace = 2 (func and unit)
373+
(cyclomatic_max, 4.0),
374+
(cyclomatic_min, 3.0)
375+
]
376+
);
377+
}
378+
379+
#[test]
380+
fn c_unit_after() {
381+
check_metrics!(
382+
"
383+
int sumOfPrimes(int max) { // +1
384+
int total = 0;
385+
OUT: for (int i = 1; i <= max; ++i) { // +1
386+
for (int j = 2; j < i; ++j) { // +1
387+
if (i % j == 0) { // +1
388+
continue OUT;
389+
}
390+
}
391+
total += i;
392+
}
393+
return total;
394+
}
395+
396+
int a=42;
397+
if(a==42) //+2(+1 unit space)
398+
{
399+
400+
}
401+
if(a==34) //+1
402+
{
403+
404+
}",
405+
"foo.c",
406+
CppParser,
407+
cyclomatic,
408+
[(cyclomatic, 7, usize)],
409+
[
410+
(cyclomatic_average, 3.5), // nspace = 2 (func and unit)
411+
(cyclomatic_max, 4.0),
412+
(cyclomatic_min, 3.0)
288413
]
289414
);
290415
}

src/spaces.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,10 @@ fn compute_averages(state: &mut State) {
195195
.nargs
196196
.finalize(nom_functions, nom_closures);
197197
}
198+
#[inline(always)]
199+
fn compute_minmax(state: &mut State) {
200+
state.space.metrics.cyclomatic.compute_minmax();
201+
}
198202

199203
fn finalize<T: ParserTrait>(state_stack: &mut Vec<State>, diff_level: usize) {
200204
if state_stack.is_empty() {
@@ -205,6 +209,7 @@ fn finalize<T: ParserTrait>(state_stack: &mut Vec<State>, diff_level: usize) {
205209
let mut last_state = state_stack.last_mut().unwrap();
206210
compute_halstead_and_mi::<T>(&mut last_state);
207211
compute_averages(&mut last_state);
212+
compute_minmax(&mut last_state);
208213
break;
209214
} else {
210215
let mut state = state_stack.pop().unwrap();

0 commit comments

Comments
 (0)