code-gauge 4.2.2 → 4.3.0
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 +12 -3
- package/dist/cliConfig.cjs +1 -1
- package/dist/cliConfig.cjs.map +1 -1
- package/dist/cliConfig.js +1 -1
- package/dist/diffCommand.cjs +1 -1
- package/dist/diffCommand.cjs.map +1 -1
- package/dist/diffCommand.js +1 -1
- package/dist/diffCommand.js.map +1 -1
- package/dist/duplication.cjs +1 -1
- package/dist/duplication.cjs.map +1 -1
- package/dist/duplication.js +1 -1
- package/dist/duplication.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/languages.cjs +1 -1
- package/dist/languages.cjs.map +1 -1
- package/dist/languages.d.ts +3 -1
- package/dist/languages.js +1 -1
- package/dist/languages.js.map +1 -1
- package/dist/metrics.cjs +1 -1
- package/dist/metrics.cjs.map +1 -1
- package/dist/metrics.js +1 -1
- package/dist/metrics.js.map +1 -1
- package/dist/nativeMetrics.cjs +1 -1
- package/dist/nativeMetrics.cjs.map +1 -1
- package/dist/nativeMetrics.js +1 -1
- package/dist/nativeMetrics.js.map +1 -1
- package/dist/scan.cjs +1 -1
- package/dist/scan.cjs.map +1 -1
- package/dist/scan.js +1 -1
- package/dist/scan.js.map +1 -1
- package/dist/types.d.ts +15 -2
- package/native/src/complexity.rs +123 -34
- package/native/src/cyclomatic.rs +79 -0
- package/native/src/languages.rs +16 -0
- package/native/src/lib.rs +2 -1
- package/native/src/measure.rs +2 -0
- package/native/src/types.rs +2 -0
- package/package.json +14 -14
package/native/src/complexity.rs
CHANGED
|
@@ -12,6 +12,7 @@ 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,
|
|
15
16
|
pub nesting_nodes: HashSet<&'static str>,
|
|
16
17
|
pub ncss_nodes: HashSet<&'static str>,
|
|
17
18
|
pub ncss_containers: HashSet<&'static str>,
|
|
@@ -22,6 +23,7 @@ impl LanguageSets {
|
|
|
22
23
|
LanguageSets {
|
|
23
24
|
function_nodes: language.function_node_types.iter().copied().collect(),
|
|
24
25
|
decision_nodes: language.decision_node_types.iter().copied().collect(),
|
|
26
|
+
pmd_cyclomatic: language.pmd_cyclomatic,
|
|
25
27
|
nesting_nodes: language.nesting_node_types.iter().copied().collect(),
|
|
26
28
|
ncss_nodes: language.ncss_node_types.iter().copied().collect(),
|
|
27
29
|
ncss_containers: language.ncss_container_node_types.iter().copied().collect(),
|
|
@@ -74,7 +76,7 @@ const SWITCH_LIKE_NODE_TYPES: &[&str] = &[
|
|
|
74
76
|
"case_match",
|
|
75
77
|
];
|
|
76
78
|
|
|
77
|
-
// Per-case decision nodes
|
|
79
|
+
// Per-case decision nodes: cyclomatic-only, because the switch itself carries the cognitive cost.
|
|
78
80
|
const CASE_CLAUSE_NODE_TYPES: &[&str] = &[
|
|
79
81
|
"case_clause",
|
|
80
82
|
"switch_case",
|
|
@@ -95,6 +97,7 @@ const CASE_CLAUSE_NODE_TYPES: &[&str] = &[
|
|
|
95
97
|
const IF_LIKE_NODE_TYPES: &[&str] = &["if_statement", "if_expression", "if", "unless"];
|
|
96
98
|
|
|
97
99
|
pub struct FunctionBodyMetrics {
|
|
100
|
+
pub cyclomatic_complexity: u64,
|
|
98
101
|
pub cognitive_complexity: u64,
|
|
99
102
|
pub nesting_depth: u64,
|
|
100
103
|
pub ncss: u64,
|
|
@@ -102,6 +105,7 @@ pub struct FunctionBodyMetrics {
|
|
|
102
105
|
|
|
103
106
|
/// Accumulator for one function body during measure_function_body_metrics' post-order pass.
|
|
104
107
|
struct FunctionBodyFrame {
|
|
108
|
+
cyclomatic_complexity: u64,
|
|
105
109
|
cognitive_complexity: u64,
|
|
106
110
|
/// Count of `1 + nesting` cognitive increments, for re-basing on hoist into the parent frame.
|
|
107
111
|
nesting_sensitive_count: u64,
|
|
@@ -117,6 +121,7 @@ struct FunctionBodyFrame {
|
|
|
117
121
|
impl FunctionBodyFrame {
|
|
118
122
|
fn new(entry_cognitive_nesting: u64, entry_structural_nesting: u64) -> Self {
|
|
119
123
|
FunctionBodyFrame {
|
|
124
|
+
cyclomatic_complexity: 1,
|
|
120
125
|
cognitive_complexity: 0,
|
|
121
126
|
nesting_sensitive_count: 0,
|
|
122
127
|
nesting_depth: 0,
|
|
@@ -141,8 +146,8 @@ struct FunctionBodyPass<'sets, 'code, 'source> {
|
|
|
141
146
|
/// already-computed totals: NCSS hoists as-is; cognitive complexity re-bases the nested function's
|
|
142
147
|
/// nesting-sensitive increments (each worth `1 + nesting`) by the nesting offset at the embedding
|
|
143
148
|
/// site, while flat increments (else branches, boolean-operator sequences, chain continuations,
|
|
144
|
-
/// jumps, guards) hoist unchanged; nesting depth
|
|
145
|
-
///
|
|
149
|
+
/// jumps, guards) hoist unchanged; cyclomatic complexity and nesting depth describe the own body
|
|
150
|
+
/// only, so nothing hoists.
|
|
146
151
|
pub fn measure_function_body_metrics(
|
|
147
152
|
root: Node<'_>,
|
|
148
153
|
sets: &LanguageSets,
|
|
@@ -186,7 +191,7 @@ impl FunctionBodyPass<'_, '_, '_> {
|
|
|
186
191
|
}
|
|
187
192
|
// The node's own increments target the frame it is embedded in, not the one it opens; a
|
|
188
193
|
// frame-opening or charged-class-body node contributes nothing to that frame's own body
|
|
189
|
-
// (nesting), matching the per-function traversal this pass replaces.
|
|
194
|
+
// (cyclomatic/nesting), matching the per-function traversal this pass replaces.
|
|
190
195
|
let entry_cognitive_nesting = self.top_frame().entry_cognitive_nesting;
|
|
191
196
|
let entry_structural_nesting = self.top_frame().entry_structural_nesting;
|
|
192
197
|
let relative_nesting = current_nesting + function_nesting_bonus - entry_cognitive_nesting;
|
|
@@ -211,6 +216,19 @@ impl FunctionBodyPass<'_, '_, '_> {
|
|
|
211
216
|
// surcharge (Sonar cognitive-complexity semantics).
|
|
212
217
|
let is_continuation = is_decision && is_flat_chain_continuation(current);
|
|
213
218
|
|
|
219
|
+
if counts_for_own_body {
|
|
220
|
+
self.top_frame().cyclomatic_complexity += if self.sets.pmd_cyclomatic {
|
|
221
|
+
crate::cyclomatic::pmd_cyclomatic_increment(current, self.code)
|
|
222
|
+
} else {
|
|
223
|
+
// Each boolean operator and pattern guard is one more execution path; `else` adds
|
|
224
|
+
// none.
|
|
225
|
+
u64::from(
|
|
226
|
+
is_decision
|
|
227
|
+
|| is_boolean_operator(current, self.code)
|
|
228
|
+
|| is_pattern_guard(current),
|
|
229
|
+
)
|
|
230
|
+
};
|
|
231
|
+
}
|
|
214
232
|
if is_decision && !is_case_clause {
|
|
215
233
|
if is_continuation {
|
|
216
234
|
self.top_frame().cognitive_complexity += 1;
|
|
@@ -295,6 +313,7 @@ impl FunctionBodyPass<'_, '_, '_> {
|
|
|
295
313
|
self.results.insert(
|
|
296
314
|
current.id(),
|
|
297
315
|
FunctionBodyMetrics {
|
|
316
|
+
cyclomatic_complexity: closed.cyclomatic_complexity,
|
|
298
317
|
cognitive_complexity: closed.cognitive_complexity,
|
|
299
318
|
nesting_depth: closed.nesting_depth,
|
|
300
319
|
// A function node without a countable declaration of its own (arrow functions,
|
|
@@ -621,39 +640,51 @@ fn is_default_switch_branch(node: Node<'_>) -> bool {
|
|
|
621
640
|
return node.child_by_field_name("value").is_none();
|
|
622
641
|
}
|
|
623
642
|
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
643
|
+
// C# `default:` sections and catch-all (`_`, `var x`) labels and arms, and Kotlin `else ->`
|
|
644
|
+
// entries. A guarded catch-all (`_ when cond =>`) is still a default arm: only its guard
|
|
645
|
+
// branches, which is_pattern_guard charges, like Python's `case _ if cond:` and Rust's
|
|
646
|
+
// `_ if cond =>`.
|
|
647
|
+
if kind == "switch_section" {
|
|
648
|
+
return node.child(0).is_some_and(|first| first.kind() == "default")
|
|
649
|
+
|| crate::util::named_children(node)
|
|
650
|
+
.into_iter()
|
|
651
|
+
.any(is_csharp_catch_all_pattern);
|
|
652
|
+
}
|
|
653
|
+
if kind == "switch_expression_arm" {
|
|
654
|
+
return crate::util::named_children(node)
|
|
655
|
+
.first()
|
|
656
|
+
.is_some_and(|first| is_csharp_catch_all_pattern(*first));
|
|
657
|
+
}
|
|
658
|
+
if kind == "when_entry" {
|
|
659
|
+
return !crate::util::named_children(node)
|
|
660
|
+
.iter()
|
|
661
|
+
.any(|child| child.kind() == "when_condition");
|
|
629
662
|
}
|
|
630
663
|
|
|
631
|
-
// Python
|
|
632
|
-
|
|
633
|
-
|
|
664
|
+
// Python arms with an irrefutable pattern are unconditional like `default`.
|
|
665
|
+
// A bare `case y, z:` or `case y,:` is a sequence pattern: its elements are direct
|
|
666
|
+
// case_pattern children separated by comma tokens of the clause itself.
|
|
667
|
+
if kind == "case_clause" {
|
|
668
|
+
let patterns: Vec<Node<'_>> = crate::util::named_children(node)
|
|
634
669
|
.into_iter()
|
|
635
|
-
.
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
&& child.named_child_count() == 1
|
|
654
|
-
&& child
|
|
655
|
-
.named_child(0)
|
|
656
|
-
.is_some_and(|inner| inner.kind() == "identifier")
|
|
670
|
+
.filter(|child| child.kind() == "case_pattern")
|
|
671
|
+
.collect();
|
|
672
|
+
return !all_children(node).iter().any(|child| child.kind() == ",")
|
|
673
|
+
&& matches!(patterns[..], [pattern] if is_python_irrefutable_pattern(pattern));
|
|
674
|
+
}
|
|
675
|
+
// Rust `_ =>` (optionally guarded) fallback arms.
|
|
676
|
+
if kind == "match_arm" {
|
|
677
|
+
return crate::util::named_children(node)
|
|
678
|
+
.into_iter()
|
|
679
|
+
.find(|child| child.kind() == "match_pattern")
|
|
680
|
+
.is_some_and(|pattern| {
|
|
681
|
+
// The guard keyword is anonymous, so filter all children, not just named ones.
|
|
682
|
+
let parts: Vec<Node<'_>> = all_children(pattern)
|
|
683
|
+
.into_iter()
|
|
684
|
+
.filter(|child| !crate::ncss::COMMENT_NODE_TYPES.contains(&child.kind()))
|
|
685
|
+
.collect();
|
|
686
|
+
matches!(parts[..], [first, ..] if first.kind() == "_")
|
|
687
|
+
&& parts.get(1).is_none_or(|second| second.kind() == "if")
|
|
657
688
|
});
|
|
658
689
|
}
|
|
659
690
|
|
|
@@ -667,6 +698,64 @@ fn is_default_switch_branch(node: Node<'_>) -> bool {
|
|
|
667
698
|
false
|
|
668
699
|
}
|
|
669
700
|
|
|
701
|
+
/// PEP 634's irrefutable patterns: the wildcard `_`, a capture `y`, a group `(p)`, `p as y`, and
|
|
702
|
+
/// `p | q` when `p` (or, for `|`, any alternative) is irrefutable. A group parses as a one-element
|
|
703
|
+
/// tuple pattern that differs from the real tuple `(p,)` only by the comma token.
|
|
704
|
+
fn is_python_irrefutable_pattern(node: Node<'_>) -> bool {
|
|
705
|
+
match node.kind() {
|
|
706
|
+
"_" => true,
|
|
707
|
+
"case_pattern" => match non_comment_children(node)[..] {
|
|
708
|
+
[] => all_children(node).iter().any(|child| child.kind() == "_"),
|
|
709
|
+
[inner] => is_python_irrefutable_pattern(inner),
|
|
710
|
+
_ => false,
|
|
711
|
+
},
|
|
712
|
+
"dotted_name" => matches!(
|
|
713
|
+
non_comment_children(node)[..],
|
|
714
|
+
[name] if name.kind() == "identifier"
|
|
715
|
+
),
|
|
716
|
+
"tuple_pattern" => {
|
|
717
|
+
!all_children(node).iter().any(|child| child.kind() == ",")
|
|
718
|
+
&& matches!(
|
|
719
|
+
non_comment_children(node)[..],
|
|
720
|
+
[inner] if is_python_irrefutable_pattern(inner)
|
|
721
|
+
)
|
|
722
|
+
}
|
|
723
|
+
"as_pattern" => non_comment_children(node)
|
|
724
|
+
.first()
|
|
725
|
+
.is_some_and(|pattern| is_python_irrefutable_pattern(*pattern)),
|
|
726
|
+
"union_pattern" => all_children(node)
|
|
727
|
+
.into_iter()
|
|
728
|
+
.any(is_python_irrefutable_pattern),
|
|
729
|
+
_ => false,
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
fn non_comment_children<'t>(node: Node<'t>) -> Vec<Node<'t>> {
|
|
734
|
+
crate::util::named_children(node)
|
|
735
|
+
.into_iter()
|
|
736
|
+
.filter(|child| !crate::ncss::COMMENT_NODE_TYPES.contains(&child.kind()))
|
|
737
|
+
.collect()
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/// C# patterns that match every value: the discard `_` and `var x`/`var _`, possibly parenthesized
|
|
741
|
+
/// (but not a `var (a, b)` deconstruction, which requires a deconstructible value).
|
|
742
|
+
fn is_csharp_catch_all_pattern(node: Node<'_>) -> bool {
|
|
743
|
+
if node.kind() == "parenthesized_pattern" {
|
|
744
|
+
return crate::util::named_children(node)
|
|
745
|
+
.into_iter()
|
|
746
|
+
.find(|child| !crate::ncss::COMMENT_NODE_TYPES.contains(&child.kind()))
|
|
747
|
+
.is_some_and(is_csharp_catch_all_pattern);
|
|
748
|
+
}
|
|
749
|
+
node.kind() == "discard"
|
|
750
|
+
|| (node.kind() == "declaration_pattern"
|
|
751
|
+
&& node
|
|
752
|
+
.child_by_field_name("type")
|
|
753
|
+
.is_some_and(|ty| ty.kind() == "implicit_type")
|
|
754
|
+
&& !crate::util::named_children(node)
|
|
755
|
+
.iter()
|
|
756
|
+
.any(|child| child.kind() == "parenthesized_variable_designation"))
|
|
757
|
+
}
|
|
758
|
+
|
|
670
759
|
/// The parent guard is required because the same tokens appear in non-boolean syntax (C++ `int&&`,
|
|
671
760
|
/// `operator&&`, Rust's empty closure parameter list `|| 5`).
|
|
672
761
|
fn is_boolean_operator(node: Node<'_>, code: &Source<'_>) -> bool {
|
|
@@ -0,0 +1,79 @@
|
|
|
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
|
+
}
|
package/native/src/languages.rs
CHANGED
|
@@ -7,6 +7,9 @@ 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,
|
|
10
13
|
pub nesting_node_types: &'static [&'static str],
|
|
11
14
|
pub ncss_node_types: &'static [&'static str],
|
|
12
15
|
pub ncss_container_node_types: &'static [&'static str],
|
|
@@ -656,6 +659,7 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
656
659
|
grammar_id: GrammarId::JavaScript,
|
|
657
660
|
function_node_types: COMMON_FUNCTION_NODES,
|
|
658
661
|
decision_node_types: COMMON_DECISION_NODES,
|
|
662
|
+
pmd_cyclomatic: false,
|
|
659
663
|
nesting_node_types: COMMON_NESTING_NODES,
|
|
660
664
|
ncss_node_types: JS_NCSS_NODES,
|
|
661
665
|
ncss_container_node_types: &[],
|
|
@@ -666,6 +670,7 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
666
670
|
grammar_id: GrammarId::JavaScript,
|
|
667
671
|
function_node_types: COMMON_FUNCTION_NODES,
|
|
668
672
|
decision_node_types: COMMON_DECISION_NODES,
|
|
673
|
+
pmd_cyclomatic: false,
|
|
669
674
|
nesting_node_types: COMMON_NESTING_NODES,
|
|
670
675
|
ncss_node_types: JS_NCSS_NODES,
|
|
671
676
|
ncss_container_node_types: &[],
|
|
@@ -676,6 +681,7 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
676
681
|
grammar_id: GrammarId::TypeScript,
|
|
677
682
|
function_node_types: COMMON_FUNCTION_NODES,
|
|
678
683
|
decision_node_types: COMMON_DECISION_NODES,
|
|
684
|
+
pmd_cyclomatic: false,
|
|
679
685
|
nesting_node_types: COMMON_NESTING_NODES,
|
|
680
686
|
ncss_node_types: JS_NCSS_NODES,
|
|
681
687
|
ncss_container_node_types: &[],
|
|
@@ -686,6 +692,7 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
686
692
|
grammar_id: GrammarId::Tsx,
|
|
687
693
|
function_node_types: COMMON_FUNCTION_NODES,
|
|
688
694
|
decision_node_types: COMMON_DECISION_NODES,
|
|
695
|
+
pmd_cyclomatic: false,
|
|
689
696
|
nesting_node_types: COMMON_NESTING_NODES,
|
|
690
697
|
ncss_node_types: JS_NCSS_NODES,
|
|
691
698
|
ncss_container_node_types: &[],
|
|
@@ -696,6 +703,7 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
696
703
|
grammar_id: GrammarId::Python,
|
|
697
704
|
function_node_types: COMMON_FUNCTION_NODES,
|
|
698
705
|
decision_node_types: COMMON_DECISION_NODES,
|
|
706
|
+
pmd_cyclomatic: false,
|
|
699
707
|
nesting_node_types: COMMON_NESTING_NODES,
|
|
700
708
|
ncss_node_types: PYTHON_NCSS_NODES,
|
|
701
709
|
ncss_container_node_types: &[],
|
|
@@ -706,6 +714,7 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
706
714
|
grammar_id: GrammarId::Go,
|
|
707
715
|
function_node_types: COMMON_FUNCTION_NODES,
|
|
708
716
|
decision_node_types: GO_DECISION_NODES,
|
|
717
|
+
pmd_cyclomatic: false,
|
|
709
718
|
nesting_node_types: GO_NESTING_NODES,
|
|
710
719
|
ncss_node_types: GO_NCSS_NODES,
|
|
711
720
|
ncss_container_node_types: &[],
|
|
@@ -716,6 +725,7 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
716
725
|
grammar_id: GrammarId::Rust,
|
|
717
726
|
function_node_types: COMMON_FUNCTION_NODES,
|
|
718
727
|
decision_node_types: COMMON_DECISION_NODES,
|
|
728
|
+
pmd_cyclomatic: false,
|
|
719
729
|
nesting_node_types: COMMON_NESTING_NODES,
|
|
720
730
|
ncss_node_types: RUST_NCSS_NODES,
|
|
721
731
|
ncss_container_node_types: RUST_NCSS_CONTAINERS,
|
|
@@ -726,6 +736,7 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
726
736
|
grammar_id: GrammarId::Java,
|
|
727
737
|
function_node_types: JAVA_FUNCTION_NODES,
|
|
728
738
|
decision_node_types: JAVA_DECISION_NODES,
|
|
739
|
+
pmd_cyclomatic: true,
|
|
729
740
|
nesting_node_types: JAVA_DECISION_NODES,
|
|
730
741
|
ncss_node_types: JAVA_NCSS_NODES,
|
|
731
742
|
ncss_container_node_types: &[],
|
|
@@ -736,6 +747,7 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
736
747
|
grammar_id: GrammarId::Ruby,
|
|
737
748
|
function_node_types: RUBY_FUNCTION_NODES,
|
|
738
749
|
decision_node_types: RUBY_DECISION_NODES,
|
|
750
|
+
pmd_cyclomatic: false,
|
|
739
751
|
nesting_node_types: RUBY_DECISION_NODES,
|
|
740
752
|
ncss_node_types: RUBY_NCSS_NODES,
|
|
741
753
|
ncss_container_node_types: RUBY_NCSS_CONTAINERS,
|
|
@@ -746,6 +758,7 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
746
758
|
grammar_id: GrammarId::C,
|
|
747
759
|
function_node_types: C_FUNCTION_NODES,
|
|
748
760
|
decision_node_types: C_DECISION_NODES,
|
|
761
|
+
pmd_cyclomatic: false,
|
|
749
762
|
nesting_node_types: C_DECISION_NODES,
|
|
750
763
|
ncss_node_types: C_NCSS_NODES,
|
|
751
764
|
ncss_container_node_types: &[],
|
|
@@ -756,6 +769,7 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
756
769
|
grammar_id: GrammarId::Cpp,
|
|
757
770
|
function_node_types: C_FUNCTION_NODES,
|
|
758
771
|
decision_node_types: CPP_DECISION_NODES,
|
|
772
|
+
pmd_cyclomatic: false,
|
|
759
773
|
nesting_node_types: CPP_DECISION_NODES,
|
|
760
774
|
ncss_node_types: CPP_NCSS_NODES,
|
|
761
775
|
ncss_container_node_types: &[],
|
|
@@ -766,6 +780,7 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
766
780
|
grammar_id: GrammarId::CSharp,
|
|
767
781
|
function_node_types: CSHARP_FUNCTION_NODES,
|
|
768
782
|
decision_node_types: CSHARP_DECISION_NODES,
|
|
783
|
+
pmd_cyclomatic: false,
|
|
769
784
|
nesting_node_types: CSHARP_DECISION_NODES,
|
|
770
785
|
ncss_node_types: CSHARP_NCSS_NODES,
|
|
771
786
|
ncss_container_node_types: &[],
|
|
@@ -776,6 +791,7 @@ pub const LANGUAGES: &[LanguageDefinition] = &[
|
|
|
776
791
|
grammar_id: GrammarId::Kotlin,
|
|
777
792
|
function_node_types: KOTLIN_FUNCTION_NODES,
|
|
778
793
|
decision_node_types: KOTLIN_DECISION_NODES,
|
|
794
|
+
pmd_cyclomatic: false,
|
|
779
795
|
nesting_node_types: KOTLIN_DECISION_NODES,
|
|
780
796
|
ncss_node_types: KOTLIN_NCSS_NODES,
|
|
781
797
|
ncss_container_node_types: KOTLIN_NCSS_CONTAINERS,
|
package/native/src/lib.rs
CHANGED
|
@@ -6,6 +6,7 @@ use napi_derive::napi;
|
|
|
6
6
|
use crate::duplication::DuplicationSettings;
|
|
7
7
|
|
|
8
8
|
mod complexity;
|
|
9
|
+
mod cyclomatic;
|
|
9
10
|
mod dep_degree;
|
|
10
11
|
mod duplication;
|
|
11
12
|
mod functions;
|
|
@@ -21,7 +22,7 @@ mod util;
|
|
|
21
22
|
/// together with `expectedPayloadVersion` in src/nativeMetrics.ts.
|
|
22
23
|
#[napi]
|
|
23
24
|
pub fn payload_version() -> u32 {
|
|
24
|
-
|
|
25
|
+
5
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
/// Measures code metrics for the given source, returning the NativeMetrics payload as JSON.
|
package/native/src/measure.rs
CHANGED
|
@@ -53,6 +53,8 @@ pub fn measure(
|
|
|
53
53
|
// yields the JavaScript string (UTF-16 code unit) column.
|
|
54
54
|
start_column: node.start_position().column / 2,
|
|
55
55
|
end_line: node.end_position().row + 1,
|
|
56
|
+
end_column: node.end_position().column / 2,
|
|
57
|
+
cyclomatic_complexity: body_metrics.cyclomatic_complexity,
|
|
56
58
|
// Sonar's written spec adds +1 cognitive complexity per function in a recursion
|
|
57
59
|
// cycle, but this is intentionally not implemented (issue #22): mainstream
|
|
58
60
|
// implementations (PMD, SonarQube analyzers) omit it.
|
package/native/src/types.rs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-gauge",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.0",
|
|
4
4
|
"description": "Measure code metrics with tree-sitter.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -65,16 +65,16 @@
|
|
|
65
65
|
"@tsconfig/bun": "1.0.10",
|
|
66
66
|
"@tsconfig/node-lts": "24.0.0",
|
|
67
67
|
"@tsconfig/node-ts": "23.6.4",
|
|
68
|
-
"@types/bun": "1.4.
|
|
68
|
+
"@types/bun": "1.4.2",
|
|
69
69
|
"@types/node": "25.9.4",
|
|
70
70
|
"@willbooster/oxfmt-config": "1.2.2",
|
|
71
71
|
"@willbooster/oxlint-config": "1.4.8",
|
|
72
|
-
"@willbooster/wb": "22.
|
|
73
|
-
"build-ts": "21.0.
|
|
72
|
+
"@willbooster/wb": "22.7.0",
|
|
73
|
+
"build-ts": "21.0.16",
|
|
74
74
|
"conventional-changelog-conventionalcommits": "9.3.1",
|
|
75
|
-
"lefthook": "2.1.
|
|
76
|
-
"oxfmt": "0.
|
|
77
|
-
"oxlint": "1.
|
|
75
|
+
"lefthook": "2.1.14",
|
|
76
|
+
"oxfmt": "0.68.0",
|
|
77
|
+
"oxlint": "1.83.0",
|
|
78
78
|
"oxlint-tsgolint": "7.0.2001",
|
|
79
79
|
"semantic-release": "25.0.5",
|
|
80
80
|
"sort-package-json": "4.0.0",
|
|
@@ -89,12 +89,12 @@
|
|
|
89
89
|
"registry": "https://registry.npmjs.org/"
|
|
90
90
|
},
|
|
91
91
|
"optionalDependencies": {
|
|
92
|
-
"code-gauge-linux-x64-gnu": "4.
|
|
93
|
-
"code-gauge-linux-arm64-gnu": "4.
|
|
94
|
-
"code-gauge-linux-x64-musl": "4.
|
|
95
|
-
"code-gauge-linux-arm64-musl": "4.
|
|
96
|
-
"code-gauge-darwin-x64": "4.
|
|
97
|
-
"code-gauge-darwin-arm64": "4.
|
|
98
|
-
"code-gauge-win32-x64-msvc": "4.
|
|
92
|
+
"code-gauge-linux-x64-gnu": "4.3.0",
|
|
93
|
+
"code-gauge-linux-arm64-gnu": "4.3.0",
|
|
94
|
+
"code-gauge-linux-x64-musl": "4.3.0",
|
|
95
|
+
"code-gauge-linux-arm64-musl": "4.3.0",
|
|
96
|
+
"code-gauge-darwin-x64": "4.3.0",
|
|
97
|
+
"code-gauge-darwin-arm64": "4.3.0",
|
|
98
|
+
"code-gauge-win32-x64-msvc": "4.3.0"
|
|
99
99
|
}
|
|
100
100
|
}
|