code-gauge 4.3.0 → 4.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/types.d.ts +6 -7
- package/native/src/complexity.rs +90 -18
- package/native/src/functions.rs +9 -7
- package/native/src/languages.rs +0 -16
- package/native/src/lib.rs +0 -1
- package/package.json +8 -8
- package/native/src/cyclomatic.rs +0 -79
package/README.md
CHANGED
|
@@ -173,9 +173,9 @@ The `duplication` section tunes how clones are detected:
|
|
|
173
173
|
import declarations count, and statement-shaped content is counted uniformly in expression
|
|
174
174
|
positions too
|
|
175
175
|
- Per-function and file-level nesting depth
|
|
176
|
-
- Per-function cyclomatic complexity (McCabe, own body only),
|
|
177
|
-
|
|
178
|
-
regression gate ignore
|
|
176
|
+
- Per-function cyclomatic complexity (McCabe, own body only), counted as NIST SP 500-235 defines it:
|
|
177
|
+
every decision and short-circuit operator adds one, one per case-labelled statement; an API-only
|
|
178
|
+
metric that ranking and the regression gate ignore
|
|
179
179
|
- Per-function parameter counts and locations (name, node type, line span)
|
|
180
180
|
- Within-file duplication: copy-pasted blocks matched on normalized tokens (identifiers anonymized
|
|
181
181
|
consistently, literals by kind, and literal-dense data tables excluded unless their values also
|
package/dist/types.d.ts
CHANGED
|
@@ -65,13 +65,12 @@ export interface FunctionMetrics {
|
|
|
65
65
|
/** Exclusive end column, so functions can be tested for containment in each other. */
|
|
66
66
|
endColumn: number;
|
|
67
67
|
/**
|
|
68
|
-
* McCabe cyclomatic complexity of the function's own body
|
|
69
|
-
* (branch, loop,
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* and
|
|
73
|
-
* decision
|
|
74
|
-
* report PMD-compatible metrics.
|
|
68
|
+
* McCabe cyclomatic complexity of the function's own body, counted from source as NIST SP 500-235
|
|
69
|
+
* §4 prescribes: 1 plus one per decision (branch, loop, catch, ternary, pattern guard), per
|
|
70
|
+
* short-circuit operator (`&&`, `||`, `and`, `or`) wherever it appears, and per case-labelled
|
|
71
|
+
* statement or match arm (stacked labels share one; default and catch-all arms add none).
|
|
72
|
+
* Full-evaluation operators and `throw` add nothing. Nested function bodies are excluded, so
|
|
73
|
+
* summing over functions counts each decision once. Not used by ranking or the regression gate.
|
|
75
74
|
*/
|
|
76
75
|
cyclomaticComplexity: number;
|
|
77
76
|
cognitiveComplexity: number;
|
package/native/src/complexity.rs
CHANGED
|
@@ -12,7 +12,6 @@ pub struct ComplexityResult {
|
|
|
12
12
|
pub struct LanguageSets {
|
|
13
13
|
pub function_nodes: HashSet<&'static str>,
|
|
14
14
|
pub decision_nodes: HashSet<&'static str>,
|
|
15
|
-
pub pmd_cyclomatic: bool,
|
|
16
15
|
pub nesting_nodes: HashSet<&'static str>,
|
|
17
16
|
pub ncss_nodes: HashSet<&'static str>,
|
|
18
17
|
pub ncss_containers: HashSet<&'static str>,
|
|
@@ -23,7 +22,6 @@ impl LanguageSets {
|
|
|
23
22
|
LanguageSets {
|
|
24
23
|
function_nodes: language.function_node_types.iter().copied().collect(),
|
|
25
24
|
decision_nodes: language.decision_node_types.iter().copied().collect(),
|
|
26
|
-
pmd_cyclomatic: language.pmd_cyclomatic,
|
|
27
25
|
nesting_nodes: language.nesting_node_types.iter().copied().collect(),
|
|
28
26
|
ncss_nodes: language.ncss_node_types.iter().copied().collect(),
|
|
29
27
|
ncss_containers: language.ncss_container_node_types.iter().copied().collect(),
|
|
@@ -201,7 +199,7 @@ impl FunctionBodyPass<'_, '_, '_> {
|
|
|
201
199
|
// `if` keyword token), so only named nodes count as decisions.
|
|
202
200
|
let is_decision = current.is_named()
|
|
203
201
|
&& self.sets.decision_nodes.contains(current.kind())
|
|
204
|
-
&& !
|
|
202
|
+
&& !is_pathless_switch_branch(current, self.code);
|
|
205
203
|
let is_case_clause = current.is_named() && CASE_CLAUSE_NODE_TYPES.contains(¤t.kind());
|
|
206
204
|
// Ruby's `case ... else` arm is an `else` node; like every other language's default branch
|
|
207
205
|
// it nests its contents inside the switch (it cannot go in the Ruby nesting set because
|
|
@@ -216,18 +214,12 @@ impl FunctionBodyPass<'_, '_, '_> {
|
|
|
216
214
|
// surcharge (Sonar cognitive-complexity semantics).
|
|
217
215
|
let is_continuation = is_decision && is_flat_chain_continuation(current);
|
|
218
216
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
u64::from(
|
|
226
|
-
is_decision
|
|
227
|
-
|| is_boolean_operator(current, self.code)
|
|
228
|
-
|| is_pattern_guard(current),
|
|
229
|
-
)
|
|
230
|
-
};
|
|
217
|
+
// Each branch, short-circuit operator, and pattern guard adds one path (McCabe; NIST SP
|
|
218
|
+
// 500-235 §4); `else` adds none.
|
|
219
|
+
if counts_for_own_body
|
|
220
|
+
&& (is_decision || is_boolean_operator(current, self.code) || is_pattern_guard(current))
|
|
221
|
+
{
|
|
222
|
+
self.top_frame().cyclomatic_complexity += 1;
|
|
231
223
|
}
|
|
232
224
|
if is_decision && !is_case_clause {
|
|
233
225
|
if is_continuation {
|
|
@@ -380,7 +372,7 @@ pub fn measure_complexity(
|
|
|
380
372
|
// `if` keyword token), so only named nodes count as decisions.
|
|
381
373
|
let is_decision = current.is_named()
|
|
382
374
|
&& sets.decision_nodes.contains(current.kind())
|
|
383
|
-
&& !
|
|
375
|
+
&& !is_pathless_switch_branch(current, code);
|
|
384
376
|
let is_case_clause = current.is_named() && CASE_CLAUSE_NODE_TYPES.contains(¤t.kind());
|
|
385
377
|
// Ruby's `case ... else` arm is an `else` node; like every other language's default branch
|
|
386
378
|
// it nests its contents inside the switch (it cannot go in the Ruby nesting set because
|
|
@@ -633,13 +625,93 @@ fn is_flat_chain_continuation(node: Node<'_>) -> bool {
|
|
|
633
625
|
.is_some_and(|alternative| alternative.id() == node.id())
|
|
634
626
|
}
|
|
635
627
|
|
|
636
|
-
///
|
|
637
|
-
|
|
628
|
+
/// Switch branches that add no path. NIST SP 500-235 counts one path per case-labelled statement:
|
|
629
|
+
/// a label-only case shares the statement of the case below it, and that statement adds a path
|
|
630
|
+
/// unless one of its stacked labels is an unguarded default or catch-all (`case 3: default: g();`
|
|
631
|
+
/// and `default: case 3: g();` are the default outcome) or all of them are catch-alls. Guards are
|
|
632
|
+
/// charged separately, so a guarded catch-all never absorbs a case stacked with it.
|
|
633
|
+
fn is_pathless_switch_branch(node: Node<'_>, code: &Source<'_>) -> bool {
|
|
634
|
+
if is_label_only_case(node) {
|
|
635
|
+
return true;
|
|
636
|
+
}
|
|
637
|
+
let mut stacked = vec![node];
|
|
638
|
+
let mut previous = node.prev_named_sibling();
|
|
639
|
+
while let Some(sibling) = previous {
|
|
640
|
+
if !crate::ncss::COMMENT_NODE_TYPES.contains(&sibling.kind()) {
|
|
641
|
+
if !is_label_only_case(sibling) {
|
|
642
|
+
break;
|
|
643
|
+
}
|
|
644
|
+
stacked.push(sibling);
|
|
645
|
+
}
|
|
646
|
+
previous = sibling.prev_named_sibling();
|
|
647
|
+
}
|
|
648
|
+
stacked
|
|
649
|
+
.iter()
|
|
650
|
+
.any(|label| is_default_switch_branch(*label, code) && !has_pattern_guard(*label))
|
|
651
|
+
|| stacked
|
|
652
|
+
.iter()
|
|
653
|
+
.all(|label| is_default_switch_branch(*label, code))
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
/// A guard directly on the arm (C# `when`, Python `if`, Ruby `if`/`unless`) or inside its label or
|
|
657
|
+
/// pattern (Java `when`, Rust `if`).
|
|
658
|
+
fn has_pattern_guard(node: Node<'_>) -> bool {
|
|
659
|
+
crate::util::named_children(node).into_iter().any(|child| {
|
|
660
|
+
is_pattern_guard(child)
|
|
661
|
+
|| crate::util::named_children(child)
|
|
662
|
+
.into_iter()
|
|
663
|
+
.any(is_pattern_guard)
|
|
664
|
+
})
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
/// A C/C++/JS/Java/C# case whose labels share the next case's statements; every grammar parses each
|
|
668
|
+
/// stacked label as its own case node.
|
|
669
|
+
fn is_label_only_case(node: Node<'_>) -> bool {
|
|
670
|
+
let children = non_comment_children(node);
|
|
671
|
+
match node.kind() {
|
|
672
|
+
"case_statement" => {
|
|
673
|
+
let value = node.child_by_field_name("value").map(|value| value.id());
|
|
674
|
+
children.iter().all(|child| Some(child.id()) == value)
|
|
675
|
+
}
|
|
676
|
+
"switch_case" | "switch_default" => node.child_by_field_name("body").is_none(),
|
|
677
|
+
"switch_block_statement_group" => {
|
|
678
|
+
children.iter().all(|child| child.kind() == "switch_label")
|
|
679
|
+
}
|
|
680
|
+
// A C# label is a pattern (`case 1:` parses as a constant pattern) plus an optional `when`
|
|
681
|
+
// guard; anything else, a `#if` block included, is the section's body.
|
|
682
|
+
"switch_section" => children.iter().all(|child| {
|
|
683
|
+
child.kind().ends_with("pattern")
|
|
684
|
+
|| child.kind() == "discard"
|
|
685
|
+
|| child.kind() == "when_clause"
|
|
686
|
+
}),
|
|
687
|
+
_ => false,
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
fn is_default_switch_branch(node: Node<'_>, code: &Source<'_>) -> bool {
|
|
638
692
|
let kind = node.kind();
|
|
693
|
+
if kind == "switch_default" {
|
|
694
|
+
return true;
|
|
695
|
+
}
|
|
639
696
|
if kind == "case_statement" {
|
|
640
697
|
return node.child_by_field_name("value").is_none();
|
|
641
698
|
}
|
|
642
699
|
|
|
700
|
+
// Java `default:` groups and `default ->` rules: a label with no expression or pattern, or a
|
|
701
|
+
// `case null, default` label, whose `default` the grammar parses as an identifier.
|
|
702
|
+
if kind == "switch_block_statement_group" || kind == "switch_rule" {
|
|
703
|
+
return non_comment_children(node)
|
|
704
|
+
.into_iter()
|
|
705
|
+
.filter(|child| child.kind() == "switch_label")
|
|
706
|
+
.any(|label| {
|
|
707
|
+
let parts = non_comment_children(label);
|
|
708
|
+
parts.is_empty()
|
|
709
|
+
|| parts.iter().any(|part| {
|
|
710
|
+
part.kind() == "identifier" && node_text(*part, code) == "default"
|
|
711
|
+
})
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
|
|
643
715
|
// C# `default:` sections and catch-all (`_`, `var x`) labels and arms, and Kotlin `else ->`
|
|
644
716
|
// entries. A guarded catch-all (`_ when cond =>`) is still a default arm: only its guard
|
|
645
717
|
// branches, which is_pattern_guard charges, like Python's `case _ if cond:` and Rust's
|
package/native/src/functions.rs
CHANGED
|
@@ -3,15 +3,16 @@ use tree_sitter::Node;
|
|
|
3
3
|
|
|
4
4
|
use crate::util::{all_children, find_children_by_field_name, named_children, node_text, Source};
|
|
5
5
|
|
|
6
|
-
///
|
|
7
|
-
///
|
|
8
|
-
///
|
|
9
|
-
///
|
|
10
|
-
/// C# auto-property accessors (`{ get; set; }`) and Kotlin visibility-only accessors (`private
|
|
6
|
+
/// Declarations without a body have no control flow, so they are signatures, not functions: C++
|
|
7
|
+
/// pure-virtual/`= default`/`= delete` members, abstract/interface/extern methods (Java, C#,
|
|
8
|
+
/// Kotlin), Go assembly-backed function declarations, and Rust trait method signatures, matching
|
|
9
|
+
/// how TypeScript method signatures are excluded. C# auto-property accessors (`{ get; set; }`) and Kotlin visibility-only accessors (`private
|
|
11
10
|
/// set`) hold no code, so they need a body too; a C# property or indexer is a function only in its
|
|
12
11
|
/// expression-bodied form (`int X => ...`), otherwise its accessors are the functions.
|
|
13
12
|
const BODY_REQUIRED_FUNCTION_TYPES: &[&str] = &[
|
|
14
13
|
"function_definition",
|
|
14
|
+
"function_declaration",
|
|
15
|
+
"method_declaration",
|
|
15
16
|
"constructor_declaration",
|
|
16
17
|
"compact_constructor_declaration",
|
|
17
18
|
"function_signature_item",
|
|
@@ -35,8 +36,9 @@ pub fn is_implemented_function(node: Node<'_>) -> bool {
|
|
|
35
36
|
.is_some_and(|value| value.kind() == "arrow_expression_clause");
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
// The Kotlin grammar has no fields; an implemented accessor holds a
|
|
39
|
-
|
|
39
|
+
// The Kotlin grammar has no fields; an implemented function or accessor holds a
|
|
40
|
+
// `function_body` child.
|
|
41
|
+
if node.kind() == "getter" || node.kind() == "setter" || node.kind() == "function_declaration" {
|
|
40
42
|
return named_children(node)
|
|
41
43
|
.iter()
|
|
42
44
|
.any(|child| child.kind() == "function_body");
|
package/native/src/languages.rs
CHANGED
|
@@ -7,9 +7,6 @@ pub struct LanguageDefinition {
|
|
|
7
7
|
grammar_id: GrammarId,
|
|
8
8
|
pub function_node_types: &'static [&'static str],
|
|
9
9
|
pub decision_node_types: &'static [&'static str],
|
|
10
|
-
/// Whether cyclomatic complexity follows PMD's Java CycloVisitor (see cyclomatic.rs) instead of
|
|
11
|
-
/// the generic one-path-per-decision rule.
|
|
12
|
-
pub pmd_cyclomatic: bool,
|
|
13
10
|
pub nesting_node_types: &'static [&'static str],
|
|
14
11
|
pub ncss_node_types: &'static [&'static str],
|
|
15
12
|
pub ncss_container_node_types: &'static [&'static str],
|
|
@@ -659,7 +656,6 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
659
656
|
grammar_id: GrammarId::JavaScript,
|
|
660
657
|
function_node_types: COMMON_FUNCTION_NODES,
|
|
661
658
|
decision_node_types: COMMON_DECISION_NODES,
|
|
662
|
-
pmd_cyclomatic: false,
|
|
663
659
|
nesting_node_types: COMMON_NESTING_NODES,
|
|
664
660
|
ncss_node_types: JS_NCSS_NODES,
|
|
665
661
|
ncss_container_node_types: &[],
|
|
@@ -670,7 +666,6 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
670
666
|
grammar_id: GrammarId::JavaScript,
|
|
671
667
|
function_node_types: COMMON_FUNCTION_NODES,
|
|
672
668
|
decision_node_types: COMMON_DECISION_NODES,
|
|
673
|
-
pmd_cyclomatic: false,
|
|
674
669
|
nesting_node_types: COMMON_NESTING_NODES,
|
|
675
670
|
ncss_node_types: JS_NCSS_NODES,
|
|
676
671
|
ncss_container_node_types: &[],
|
|
@@ -681,7 +676,6 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
681
676
|
grammar_id: GrammarId::TypeScript,
|
|
682
677
|
function_node_types: COMMON_FUNCTION_NODES,
|
|
683
678
|
decision_node_types: COMMON_DECISION_NODES,
|
|
684
|
-
pmd_cyclomatic: false,
|
|
685
679
|
nesting_node_types: COMMON_NESTING_NODES,
|
|
686
680
|
ncss_node_types: JS_NCSS_NODES,
|
|
687
681
|
ncss_container_node_types: &[],
|
|
@@ -692,7 +686,6 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
692
686
|
grammar_id: GrammarId::Tsx,
|
|
693
687
|
function_node_types: COMMON_FUNCTION_NODES,
|
|
694
688
|
decision_node_types: COMMON_DECISION_NODES,
|
|
695
|
-
pmd_cyclomatic: false,
|
|
696
689
|
nesting_node_types: COMMON_NESTING_NODES,
|
|
697
690
|
ncss_node_types: JS_NCSS_NODES,
|
|
698
691
|
ncss_container_node_types: &[],
|
|
@@ -703,7 +696,6 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
703
696
|
grammar_id: GrammarId::Python,
|
|
704
697
|
function_node_types: COMMON_FUNCTION_NODES,
|
|
705
698
|
decision_node_types: COMMON_DECISION_NODES,
|
|
706
|
-
pmd_cyclomatic: false,
|
|
707
699
|
nesting_node_types: COMMON_NESTING_NODES,
|
|
708
700
|
ncss_node_types: PYTHON_NCSS_NODES,
|
|
709
701
|
ncss_container_node_types: &[],
|
|
@@ -714,7 +706,6 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
714
706
|
grammar_id: GrammarId::Go,
|
|
715
707
|
function_node_types: COMMON_FUNCTION_NODES,
|
|
716
708
|
decision_node_types: GO_DECISION_NODES,
|
|
717
|
-
pmd_cyclomatic: false,
|
|
718
709
|
nesting_node_types: GO_NESTING_NODES,
|
|
719
710
|
ncss_node_types: GO_NCSS_NODES,
|
|
720
711
|
ncss_container_node_types: &[],
|
|
@@ -725,7 +716,6 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
725
716
|
grammar_id: GrammarId::Rust,
|
|
726
717
|
function_node_types: COMMON_FUNCTION_NODES,
|
|
727
718
|
decision_node_types: COMMON_DECISION_NODES,
|
|
728
|
-
pmd_cyclomatic: false,
|
|
729
719
|
nesting_node_types: COMMON_NESTING_NODES,
|
|
730
720
|
ncss_node_types: RUST_NCSS_NODES,
|
|
731
721
|
ncss_container_node_types: RUST_NCSS_CONTAINERS,
|
|
@@ -736,7 +726,6 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
736
726
|
grammar_id: GrammarId::Java,
|
|
737
727
|
function_node_types: JAVA_FUNCTION_NODES,
|
|
738
728
|
decision_node_types: JAVA_DECISION_NODES,
|
|
739
|
-
pmd_cyclomatic: true,
|
|
740
729
|
nesting_node_types: JAVA_DECISION_NODES,
|
|
741
730
|
ncss_node_types: JAVA_NCSS_NODES,
|
|
742
731
|
ncss_container_node_types: &[],
|
|
@@ -747,7 +736,6 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
747
736
|
grammar_id: GrammarId::Ruby,
|
|
748
737
|
function_node_types: RUBY_FUNCTION_NODES,
|
|
749
738
|
decision_node_types: RUBY_DECISION_NODES,
|
|
750
|
-
pmd_cyclomatic: false,
|
|
751
739
|
nesting_node_types: RUBY_DECISION_NODES,
|
|
752
740
|
ncss_node_types: RUBY_NCSS_NODES,
|
|
753
741
|
ncss_container_node_types: RUBY_NCSS_CONTAINERS,
|
|
@@ -758,7 +746,6 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
758
746
|
grammar_id: GrammarId::C,
|
|
759
747
|
function_node_types: C_FUNCTION_NODES,
|
|
760
748
|
decision_node_types: C_DECISION_NODES,
|
|
761
|
-
pmd_cyclomatic: false,
|
|
762
749
|
nesting_node_types: C_DECISION_NODES,
|
|
763
750
|
ncss_node_types: C_NCSS_NODES,
|
|
764
751
|
ncss_container_node_types: &[],
|
|
@@ -769,7 +756,6 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
769
756
|
grammar_id: GrammarId::Cpp,
|
|
770
757
|
function_node_types: C_FUNCTION_NODES,
|
|
771
758
|
decision_node_types: CPP_DECISION_NODES,
|
|
772
|
-
pmd_cyclomatic: false,
|
|
773
759
|
nesting_node_types: CPP_DECISION_NODES,
|
|
774
760
|
ncss_node_types: CPP_NCSS_NODES,
|
|
775
761
|
ncss_container_node_types: &[],
|
|
@@ -780,7 +766,6 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
780
766
|
grammar_id: GrammarId::CSharp,
|
|
781
767
|
function_node_types: CSHARP_FUNCTION_NODES,
|
|
782
768
|
decision_node_types: CSHARP_DECISION_NODES,
|
|
783
|
-
pmd_cyclomatic: false,
|
|
784
769
|
nesting_node_types: CSHARP_DECISION_NODES,
|
|
785
770
|
ncss_node_types: CSHARP_NCSS_NODES,
|
|
786
771
|
ncss_container_node_types: &[],
|
|
@@ -791,7 +776,6 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
791
776
|
grammar_id: GrammarId::Kotlin,
|
|
792
777
|
function_node_types: KOTLIN_FUNCTION_NODES,
|
|
793
778
|
decision_node_types: KOTLIN_DECISION_NODES,
|
|
794
|
-
pmd_cyclomatic: false,
|
|
795
779
|
nesting_node_types: KOTLIN_DECISION_NODES,
|
|
796
780
|
ncss_node_types: KOTLIN_NCSS_NODES,
|
|
797
781
|
ncss_container_node_types: KOTLIN_NCSS_CONTAINERS,
|
package/native/src/lib.rs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-gauge",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.1",
|
|
4
4
|
"description": "Measure code metrics with tree-sitter.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -89,12 +89,12 @@
|
|
|
89
89
|
"registry": "https://registry.npmjs.org/"
|
|
90
90
|
},
|
|
91
91
|
"optionalDependencies": {
|
|
92
|
-
"code-gauge-linux-x64-gnu": "4.3.
|
|
93
|
-
"code-gauge-linux-arm64-gnu": "4.3.
|
|
94
|
-
"code-gauge-linux-x64-musl": "4.3.
|
|
95
|
-
"code-gauge-linux-arm64-musl": "4.3.
|
|
96
|
-
"code-gauge-darwin-x64": "4.3.
|
|
97
|
-
"code-gauge-darwin-arm64": "4.3.
|
|
98
|
-
"code-gauge-win32-x64-msvc": "4.3.
|
|
92
|
+
"code-gauge-linux-x64-gnu": "4.3.1",
|
|
93
|
+
"code-gauge-linux-arm64-gnu": "4.3.1",
|
|
94
|
+
"code-gauge-linux-x64-musl": "4.3.1",
|
|
95
|
+
"code-gauge-linux-arm64-musl": "4.3.1",
|
|
96
|
+
"code-gauge-darwin-x64": "4.3.1",
|
|
97
|
+
"code-gauge-darwin-arm64": "4.3.1",
|
|
98
|
+
"code-gauge-win32-x64-msvc": "4.3.1"
|
|
99
99
|
}
|
|
100
100
|
}
|
package/native/src/cyclomatic.rs
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
use tree_sitter::Node;
|
|
2
|
-
|
|
3
|
-
use crate::util::{named_children, node_text, Source};
|
|
4
|
-
|
|
5
|
-
/// Cyclomatic paths a Java node adds to its function, following PMD 7.26.0's CycloVisitor with
|
|
6
|
-
/// its default options: branches and loops add 1 plus the boolean paths of their condition,
|
|
7
|
-
/// `throw`/`catch`/enhanced `for` add 1, and a switch adds the boolean paths of its tested
|
|
8
|
-
/// expression plus the expression alternatives of each non-default label. `&&`/`||` outside these
|
|
9
|
-
/// conditions (initializers, returns, arguments) and pattern labels and guards add nothing.
|
|
10
|
-
pub fn pmd_cyclomatic_increment(node: Node<'_>, code: &Source<'_>) -> u64 {
|
|
11
|
-
match node.kind() {
|
|
12
|
-
"if_statement" | "while_statement" | "do_statement" | "for_statement"
|
|
13
|
-
| "ternary_expression" => {
|
|
14
|
-
1 + node
|
|
15
|
-
.child_by_field_name("condition")
|
|
16
|
-
.map_or(0, |condition| {
|
|
17
|
-
boolean_expression_complexity(condition, code)
|
|
18
|
-
})
|
|
19
|
-
}
|
|
20
|
-
"enhanced_for_statement" | "catch_clause" | "throw_statement" => 1,
|
|
21
|
-
"switch_expression" => node
|
|
22
|
-
.child_by_field_name("condition")
|
|
23
|
-
.map_or(0, |condition| {
|
|
24
|
-
boolean_expression_complexity(condition, code)
|
|
25
|
-
}),
|
|
26
|
-
"switch_block_statement_group" | "switch_rule" => named_children(node)
|
|
27
|
-
.into_iter()
|
|
28
|
-
.filter(|child| child.kind() == "switch_label")
|
|
29
|
-
.flat_map(named_children)
|
|
30
|
-
.filter(|child| {
|
|
31
|
-
child.kind() != "guard"
|
|
32
|
-
&& child.kind() != "pattern"
|
|
33
|
-
&& !crate::ncss::COMMENT_NODE_TYPES.contains(&child.kind())
|
|
34
|
-
})
|
|
35
|
-
.count() as u64,
|
|
36
|
-
_ => 0,
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/// PMD's booleanExpressionComplexity: a conditional expression costs 2 plus its parts; any other
|
|
41
|
-
/// expression costs its `&&`/`||` operators. PMD has no parenthesis nodes, so they are unwrapped.
|
|
42
|
-
fn boolean_expression_complexity(expression: Node<'_>, code: &Source<'_>) -> u64 {
|
|
43
|
-
let mut expression = expression;
|
|
44
|
-
while expression.kind() == "parenthesized_expression" {
|
|
45
|
-
let Some(inner) = named_children(expression)
|
|
46
|
-
.into_iter()
|
|
47
|
-
.find(|child| !crate::ncss::COMMENT_NODE_TYPES.contains(&child.kind()))
|
|
48
|
-
else {
|
|
49
|
-
return 0;
|
|
50
|
-
};
|
|
51
|
-
expression = inner;
|
|
52
|
-
}
|
|
53
|
-
if expression.kind() == "ternary_expression" {
|
|
54
|
-
return 2 + ["condition", "consequence", "alternative"]
|
|
55
|
-
.iter()
|
|
56
|
-
.filter_map(|field| expression.child_by_field_name(field))
|
|
57
|
-
.map(|part| boolean_expression_complexity(part, code))
|
|
58
|
-
.sum::<u64>();
|
|
59
|
-
}
|
|
60
|
-
count_conditional_operators(expression, code)
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
/// `&&`/`||` binaries in the subtree, not descending into lambdas or class bodies, which PMD treats
|
|
64
|
-
/// as find boundaries.
|
|
65
|
-
fn count_conditional_operators(node: Node<'_>, code: &Source<'_>) -> u64 {
|
|
66
|
-
if node.kind() == "lambda_expression" || node.kind() == "class_body" {
|
|
67
|
-
return 0;
|
|
68
|
-
}
|
|
69
|
-
let own = u64::from(
|
|
70
|
-
node.kind() == "binary_expression"
|
|
71
|
-
&& node
|
|
72
|
-
.child_by_field_name("operator")
|
|
73
|
-
.is_some_and(|operator| matches!(node_text(operator, code), "&&" | "||")),
|
|
74
|
-
);
|
|
75
|
-
own + named_children(node)
|
|
76
|
-
.into_iter()
|
|
77
|
-
.map(|child| count_conditional_operators(child, code))
|
|
78
|
-
.sum::<u64>()
|
|
79
|
-
}
|