1use serde::Serialize;
2use serde::ser::{SerializeStruct, Serializer};
3use std::fmt;
4
5use crate::checker::Checker;
6use crate::macros::implement_metric_trait;
7use crate::node::Node;
8use crate::*;
9
10#[derive(Debug, Clone)]
24pub struct Stats {
25 assignments: f64,
26 assignments_sum: f64,
27 assignments_min: f64,
28 assignments_max: f64,
29 branches: f64,
30 branches_sum: f64,
31 branches_min: f64,
32 branches_max: f64,
33 conditions: f64,
34 conditions_sum: f64,
35 conditions_min: f64,
36 conditions_max: f64,
37 space_count: usize,
38 declaration: Vec<DeclKind>,
39}
40
41#[derive(Debug, Clone)]
42enum DeclKind {
43 Var,
44 Const,
45}
46
47impl Default for Stats {
48 fn default() -> Self {
49 Self {
50 assignments: 0.,
51 assignments_sum: 0.,
52 assignments_min: f64::MAX,
53 assignments_max: 0.,
54 branches: 0.,
55 branches_sum: 0.,
56 branches_min: f64::MAX,
57 branches_max: 0.,
58 conditions: 0.,
59 conditions_sum: 0.,
60 conditions_min: f64::MAX,
61 conditions_max: 0.,
62 space_count: 1,
63 declaration: Vec::new(),
64 }
65 }
66}
67
68impl Serialize for Stats {
69 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
70 where
71 S: Serializer,
72 {
73 let mut st = serializer.serialize_struct("abc", 13)?;
74 st.serialize_field("assignments", &self.assignments_sum())?;
75 st.serialize_field("branches", &self.branches_sum())?;
76 st.serialize_field("conditions", &self.conditions_sum())?;
77 st.serialize_field("magnitude", &self.magnitude_sum())?;
78 st.serialize_field("assignments_average", &self.assignments_average())?;
79 st.serialize_field("branches_average", &self.branches_average())?;
80 st.serialize_field("conditions_average", &self.conditions_average())?;
81 st.serialize_field("assignments_min", &self.assignments_min())?;
82 st.serialize_field("assignments_max", &self.assignments_max())?;
83 st.serialize_field("branches_min", &self.branches_min())?;
84 st.serialize_field("branches_max", &self.branches_max())?;
85 st.serialize_field("conditions_min", &self.conditions_min())?;
86 st.serialize_field("conditions_max", &self.conditions_max())?;
87 st.end()
88 }
89}
90
91impl fmt::Display for Stats {
92 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
93 write!(
94 f,
95 "assignments: {}, branches: {}, conditions: {}, magnitude: {}, \
96 assignments_average: {}, branches_average: {}, conditions_average: {}, \
97 assignments_min: {}, assignments_max: {}, \
98 branches_min: {}, branches_max: {}, \
99 conditions_min: {}, conditions_max: {}",
100 self.assignments_sum(),
101 self.branches_sum(),
102 self.conditions_sum(),
103 self.magnitude_sum(),
104 self.assignments_average(),
105 self.branches_average(),
106 self.conditions_average(),
107 self.assignments_min(),
108 self.assignments_max(),
109 self.branches_min(),
110 self.branches_max(),
111 self.conditions_min(),
112 self.conditions_max()
113 )
114 }
115}
116
117impl Stats {
118 pub fn merge(&mut self, other: &Stats) {
120 self.assignments_min = self.assignments_min.min(other.assignments_min);
122 self.assignments_max = self.assignments_max.max(other.assignments_max);
123 self.branches_min = self.branches_min.min(other.branches_min);
124 self.branches_max = self.branches_max.max(other.branches_max);
125 self.conditions_min = self.conditions_min.min(other.conditions_min);
126 self.conditions_max = self.conditions_max.max(other.conditions_max);
127
128 self.assignments_sum += other.assignments_sum;
129 self.branches_sum += other.branches_sum;
130 self.conditions_sum += other.conditions_sum;
131
132 self.space_count += other.space_count;
133 }
134
135 pub fn assignments(&self) -> f64 {
137 self.assignments
138 }
139
140 pub fn assignments_sum(&self) -> f64 {
142 self.assignments_sum
143 }
144
145 pub fn assignments_average(&self) -> f64 {
150 self.assignments_sum() / self.space_count as f64
151 }
152
153 pub fn assignments_min(&self) -> f64 {
155 self.assignments_min
156 }
157
158 pub fn assignments_max(&self) -> f64 {
160 self.assignments_max
161 }
162
163 pub fn branches(&self) -> f64 {
165 self.branches
166 }
167
168 pub fn branches_sum(&self) -> f64 {
170 self.branches_sum
171 }
172
173 pub fn branches_average(&self) -> f64 {
178 self.branches_sum() / self.space_count as f64
179 }
180
181 pub fn branches_min(&self) -> f64 {
183 self.branches_min
184 }
185
186 pub fn branches_max(&self) -> f64 {
188 self.branches_max
189 }
190
191 pub fn conditions(&self) -> f64 {
193 self.conditions
194 }
195
196 pub fn conditions_sum(&self) -> f64 {
198 self.conditions_sum
199 }
200
201 pub fn conditions_average(&self) -> f64 {
206 self.conditions_sum() / self.space_count as f64
207 }
208
209 pub fn conditions_min(&self) -> f64 {
211 self.conditions_min
212 }
213
214 pub fn conditions_max(&self) -> f64 {
216 self.conditions_max
217 }
218
219 pub fn magnitude(&self) -> f64 {
221 (self.assignments.powi(2) + self.branches.powi(2) + self.conditions.powi(2)).sqrt()
222 }
223
224 pub fn magnitude_sum(&self) -> f64 {
226 (self.assignments_sum.powi(2) + self.branches_sum.powi(2) + self.conditions_sum.powi(2))
227 .sqrt()
228 }
229
230 #[inline(always)]
231 pub(crate) fn compute_sum(&mut self) {
232 self.assignments_sum += self.assignments;
233 self.branches_sum += self.branches;
234 self.conditions_sum += self.conditions;
235 }
236
237 #[inline(always)]
238 pub(crate) fn compute_minmax(&mut self) {
239 self.assignments_min = self.assignments_min.min(self.assignments);
240 self.assignments_max = self.assignments_max.max(self.assignments);
241 self.branches_min = self.branches_min.min(self.branches);
242 self.branches_max = self.branches_max.max(self.branches);
243 self.conditions_min = self.conditions_min.min(self.conditions);
244 self.conditions_max = self.conditions_max.max(self.conditions);
245 self.compute_sum();
246 }
247}
248
249pub trait Abc
250where
251 Self: Checker,
252{
253 fn compute(node: &Node, stats: &mut Stats);
254}
255
256fn java_inspect_container(container_node: &Node, conditions: &mut f64) {
259 use Java::*;
260
261 let mut node = *container_node;
262 let mut node_kind = node.kind_id().into();
263
264 let mut has_boolean_content = match node.parent().unwrap().kind_id().into() {
266 BinaryExpression | IfStatement | WhileStatement | DoStatement | ForStatement => true,
267 TernaryExpression => node
268 .previous_sibling()
269 .is_none_or(|prev_node| !matches!(prev_node.kind_id().into(), QMARK | COLON)),
270 _ => false,
271 };
272
273 loop {
275 let is_parenthesised_exp = matches!(node_kind, ParenthesizedExpression);
278 let is_not_operator = matches!(node_kind, UnaryExpression)
279 && matches!(node.child(0).unwrap().kind_id().into(), BANG);
280
281 if !is_parenthesised_exp && !is_not_operator {
284 break;
285 }
286
287 if !has_boolean_content && is_not_operator {
291 has_boolean_content = true;
292 }
293
294 node = node.child(1).unwrap();
299 node_kind = node.kind_id().into();
300
301 if matches!(node_kind, MethodInvocation | Identifier | True | False) {
303 if has_boolean_content {
304 *conditions += 1.;
305 }
306 break;
307 }
308 }
309}
310
311fn java_count_unary_conditions(list_node: &Node, conditions: &mut f64) {
313 use Java::*;
314
315 let list_kind = list_node.kind_id().into();
316 let mut cursor = list_node.cursor();
317
318 if cursor.goto_first_child() {
320 loop {
321 let node = cursor.node();
323 let node_kind = node.kind_id().into();
324
325 if matches!(node_kind, MethodInvocation | Identifier | True | False)
327 && matches!(list_kind, BinaryExpression)
328 && !matches!(list_kind, ArgumentList)
329 {
330 *conditions += 1.;
331 } else {
332 java_inspect_container(&node, conditions);
334 }
335
336 if !cursor.goto_next_sibling() {
339 break;
340 }
341 }
342 }
343}
344
345implement_metric_trait!(
346 Abc,
347 PythonCode,
348 MozjsCode,
349 JavascriptCode,
350 TypescriptCode,
351 TsxCode,
352 RustCode,
353 CppCode,
354 PreprocCode,
355 CcommentCode,
356 KotlinCode
357);
358
359impl Abc for JavaCode {
364 fn compute(node: &Node, stats: &mut Stats) {
365 use Java::*;
366
367 match node.kind_id().into() {
368 STAREQ | SLASHEQ | PERCENTEQ | DASHEQ | PLUSEQ | LTLTEQ | GTGTEQ | AMPEQ | PIPEEQ
369 | CARETEQ | GTGTGTEQ | PLUSPLUS | DASHDASH => {
370 stats.assignments += 1.;
371 }
372 FieldDeclaration | LocalVariableDeclaration => {
373 stats.declaration.push(DeclKind::Var);
374 }
375 Final => {
376 if let Some(DeclKind::Var) = stats.declaration.last() {
377 stats.declaration.push(DeclKind::Const);
378 }
379 }
380 SEMI => {
381 if let Some(DeclKind::Const | DeclKind::Var) = stats.declaration.last() {
382 stats.declaration.clear();
383 }
384 }
385 EQ => {
386 stats
388 .declaration
389 .last()
390 .map(|decl| {
391 if matches!(decl, DeclKind::Var) {
392 stats.assignments += 1.;
393 }
394 })
395 .unwrap_or_else(|| {
396 stats.assignments += 1.;
397 });
398 }
399 MethodInvocation | New => {
400 stats.branches += 1.;
401 }
402 GTEQ | LTEQ | EQEQ | BANGEQ | Else | Case | Default | QMARK | Try | Catch => {
403 stats.conditions += 1.;
404 }
405 GT | LT => {
406 if let Some(parent) = node.parent()
408 && !matches!(parent.kind_id().into(), TypeArguments)
409 {
410 stats.conditions += 1.;
411 }
412 }
413 AMPAMP | PIPEPIPE => {
415 if let Some(parent) = node.parent() {
416 java_count_unary_conditions(&parent, &mut stats.conditions);
417 }
418 }
419 ArgumentList => {
421 java_count_unary_conditions(node, &mut stats.conditions);
422 }
423 VariableDeclarator | AssignmentExpression => {
425 if let Some(right_operand) = node.child(2)
427 && matches!(
428 right_operand.kind_id().into(),
429 ParenthesizedExpression | UnaryExpression
430 )
431 {
432 java_inspect_container(&right_operand, &mut stats.conditions);
433 }
434 }
435 IfStatement | WhileStatement => {
437 if let Some(condition) = node.child(1)
439 && matches!(condition.kind_id().into(), ParenthesizedExpression)
440 {
441 java_inspect_container(&condition, &mut stats.conditions);
442 }
443 }
444 DoStatement => {
446 if let Some(condition) = node.child(3)
448 && matches!(condition.kind_id().into(), ParenthesizedExpression)
449 {
450 java_inspect_container(&condition, &mut stats.conditions);
451 }
452 }
453 ForStatement => {
455 if let Some(condition) = node.child(3) {
459 match condition.kind_id().into() {
460 SEMI => {
461 if let Some(cond) = node.child(4) {
465 match cond.kind_id().into() {
466 MethodInvocation | Identifier | True | False | SEMI
467 | RPAREN => {
468 stats.conditions += 1.;
469 }
470 ParenthesizedExpression | UnaryExpression => {
471 java_inspect_container(&cond, &mut stats.conditions);
472 }
473 _ => {}
474 }
475 }
476 }
477 MethodInvocation | Identifier | True | False => {
478 stats.conditions += 1.;
479 }
480 ParenthesizedExpression | UnaryExpression => {
481 java_inspect_container(&condition, &mut stats.conditions);
482 }
483 _ => {}
484 }
485 }
486 }
487 ReturnStatement => {
489 if let Some(value) = node.child(1)
491 && matches!(
492 value.kind_id().into(),
493 ParenthesizedExpression | UnaryExpression
494 )
495 {
496 java_inspect_container(&value, &mut stats.conditions)
497 }
498 }
499 LambdaExpression => {
501 if let Some(value) = node.child(2)
503 && matches!(
504 value.kind_id().into(),
505 ParenthesizedExpression | UnaryExpression
506 )
507 {
508 java_inspect_container(&value, &mut stats.conditions)
509 }
510 }
511 TernaryExpression => {
513 if let Some(condition) = node.child(0) {
515 match condition.kind_id().into() {
516 MethodInvocation | Identifier | True | False => {
517 stats.conditions += 1.;
518 }
519 ParenthesizedExpression | UnaryExpression => {
520 java_inspect_container(&condition, &mut stats.conditions);
521 }
522 _ => {}
523 }
524 }
525 if let Some(expression) = node.child(2)
527 && matches!(
528 expression.kind_id().into(),
529 ParenthesizedExpression | UnaryExpression
530 )
531 {
532 java_inspect_container(&expression, &mut stats.conditions);
533 }
534 if let Some(expression) = node.child(4)
536 && matches!(
537 expression.kind_id().into(),
538 ParenthesizedExpression | UnaryExpression
539 )
540 {
541 java_inspect_container(&expression, &mut stats.conditions);
542 }
543 }
544 _ => {}
545 }
546 }
547}
548
549#[cfg(test)]
550mod tests {
551 use crate::tools::check_metrics;
552
553 use super::*;
554
555 #[test]
557 fn java_constant_declarations() {
558 check_metrics::<JavaParser>(
559 "class A {
560 private final int X1 = 0, Y1 = 0;
561 public final float PI = 3.14f;
562 final static String HELLO = \"Hello,\";
563 protected String world = \" world!\"; // +1a
564 public float e = 2.718f; // +1a
565 private int x2 = 1, y2 = 2; // +2a
566
567 void m() {
568 final int Z1 = 0, Z2 = 0, Z3 = 0;
569 final float T = 0.0f;
570 int z1 = 1, z2 = 2, z3 = 3; // +3a
571 float t = 60.0f; // +1a
572 }
573 }",
574 "foo.java",
575 |metric| {
576 insta::assert_json_snapshot!(
579 metric.abc,
580 @r###"
581 {
582 "assignments": 8.0,
583 "branches": 0.0,
584 "conditions": 0.0,
585 "magnitude": 8.0,
586 "assignments_average": 2.6666666666666665,
587 "branches_average": 0.0,
588 "conditions_average": 0.0,
589 "assignments_min": 0.0,
590 "assignments_max": 4.0,
591 "branches_min": 0.0,
592 "branches_max": 0.0,
593 "conditions_min": 0.0,
594 "conditions_max": 0.0
595 }"###
596 );
597 },
598 );
599 }
600
601 #[test]
608 fn java_declarations_with_conditions() {
609 check_metrics::<JavaParser>(
610 "
611 boolean a = (1 > 2); // +1a +1c
612 boolean b = 3 > 4; // +1a +1c
613 boolean c = (1 > 2) && 3 > 4; // +1a +2c
614 boolean d = b && (x > 5) || c; // +1a +3c
615 boolean e = !d; // +1a +1c
616 boolean f = ((!false)); // +1a +1c
617 boolean g = !(!(true)); // +1a +1c
618 boolean h = true; // +1a
619 boolean i = (false); // +1a
620 boolean j = (((((true))))); // +1a
621 boolean k = (((((m()))))); // +1a +1b
622 boolean l = (((((!m()))))); // +1a +1b +1c
623 boolean m = (!(!((m())))); // +1a +1b +1c
624 List<String> n = null; // +1a (< and > used for generic types are not counted as conditions)
625 ",
626 "foo.java",
627 |metric| {
628 insta::assert_json_snapshot!(
631 metric.abc,
632 @r###"
633 {
634 "assignments": 14.0,
635 "branches": 3.0,
636 "conditions": 12.0,
637 "magnitude": 18.681541692269406,
638 "assignments_average": 14.0,
639 "branches_average": 3.0,
640 "conditions_average": 12.0,
641 "assignments_min": 14.0,
642 "assignments_max": 14.0,
643 "branches_min": 3.0,
644 "branches_max": 3.0,
645 "conditions_min": 12.0,
646 "conditions_max": 12.0
647 }"###
648 );
649 },
650 );
651 }
652
653 #[test]
655 fn java_assignments_with_conditions() {
656 check_metrics::<JavaParser>(
657 "
658 a = 2 < 1; // +1a +1c
659 b = (4 >= 3) && 2 <= 1; // +1a +2c
660 c = a || (x != 10) && b; // +1a +3c
661 d = !false; // +1a +1c
662 e = (!false); // +1a +1c
663 f = !(false); // +1a +1c
664 g = (!(((true)))); // +1a +1c
665 h = ((true)); // +1a
666 i = !m(); // +1a +1b +1c
667 j = !((m())); // +1a +1b +1c
668 k = (!(m())); // +1a +1b +1c
669 l = ((!(m()))); // +1a +1b +1c
670 m = !B.<Integer>m(2); // +1a +1b +1c
671 n = !((B.<Integer>m(4))); // +1a +1b +1c
672 ",
673 "foo.java",
674 |metric| {
675 insta::assert_json_snapshot!(
678 metric.abc,
679 @r###"
680 {
681 "assignments": 14.0,
682 "branches": 6.0,
683 "conditions": 16.0,
684 "magnitude": 22.090722034374522,
685 "assignments_average": 14.0,
686 "branches_average": 6.0,
687 "conditions_average": 16.0,
688 "assignments_min": 14.0,
689 "assignments_max": 14.0,
690 "branches_min": 6.0,
691 "branches_max": 6.0,
692 "conditions_min": 16.0,
693 "conditions_max": 16.0
694 }"###
695 );
696 },
697 );
698 }
699
700 #[test]
702 fn java_methods_arguments_with_conditions() {
703 check_metrics::<JavaParser>(
704 "
705 m1(a); // +1b
706 m2(a, b); // +1b
707 m3(true, (false), (((true)))); // +1b
708 m3(m1(false), m1(true), m1(false)); // +4b
709 m1(!a); // +1b +1c
710 m2((((a))), (!b)); // +1b +1c
711 m3(!(a), b, !!!c); // +1b +2c
712 m3(a, !b, m2(!a, !m2(!b, !m1(!c)))); // +4b +6c
713 ",
714 "foo.java",
715 |metric| {
716 insta::assert_json_snapshot!(
719 metric.abc,
720 @r###"
721 {
722 "assignments": 0.0,
723 "branches": 14.0,
724 "conditions": 10.0,
725 "magnitude": 17.204650534085253,
726 "assignments_average": 0.0,
727 "branches_average": 14.0,
728 "conditions_average": 10.0,
729 "assignments_min": 0.0,
730 "assignments_max": 0.0,
731 "branches_min": 14.0,
732 "branches_max": 14.0,
733 "conditions_min": 10.0,
734 "conditions_max": 10.0
735 }"###
736 );
737 },
738 );
739 }
740
741 #[test]
745 fn java_if_single_conditions() {
746 check_metrics::<JavaParser>(
747 "
748 if ( a < 0 ) {} // +1c
749 if ( ((a != 0)) ) {} // +1c
750 if ( !(a > 0) ) {} // +1c
751 if ( !(((a == 0))) ) {} // +1c
752 if ( b.m1() ) {} // +1b +1c
753 if ( !b.m1() ) {} // +1b +1c
754 if ( !!b.m2() ) {} // +1b +1c
755 if ( (!(b.m1())) ) {} // +1b +1c
756 if ( (!(!b.m1())) ) {} // +1b +1c
757 if ( ((b.m2())) ) {} // +1b +1c
758 if ( ((b.m().m1())) ) {} // +2b +1c
759 if ( c ) {} // +1c
760 if ( !c ) {} // +1c
761 if ( !!!!!!!!!!c ) {} // +1c
762 if ( (((c))) ) {} // +1c
763 if ( (((!c))) ) {} // +1c
764 if ( ((!(c))) ) {} // +1c
765 if ( true ) {} // +1c
766 if ( !true ) {} // +1c
767 if ( ((false)) ) {} // +1c
768 if ( !(!(false)) ) {} // +1c
769 if ( !!!false ) {} // +1c
770 ",
771 "foo.java",
772 |metric| {
773 insta::assert_json_snapshot!(
776 metric.abc,
777 @r###"
778 {
779 "assignments": 0.0,
780 "branches": 8.0,
781 "conditions": 22.0,
782 "magnitude": 23.40939982143925,
783 "assignments_average": 0.0,
784 "branches_average": 8.0,
785 "conditions_average": 22.0,
786 "assignments_min": 0.0,
787 "assignments_max": 0.0,
788 "branches_min": 8.0,
789 "branches_max": 8.0,
790 "conditions_min": 22.0,
791 "conditions_max": 22.0
792 }"###
793 );
794 },
795 );
796 }
797
798 #[test]
799 fn java_if_multiple_conditions() {
800 check_metrics::<JavaParser>(
801 "
802 if ( a || b || c || d ) {} // +4c
803 if ( a || b && c && d ) {} // +4c
804 if ( x < y && a == b ) {} // +2c
805 if ( ((z < (x + y))) ) {} // +1c
806 if ( a || ((((b))) && c) ) {} // +3c
807 if ( a && ((((a == b))) && c) ) {} // +3c
808 if ( a || ((((a == b))) || ((c))) ) {} // +3c
809 if ( x < y && B.m() ) {} // +1b +2c
810 if ( x < y && !(((B.m()))) ) {} // +1b +2c
811 if ( !(x < y) && !B.m() ) {} // +1b +2c
812 if ( !!!(!!!(a)) && B.m() || // +1b +2c
813 !B.m() && (((x > 4))) ) {} // +1b +2c
814 ",
815 "foo.java",
816 |metric| {
817 insta::assert_json_snapshot!(
820 metric.abc,
821 @r###"
822 {
823 "assignments": 0.0,
824 "branches": 5.0,
825 "conditions": 30.0,
826 "magnitude": 30.4138126514911,
827 "assignments_average": 0.0,
828 "branches_average": 5.0,
829 "conditions_average": 30.0,
830 "assignments_min": 0.0,
831 "assignments_max": 0.0,
832 "branches_min": 5.0,
833 "branches_max": 5.0,
834 "conditions_min": 30.0,
835 "conditions_max": 30.0
836 }"###
837 );
838 },
839 );
840 }
841
842 #[test]
843 fn java_while_and_do_while_conditions() {
844 check_metrics::<JavaParser>(
845 "
846 while ( (!(!(!(a)))) ) {} // +1c
847 while ( b || 1 > 2 ) {} // +2c
848 while ( x.m() && (((c))) ) {} // +1b +2c
849 do {} while ( !!!(((!!!a))) ); // +1c
850 do {} while ( a || (b && c) ); // +3c
851 do {} while ( !x.m() && 1 > 2 || !true ); // +1b +3c
852 ",
853 "foo.java",
854 |metric| {
855 insta::assert_json_snapshot!(
858 metric.abc,
859 @r###"
860 {
861 "assignments": 0.0,
862 "branches": 2.0,
863 "conditions": 12.0,
864 "magnitude": 12.165525060596439,
865 "assignments_average": 0.0,
866 "branches_average": 2.0,
867 "conditions_average": 12.0,
868 "assignments_min": 0.0,
869 "assignments_max": 0.0,
870 "branches_min": 2.0,
871 "branches_max": 2.0,
872 "conditions_min": 12.0,
873 "conditions_max": 12.0
874 }"###
875 );
876 },
877 );
878 }
879
880 #[test]
887 fn java_return_with_conditions() {
888 check_metrics::<JavaParser>(
889 "class A {
890 boolean m1() {
891 return !(z >= 0); // +1c
892 }
893 boolean m2() {
894 return (((!x))); // +1c
895 }
896 boolean m3() {
897 return x && y; // +2c
898 }
899 boolean m4() {
900 return y || (z < 0); // +2c
901 }
902 boolean m5() {
903 return x || y ? // +3c (two unary conditions and one ?)
904 true : false;
905 }
906 }",
907 "foo.java",
908 |metric| {
909 insta::assert_json_snapshot!(
912 metric.abc,
913 @r###"
914 {
915 "assignments": 0.0,
916 "branches": 0.0,
917 "conditions": 9.0,
918 "magnitude": 9.0,
919 "assignments_average": 0.0,
920 "branches_average": 0.0,
921 "conditions_average": 1.2857142857142858,
922 "assignments_min": 0.0,
923 "assignments_max": 0.0,
924 "branches_min": 0.0,
925 "branches_max": 0.0,
926 "conditions_min": 0.0,
927 "conditions_max": 3.0
928 }"###
929 );
930 },
931 );
932 }
933
934 #[test]
937 fn java_return_without_conditions() {
938 check_metrics::<JavaParser>(
939 "class A {
940 boolean m1() {
941 return x;
942 }
943 boolean m2() {
944 return (x);
945 }
946 boolean m3() {
947 return y.m(); // +1b
948 }
949 boolean m4() {
950 return false;
951 }
952 void m5() {
953 return;
954 }
955 }",
956 "foo.java",
957 |metric| {
958 insta::assert_json_snapshot!(
961 metric.abc,
962 @r###"
963 {
964 "assignments": 0.0,
965 "branches": 1.0,
966 "conditions": 0.0,
967 "magnitude": 1.0,
968 "assignments_average": 0.0,
969 "branches_average": 0.14285714285714285,
970 "conditions_average": 0.0,
971 "assignments_min": 0.0,
972 "assignments_max": 0.0,
973 "branches_min": 0.0,
974 "branches_max": 1.0,
975 "conditions_min": 0.0,
976 "conditions_max": 0.0
977 }"###
978 );
979 },
980 );
981 }
982
983 #[test]
986 fn java_lambda_expressions_return_with_conditions() {
987 check_metrics::<JavaParser>(
988 "
989 Predicate<Boolean> p1 = a -> a; // +1a
990 Predicate<Boolean> p2 = b -> true; // +1a
991 Predicate<Boolean> p3 = c -> m(); // +1a
992 Predicate<Integer> p4 = d -> d > 10; // +1a +1c
993 Predicate<Boolean> p5 = (e) -> !e; // +1a +1c
994 Predicate<Boolean> p6 = (f) -> !((!f)); // +1a +1c
995 Predicate<Boolean> p7 = (g) -> !g && true; // +1a +2c
996 BiPredicate<Boolean, Boolean> bp1 = (h, i) -> !h && !i; // +1a +2c
997 BiPredicate<Boolean, Boolean> bp2 = (j, k) -> {
998 return j || k; // +1a +2c
999 };
1000 ",
1001 "foo.java",
1002 |metric| {
1003 insta::assert_json_snapshot!(
1006 metric.abc,
1007 @r###"
1008 {
1009 "assignments": 9.0,
1010 "branches": 1.0,
1011 "conditions": 9.0,
1012 "magnitude": 12.767145334803704,
1013 "assignments_average": 9.0,
1014 "branches_average": 1.0,
1015 "conditions_average": 9.0,
1016 "assignments_min": 9.0,
1017 "assignments_max": 9.0,
1018 "branches_min": 1.0,
1019 "branches_max": 1.0,
1020 "conditions_min": 9.0,
1021 "conditions_max": 9.0
1022 }"###
1023 );
1024 },
1025 );
1026 }
1027
1028 #[test]
1029 fn java_for_with_variable_declaration() {
1030 check_metrics::<JavaParser>(
1031 "
1032 for ( int i1 = 0; !(!(!(!a))); i1++ ) {} // +2a +1c
1033 for ( int i2 = 0; !B.m(); i2++ ) {} // +2a +1b +1c
1034 for ( int i3 = 0; a || false; i3++ ) {} // +2a +2c
1035 for ( int i4 = 0; a && B.m() ? true : false; i4++ ) {} // +2a +1b +3c
1036 for ( int i5 = 0; true; i5++ ) {} // +2a +1c
1037 ",
1038 "foo.java",
1039 |metric| {
1040 insta::assert_json_snapshot!(
1043 metric.abc,
1044 @r###"
1045 {
1046 "assignments": 10.0,
1047 "branches": 2.0,
1048 "conditions": 8.0,
1049 "magnitude": 12.96148139681572,
1050 "assignments_average": 10.0,
1051 "branches_average": 2.0,
1052 "conditions_average": 8.0,
1053 "assignments_min": 10.0,
1054 "assignments_max": 10.0,
1055 "branches_min": 2.0,
1056 "branches_max": 2.0,
1057 "conditions_min": 8.0,
1058 "conditions_max": 8.0
1059 }"###
1060 );
1061 },
1062 );
1063 }
1064
1065 #[test]
1066 fn java_for_without_variable_declaration() {
1067 check_metrics::<JavaParser>(
1068 "class A{
1069 void m1() {
1070 for (i = 0; x < y; i++) {} // +2a +1c
1071 for (i = 0; ((x < y)); i++) {} // +2a +1c
1072 for (i = 0; !(!(x < y)); i++) {} // +2a +1c
1073 for (i = 0; true; i++) {} // +2a +1c
1074 }
1075 void m2() {
1076 for ( ; true; ) {} // +1c
1077 }
1078 void m3() {
1079 for ( ; ; ) {} // +1c (one implicit unary condition set to true)
1080 }
1081 }",
1082 "foo.java",
1083 |metric| {
1084 insta::assert_json_snapshot!(
1087 metric.abc,
1088 @r###"
1089 {
1090 "assignments": 8.0,
1091 "branches": 0.0,
1092 "conditions": 6.0,
1093 "magnitude": 10.0,
1094 "assignments_average": 1.6,
1095 "branches_average": 0.0,
1096 "conditions_average": 1.2,
1097 "assignments_min": 0.0,
1098 "assignments_max": 8.0,
1099 "branches_min": 0.0,
1100 "branches_max": 0.0,
1101 "conditions_min": 0.0,
1102 "conditions_max": 4.0
1103 }"###
1104 );
1105 },
1106 );
1107 }
1108
1109 #[test]
1112 fn java_ternary_conditions() {
1113 check_metrics::<JavaParser>(
1114 "
1115 a = true; // +1a
1116 b = a ? true : false; // +1a +2c
1117 c = ((((a)))) ? !false : !b; // +1a +4c
1118 d = !this.m() ? !!a : (false); // +1a +1b +3c
1119 e = !(a) && b ? ((c)) : !d; // +1a +4c
1120 if ( this.m() ? a : !this.m() ) {} // +2b +3c
1121 if ( x > 0 ? !(false) : this.m() ) {} // +1b +3c
1122 if ( x > 0 && x != 3 ? !(a) : (!(b)) ) {} // +5c
1123 ",
1124 "foo.java",
1125 |metric| {
1126 insta::assert_json_snapshot!(
1129 metric.abc,
1130 @r###"
1131 {
1132 "assignments": 5.0,
1133 "branches": 4.0,
1134 "conditions": 24.0,
1135 "magnitude": 24.839484696748443,
1136 "assignments_average": 5.0,
1137 "branches_average": 4.0,
1138 "conditions_average": 24.0,
1139 "assignments_min": 5.0,
1140 "assignments_max": 5.0,
1141 "branches_min": 4.0,
1142 "branches_max": 4.0,
1143 "conditions_min": 24.0,
1144 "conditions_max": 24.0
1145 }"###
1146 );
1147 },
1148 );
1149 }
1150}