rust_code_analysis/metrics/
wmc.rs1use serde::ser::{SerializeStruct, Serializer};
2use serde::Serialize;
3use std::fmt;
4
5use crate::checker::Checker;
6use crate::*;
7
8#[derive(Debug, Clone, Default)]
21pub struct Stats {
22 cyclomatic: f64,
23 class_wmc: f64,
24 interface_wmc: f64,
25 class_wmc_sum: f64,
26 interface_wmc_sum: f64,
27 space_kind: SpaceKind,
28}
29
30impl Serialize for Stats {
31 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
32 where
33 S: Serializer,
34 {
35 let mut st = serializer.serialize_struct("wmc", 3)?;
36 st.serialize_field("classes", &self.class_wmc_sum())?;
37 st.serialize_field("interfaces", &self.interface_wmc_sum())?;
38 st.serialize_field("total", &self.total_wmc())?;
39 st.end()
40 }
41}
42
43impl fmt::Display for Stats {
44 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45 write!(
46 f,
47 "classes: {}, interfaces: {}, total: {}",
48 self.class_wmc_sum(),
49 self.interface_wmc_sum(),
50 self.total_wmc()
51 )
52 }
53}
54
55impl Stats {
56 pub fn merge(&mut self, other: &Stats) {
58 use SpaceKind::*;
59
60 if let Function = other.space_kind {
63 match self.space_kind {
64 Class => self.class_wmc += other.cyclomatic,
65 Interface => self.interface_wmc += other.cyclomatic,
66 _ => {}
67 }
68 }
69
70 self.class_wmc_sum += other.class_wmc_sum;
71 self.interface_wmc_sum += other.interface_wmc_sum;
72 }
73
74 #[inline(always)]
76 pub fn class_wmc(&self) -> f64 {
77 self.class_wmc
78 }
79
80 #[inline(always)]
82 pub fn interface_wmc(&self) -> f64 {
83 self.interface_wmc
84 }
85
86 #[inline(always)]
88 pub fn class_wmc_sum(&self) -> f64 {
89 self.class_wmc_sum
90 }
91
92 #[inline(always)]
94 pub fn interface_wmc_sum(&self) -> f64 {
95 self.interface_wmc_sum
96 }
97
98 #[inline(always)]
100 pub fn total_wmc(&self) -> f64 {
101 self.class_wmc_sum() + self.interface_wmc_sum()
102 }
103
104 #[inline(always)]
107 pub(crate) fn compute_sum(&mut self) {
108 self.class_wmc_sum += self.class_wmc;
109 self.interface_wmc_sum += self.interface_wmc;
110 }
111
112 #[inline(always)]
114 pub(crate) fn is_disabled(&self) -> bool {
115 matches!(self.space_kind, SpaceKind::Function | SpaceKind::Unknown)
116 }
117}
118
119#[doc(hidden)]
120pub trait Wmc
121where
122 Self: Checker,
123{
124 fn compute(_space_kind: SpaceKind, _cyclomatic: &cyclomatic::Stats, _stats: &mut Stats) {}
125}
126
127impl Wmc for PythonCode {}
128impl Wmc for MozjsCode {}
129impl Wmc for JavascriptCode {}
130impl Wmc for TypescriptCode {}
131impl Wmc for TsxCode {}
132impl Wmc for RustCode {}
133impl Wmc for CppCode {}
134impl Wmc for PreprocCode {}
135impl Wmc for CcommentCode {}
136
137impl Wmc for JavaCode {
138 fn compute(space_kind: SpaceKind, cyclomatic: &cyclomatic::Stats, stats: &mut Stats) {
139 use SpaceKind::*;
140
141 if let Unit | Class | Interface | Function = space_kind {
142 if stats.space_kind == Unknown {
143 stats.space_kind = space_kind;
144 }
145 if space_kind == Function {
146 stats.cyclomatic = cyclomatic.cyclomatic_sum();
148 }
149 }
150 }
151}
152
153#[cfg(test)]
154mod tests {
155 use std::path::PathBuf;
156
157 use super::*;
158
159 #[test]
160 fn java_single_class() {
161 check_metrics!(
162 "public class Example { // wmc = 13
163
164 public boolean m1(boolean a, boolean b) { // +1
165 boolean r = false;
166 if (a && b == a || b) { // +3
167 r = true;
168 }
169 return r;
170 }
171
172 public boolean m2(int n) { // +1
173 for (int i = 0; i < n; i++) { // +1
174 int j = n;
175 while (j > i) { // +1
176 j--;
177 }
178 }
179 return (n % 2 == 0) ? true : false; // +1
180 }
181
182 public int m3(int x, int y, int z) { // +1
183 int ret;
184 try {
185 z = x/y + y/x;
186 } catch (ArithmeticException e) { // +1
187 z = (x == 0) ? -1 : -2; // +1
188 }
189 switch (z) {
190 case -1: // +1
191 ret = y * y;
192 break;
193 case -2: // +1
194 ret = x * x;
195 break;
196 default:
197 ret = x + y;
198 }
199 return ret;
200 }
201 }",
202 "foo.java",
203 JavaParser,
204 wmc,
205 [
206 (class_wmc_sum, 13.0), (interface_wmc_sum, 0.0),
208 (total_wmc, 13.0)
209 ]
210 );
211 }
212
213 #[test]
216 fn java_multiple_classes() {
217 check_metrics!(
218 "public class MainClass { // wmc = 3
219 private int a;
220 public MainClass() { // +1
221 a = 0;
222 }
223 public void setA(int n) { // +1
224 a = n;
225 }
226 public int getA() { // +1
227 return a;
228 }
229 }
230
231 class TopLevelClass { // wmc = 2
232 private int b;
233 public TopLevelClass() { // +1
234 b = 0;
235 }
236 public int getB() { // +1
237 return b;
238 }
239 }",
240 "foo.java",
241 JavaParser,
242 wmc,
243 [
244 (class_wmc_sum, 5.0), (interface_wmc_sum, 0.0),
246 (total_wmc, 5.0)
247 ]
248 );
249 }
250
251 #[test]
252 fn java_static_nested_class() {
253 check_metrics!(
254 "public class TopLevelClass { // wmc = 0
255 public static class StaticNestedClass { // wmc = 1
256 private void m() { // +1
257 System.out.println(\"Test\");
258 }
259 }
260 }",
261 "foo.java",
262 JavaParser,
263 wmc,
264 [
265 (class_wmc_sum, 1.0), (interface_wmc_sum, 0.0),
267 (total_wmc, 1.0)
268 ]
269 );
270 }
271
272 #[test]
273 fn java_nested_inner_classes() {
274 check_metrics!(
275 "public class TopLevelClass { // wmc = 2
276 private int a;
277
278 class InnerClassBefore { // wmc = 1
279 private boolean b = (a % 2 == 0) ? true : false;
280 public boolean getB() { // +1
281 return b;
282 }
283 }
284
285 public TopLevelClass(int n) { // +1
286 if (a != n) { // +1
287 a = n;
288 }
289 }
290
291 class InnerClassAfter { // wmc = 2
292 private int c = a;
293
294 public int getC() { // +1
295 return c;
296 }
297 public void setC(int n) { // +1
298 c = n;
299 }
300
301 class InnerClass1 { // wmc = 1
302 private int p1;
303 class InnerClass2 { // wmc = 1
304 private int p2;
305 public int getP2() { // +1
306 return p2;
307 }
308 class InnerClass3 { // wmc = 2
309 private int p3;
310 public int getP3() { // +1
311 return p3;
312 }
313 public void setP3(int n) { // +1
314 p3 = n;
315 }
316 }
317 }
318 public void setP1(int n) { // +1
319 p1 = n;
320 }
321 }
322 }
323 }",
324 "foo.java",
325 JavaParser,
326 wmc,
327 [
328 (class_wmc_sum, 9.0), (interface_wmc_sum, 0.0),
330 (total_wmc, 9.0)
331 ]
332 );
333 }
334
335 #[test]
336 fn java_local_inner_class() {
337 check_metrics!(
338 "import java.util.LinkedList;
339 import java.util.List;
340
341 public final class FinalClass { // wmc = 5
342 private int a = 1;
343 public void test() { // +1
344 final List<String> localList = new LinkedList<String>();
345
346 class LocalInnerClass { // +1, wmc = 2
347 private int b = (a == 1) ? 1 : 0; // +1
348 public void print() { // +1
349 for ( String s : localList ) { // +1
350 System.out.println(s);
351 }
352 }
353 }
354 }
355 }",
356 "foo.java",
357 JavaParser,
358 wmc,
359 [
360 (class_wmc_sum, 7.0), (interface_wmc_sum, 0.0),
362 (total_wmc, 7.0)
363 ]
364 );
365 }
366
367 #[test]
368 fn java_anonymous_inner_class() {
369 check_metrics!(
370 "abstract class AbstractClass { // wmc = 1
371 abstract void m1(); // +1
372 }
373 public class TopLevelClass{ // wmc = 3
374 public void m(){ // +1
375 AbstractClass ac1 = new AbstractClass() {
376 void m1() { // +1
377 for (int i = 0; i < 5; i++) { // +1
378 System.out.println(\"Test 1: \" + i);
379 }
380 }
381 };
382 ac1.m1();
383 }
384 }",
385 "foo.java",
386 JavaParser,
387 wmc,
388 [
389 (class_wmc_sum, 4.0), (interface_wmc_sum, 0.0),
391 (total_wmc, 4.0)
392 ]
393 );
394 }
395
396 #[test]
397 fn java_nested_anonymous_inner_classes() {
398 check_metrics!(
399 "abstract class AbstractClass{ // wmc = 2
400 abstract void m1(); // +1
401 abstract void m2(); // +1
402 }
403 public class TopLevelClass{ // wmc = 6
404 public void m(){ // +1
405
406 AbstractClass ac1 = new AbstractClass() {
407 void m1() { // +1
408 for (int i = 0; i < 5; i++) { // +1
409 System.out.println(\"Test 1: \" + i);
410 }
411 }
412 void m2() { // +1
413 AbstractClass ac2 = new AbstractClass() {
414 void m1() { // +1
415 System.out.println(\"Test A\");
416 }
417 void m2() { // +1
418 System.out.println(\"Test B\");
419 }
420 };
421 ac2.m2();
422 System.out.println(\"Test 2\");
423 }
424 };
425 ac1.m1();
426 }
427 }",
428 "foo.java",
429 JavaParser,
430 wmc,
431 [
432 (class_wmc_sum, 8.0), (interface_wmc_sum, 0.0),
434 (total_wmc, 8.0)
435 ]
436 );
437 }
438
439 #[test]
440 fn java_lambda_expression() {
441 check_metrics!(
442 "import java.util.ArrayList;
443
444 public class TopLevelClass { // wmc = 2
445 private ArrayList<Integer> numbers;
446
447 public void m1() { // +1
448 numbers = new ArrayList<Integer>();
449 numbers.add(1);
450 numbers.add(2);
451 numbers.add(3);
452 }
453
454 public void m2() { // +1
455 numbers.forEach( (n) -> { System.out.println(n); } );
456 }
457 }",
458 "foo.java",
459 JavaParser,
460 wmc,
461 [
462 (class_wmc_sum, 2.0), (interface_wmc_sum, 0.0),
464 (total_wmc, 2.0)
465 ]
466 );
467 }
468
469 #[test]
470 fn java_single_interface() {
471 check_metrics!(
472 "interface Example { // wmc = 6
473 default boolean m1(boolean a, boolean b) { // +1
474 return (a && b == a || b); // +2
475 }
476 default int m2(int n) { // +1
477 return (n != 0) ? 1/n : n; // +1
478 };
479 void m3(); // +1
480 }",
481 "foo.java",
482 JavaParser,
483 wmc,
484 [
485 (class_wmc_sum, 0.0),
486 (interface_wmc_sum, 6.0), (total_wmc, 6.0)
488 ]
489 );
490 }
491
492 #[test]
493 fn java_multiple_interfaces() {
494 check_metrics!(
495 "interface FirstInterface { // wmc = 1
496 int a = 0;
497 default int getA() { // +1
498 return a;
499 }
500 }
501
502 interface SecondInterface { // wmc = 2
503 void setB(int n); // +1
504 int getB(); // +1
505 }",
506 "foo.java",
507 JavaParser,
508 wmc,
509 [
510 (class_wmc_sum, 0.0),
511 (interface_wmc_sum, 3.0), (total_wmc, 3.0)
513 ]
514 );
515 }
516
517 #[test]
518 fn java_nested_inner_interfaces() {
519 check_metrics!(
520 "interface TopLevelInterface { // wmc = 1
521 interface InnerInterfaceBefore { // wmc = 1
522 void m1(); // +1
523 }
524
525 void m2(); // +1
526
527 interface InnerInterfaceAfter { // wmc = 2
528 void m3(); // +1
529 interface InnerInterface { // wmc = 1
530 void m4(); // +1
531 }
532 void m5(); // +1
533 }
534 }",
535 "foo.java",
536 JavaParser,
537 wmc,
538 [
539 (class_wmc_sum, 0.0),
540 (interface_wmc_sum, 5.0), (total_wmc, 5.0)
542 ]
543 );
544 }
545
546 #[test]
547 fn java_class_in_interface() {
548 check_metrics!(
549 "interface TopLevelInterface { // wmc = 2
550 int getA(); // +1
551 boolean getB(); // +1
552
553 class InnerClass { // wmc = 2
554 float c;
555 double d;
556 float getC() { // +1
557 return c;
558 }
559 double getD() { // +1
560 return d;
561 }
562 }
563 }",
564 "foo.java",
565 JavaParser,
566 wmc,
567 [
568 (class_wmc_sum, 2.0), (interface_wmc_sum, 2.0), (total_wmc, 4.0)
571 ]
572 );
573 }
574
575 #[test]
576 fn java_interface_in_class() {
577 check_metrics!(
578 "class TopLevelClass { // wmc = 2
579 int a;
580 boolean b;
581 int getA() { // +1
582 return a;
583 }
584 boolean getB() { // +1
585 return b;
586 }
587
588 interface InnerInterface { // wmc = 2
589 float getC(); // +1
590 double getD(); // +1
591 }
592 }",
593 "foo.java",
594 JavaParser,
595 wmc,
596 [
597 (class_wmc_sum, 2.0), (interface_wmc_sum, 2.0), (total_wmc, 4.0)
600 ]
601 );
602 }
603}