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