code-gauge 10.2.4 → 10.4.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 +47 -5
- package/THIRD-PARTY-NOTICES.txt +36 -12
- package/dist/allowDirectives.cjs +2 -0
- package/dist/allowDirectives.cjs.map +1 -0
- package/dist/allowDirectives.d.ts +45 -0
- package/dist/allowDirectives.js +2 -0
- package/dist/allowDirectives.js.map +1 -0
- package/dist/checkCommand.cjs +3 -3
- package/dist/checkCommand.cjs.map +1 -1
- package/dist/checkCommand.d.ts +1 -1
- package/dist/checkCommand.js +3 -3
- package/dist/checkCommand.js.map +1 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/cliConfig.cjs.map +1 -1
- package/dist/cliConfig.js.map +1 -1
- package/dist/crossFileDuplication.cjs.map +1 -1
- package/dist/crossFileDuplication.js.map +1 -1
- package/dist/crossFileNearMiss.cjs +1 -1
- package/dist/crossFileNearMiss.cjs.map +1 -1
- package/dist/crossFileNearMiss.js +1 -1
- package/dist/crossFileNearMiss.js.map +1 -1
- package/dist/duplicateSelection.cjs.map +1 -1
- package/dist/duplicateSelection.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/exclusion.cjs.map +1 -1
- package/dist/exclusion.js.map +1 -1
- package/dist/git.cjs +2 -1
- package/dist/git.cjs.map +1 -1
- package/dist/git.d.ts +7 -0
- package/dist/git.js +2 -1
- package/dist/git.js.map +1 -1
- package/dist/languages.cjs.map +1 -1
- package/dist/languages.js.map +1 -1
- package/dist/metrics.cjs.map +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/thresholdCheck.cjs +1 -1
- package/dist/thresholdCheck.cjs.map +1 -1
- package/dist/thresholdCheck.d.ts +15 -1
- package/dist/thresholdCheck.js +1 -1
- package/dist/thresholdCheck.js.map +1 -1
- package/dist/thresholds.cjs.map +1 -1
- package/dist/thresholds.js.map +1 -1
- package/dist/wasmBinding.cjs.map +1 -1
- package/dist/wasmBinding.js.map +1 -1
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.js.map +1 -1
- package/native/Cargo.lock +24 -24
- package/native/Cargo.toml +12 -12
- package/native/code-gauge.wasm +0 -0
- package/native/src/complexity.rs +28 -22
- package/native/src/duplication.rs +37 -3
- package/native/src/functions.rs +10 -12
- package/native/src/languages.rs +2 -3
- package/native/src/ncss.rs +29 -12
- package/native/src/util.rs +6 -0
- package/package.json +39 -43
package/native/Cargo.toml
CHANGED
|
@@ -22,18 +22,18 @@ rustc-hash = "2"
|
|
|
22
22
|
# `willbooster-*` crates are WillBooster's forks of the runtime and grammars. Every grammar crate
|
|
23
23
|
# depends only on `tree-sitter-language`, never on upstream's `tree-sitter` crate, whose
|
|
24
24
|
# `links = "tree-sitter"` would conflict with the forked runtime's.
|
|
25
|
-
tree-sitter = { package = "willbooster-tree-sitter", version = "
|
|
26
|
-
tree-sitter-c = { package = "willbooster-tree-sitter-c", version = "=
|
|
27
|
-
tree-sitter-c-sharp = { package = "willbooster-tree-sitter-c-sharp", version = "=
|
|
28
|
-
tree-sitter-cpp = { package = "willbooster-tree-sitter-cpp", version = "=
|
|
29
|
-
tree-sitter-go = { package = "willbooster-tree-sitter-go", version = "=
|
|
30
|
-
tree-sitter-java = { package = "willbooster-tree-sitter-java", version = "=
|
|
31
|
-
tree-sitter-javascript = { package = "willbooster-tree-sitter-javascript", version = "=
|
|
32
|
-
tree-sitter-kotlin = { package = "willbooster-tree-sitter-kotlin", version = "=
|
|
33
|
-
tree-sitter-python = { package = "willbooster-tree-sitter-python", version = "=
|
|
34
|
-
tree-sitter-ruby = { package = "willbooster-tree-sitter-ruby", version = "=
|
|
35
|
-
tree-sitter-rust = { package = "willbooster-tree-sitter-rust", version = "=
|
|
36
|
-
tree-sitter-typescript = { package = "willbooster-tree-sitter-typescript", version = "=
|
|
25
|
+
tree-sitter = { package = "willbooster-tree-sitter", version = "17" }
|
|
26
|
+
tree-sitter-c = { package = "willbooster-tree-sitter-c", version = "=34.0.0" }
|
|
27
|
+
tree-sitter-c-sharp = { package = "willbooster-tree-sitter-c-sharp", version = "=19.0.0" }
|
|
28
|
+
tree-sitter-cpp = { package = "willbooster-tree-sitter-cpp", version = "=28.0.0" }
|
|
29
|
+
tree-sitter-go = { package = "willbooster-tree-sitter-go", version = "=9.0.0" }
|
|
30
|
+
tree-sitter-java = { package = "willbooster-tree-sitter-java", version = "=32.0.0" }
|
|
31
|
+
tree-sitter-javascript = { package = "willbooster-tree-sitter-javascript", version = "=21.1.0" }
|
|
32
|
+
tree-sitter-kotlin = { package = "willbooster-tree-sitter-kotlin", version = "=19.0.0" }
|
|
33
|
+
tree-sitter-python = { package = "willbooster-tree-sitter-python", version = "=16.0.0" }
|
|
34
|
+
tree-sitter-ruby = { package = "willbooster-tree-sitter-ruby", version = "=15.0.0" }
|
|
35
|
+
tree-sitter-rust = { package = "willbooster-tree-sitter-rust", version = "=17.0.0" }
|
|
36
|
+
tree-sitter-typescript = { package = "willbooster-tree-sitter-typescript", version = "=26.0.1" }
|
|
37
37
|
|
|
38
38
|
# The WebAssembly build (src/wasm.rs) exports a plain C ABI instead of N-API.
|
|
39
39
|
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
package/native/code-gauge.wasm
CHANGED
|
Binary file
|
package/native/src/complexity.rs
CHANGED
|
@@ -302,11 +302,7 @@ impl FunctionBodyPass<'_, '_, '_> {
|
|
|
302
302
|
let relative_nesting = current_nesting + function_nesting_bonus - entry_cognitive_nesting;
|
|
303
303
|
let counts_for_own_body = !inside_nested_region && !opens_frame;
|
|
304
304
|
|
|
305
|
-
|
|
306
|
-
// `if` keyword token), so only named nodes count as decisions.
|
|
307
|
-
let is_decision = current.is_named()
|
|
308
|
-
&& self.sets.decision_nodes.contains(current.kind_name())
|
|
309
|
-
&& !is_pathless_switch_branch(current, self.code);
|
|
305
|
+
let is_decision = self.is_decision(current);
|
|
310
306
|
let is_case_clause =
|
|
311
307
|
current.is_named() && CASE_CLAUSE_NODE_TYPES.contains(¤t.kind_name());
|
|
312
308
|
// Ruby's `case ... else` arm is an `else` node; like every other language's default branch
|
|
@@ -513,6 +509,15 @@ impl FunctionBodyPass<'_, '_, '_> {
|
|
|
513
509
|
}
|
|
514
510
|
}
|
|
515
511
|
|
|
512
|
+
fn is_decision(&self, node: Node<'_>) -> bool {
|
|
513
|
+
// Anonymous keyword tokens can share a type with named nodes (Ruby's `if` node contains an
|
|
514
|
+
// `if` keyword token), so only named nodes count as decisions.
|
|
515
|
+
node.is_named()
|
|
516
|
+
&& (self.sets.decision_nodes.contains(node.kind_name())
|
|
517
|
+
|| crate::util::is_else_if_clause(node))
|
|
518
|
+
&& !is_pathless_switch_branch(node)
|
|
519
|
+
}
|
|
520
|
+
|
|
516
521
|
fn top_frame(&mut self) -> &mut FunctionBodyFrame {
|
|
517
522
|
self.frames
|
|
518
523
|
.last_mut()
|
|
@@ -534,6 +539,9 @@ fn count_plain_else_branches(current: Node<'_>, parent: Option<Node<'_>>) -> u64
|
|
|
534
539
|
return u64::from(!is_case_else_parent(parent));
|
|
535
540
|
}
|
|
536
541
|
if kind == "else_clause" {
|
|
542
|
+
if crate::util::is_else_if_clause(current) {
|
|
543
|
+
return 0;
|
|
544
|
+
}
|
|
537
545
|
let has_if_like_child = crate::util::named_children(current)
|
|
538
546
|
.iter()
|
|
539
547
|
.any(|child| IF_LIKE_NODE_TYPES.contains(&child.kind_name()));
|
|
@@ -671,10 +679,11 @@ fn is_pattern_guard(node: Node<'_>, parent: Option<Node<'_>>) -> bool {
|
|
|
671
679
|
.any(|child| !child.is_named() && child.kind_name() == "if")
|
|
672
680
|
}
|
|
673
681
|
|
|
674
|
-
/// Ruby `elsif`, Python `elif`, and `else if` (an if node in an
|
|
682
|
+
/// Ruby `elsif`, Python `elif`, Rust `else if` clauses, and `else if` (an if node in an
|
|
683
|
+
/// else/alternative position).
|
|
675
684
|
fn is_flat_chain_continuation(node: Node<'_>, parent: Option<Node<'_>>) -> bool {
|
|
676
685
|
let kind = node.kind_name();
|
|
677
|
-
if kind == "elsif" || kind == "elif_clause" {
|
|
686
|
+
if kind == "elsif" || kind == "elif_clause" || crate::util::is_else_if_clause(node) {
|
|
678
687
|
return true;
|
|
679
688
|
}
|
|
680
689
|
if kind != "if_statement" && kind != "if_expression" && kind != "if" {
|
|
@@ -683,9 +692,9 @@ fn is_flat_chain_continuation(node: Node<'_>, parent: Option<Node<'_>>) -> bool
|
|
|
683
692
|
let Some(parent) = parent else {
|
|
684
693
|
return false;
|
|
685
694
|
};
|
|
686
|
-
// JS/C/C++/
|
|
687
|
-
// `alternative`.
|
|
688
|
-
parent.kind_name() == "else_clause"
|
|
695
|
+
// JS/C/C++/C# wrap `else if` in an else clause; Java/Go/Kotlin put it directly in
|
|
696
|
+
// `alternative`. An if inside a Rust else-if clause is its condition, not a continuation.
|
|
697
|
+
(parent.kind_name() == "else_clause" && !crate::util::is_else_if_clause(parent))
|
|
689
698
|
|| parent
|
|
690
699
|
.child_by_field_name("alternative")
|
|
691
700
|
.is_some_and(|alternative| alternative.id() == node.id())
|
|
@@ -696,7 +705,7 @@ fn is_flat_chain_continuation(node: Node<'_>, parent: Option<Node<'_>>) -> bool
|
|
|
696
705
|
/// unless one of its stacked labels is an unguarded default or catch-all (`case 3: default: g();`
|
|
697
706
|
/// and `default: case 3: g();` are the default outcome) or all of them are catch-alls. Guards are
|
|
698
707
|
/// charged separately, so a guarded catch-all never absorbs a case stacked with it.
|
|
699
|
-
fn is_pathless_switch_branch(node: Node<'_
|
|
708
|
+
fn is_pathless_switch_branch(node: Node<'_>) -> bool {
|
|
700
709
|
if is_label_only_case(node) {
|
|
701
710
|
return true;
|
|
702
711
|
}
|
|
@@ -713,10 +722,8 @@ fn is_pathless_switch_branch(node: Node<'_>, code: &Source<'_>) -> bool {
|
|
|
713
722
|
}
|
|
714
723
|
stacked
|
|
715
724
|
.iter()
|
|
716
|
-
.any(|label| is_default_switch_branch(*label
|
|
717
|
-
|| stacked
|
|
718
|
-
.iter()
|
|
719
|
-
.all(|label| is_default_switch_branch(*label, code))
|
|
725
|
+
.any(|label| is_default_switch_branch(*label) && !has_pattern_guard(*label))
|
|
726
|
+
|| stacked.iter().all(|label| is_default_switch_branch(*label))
|
|
720
727
|
}
|
|
721
728
|
|
|
722
729
|
/// A guard directly on the arm (C# `when`, Python `if`, Ruby `if`/`unless`) or inside its label or
|
|
@@ -754,7 +761,7 @@ fn is_label_only_case(node: Node<'_>) -> bool {
|
|
|
754
761
|
}
|
|
755
762
|
}
|
|
756
763
|
|
|
757
|
-
fn is_default_switch_branch(node: Node<'_
|
|
764
|
+
fn is_default_switch_branch(node: Node<'_>) -> bool {
|
|
758
765
|
let kind = node.kind_name();
|
|
759
766
|
if kind == "switch_default" {
|
|
760
767
|
return true;
|
|
@@ -764,17 +771,16 @@ fn is_default_switch_branch(node: Node<'_>, code: &Source<'_>) -> bool {
|
|
|
764
771
|
}
|
|
765
772
|
|
|
766
773
|
// Java `default:` groups and `default ->` rules: a label with no expression or pattern, or a
|
|
767
|
-
// `case null, default` label,
|
|
774
|
+
// `case null, default` label, which holds the `default` keyword after the `null`.
|
|
768
775
|
if kind == "switch_block_statement_group" || kind == "switch_rule" {
|
|
769
776
|
return non_comment_children(node)
|
|
770
777
|
.into_iter()
|
|
771
778
|
.filter(|child| child.kind_name() == "switch_label")
|
|
772
779
|
.any(|label| {
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
part.
|
|
777
|
-
})
|
|
780
|
+
non_comment_children(label).is_empty()
|
|
781
|
+
|| crate::util::all_children(label)
|
|
782
|
+
.iter()
|
|
783
|
+
.any(|part| !part.is_named() && part.kind_name() == "default")
|
|
778
784
|
});
|
|
779
785
|
}
|
|
780
786
|
|
|
@@ -234,6 +234,7 @@ const STRING_FRAGMENT_TYPES: &[&str] = &[
|
|
|
234
234
|
"raw_string_content",
|
|
235
235
|
"interpreted_string_literal_content",
|
|
236
236
|
"raw_string_literal_content",
|
|
237
|
+
"format_specifier",
|
|
237
238
|
"raw_string_start",
|
|
238
239
|
"raw_string_end",
|
|
239
240
|
"escape_sequence",
|
|
@@ -299,6 +300,7 @@ const STRING_CONTENT_FRAGMENT_TYPES: &[&str] = &[
|
|
|
299
300
|
"raw_string_content",
|
|
300
301
|
"interpreted_string_literal_content",
|
|
301
302
|
"raw_string_literal_content",
|
|
303
|
+
"format_specifier",
|
|
302
304
|
"escape_sequence",
|
|
303
305
|
"heredoc_content",
|
|
304
306
|
];
|
|
@@ -570,6 +572,9 @@ fn collect_tokens<'a>(
|
|
|
570
572
|
statement_list_statements: &mut Vec<TokenRange>,
|
|
571
573
|
) -> TokenRange {
|
|
572
574
|
let start_token_index = tokens.len();
|
|
575
|
+
// The span of the node's tokens, which can be narrower than the node: a Python block
|
|
576
|
+
// starts at its header's colon, and a Rust declaration starts at its doc comment.
|
|
577
|
+
let mut token_span: Option<SourceSpan> = None;
|
|
573
578
|
let is_token = is_token(node);
|
|
574
579
|
let atomic_kind = if is_token {
|
|
575
580
|
None
|
|
@@ -602,6 +607,7 @@ fn collect_tokens<'a>(
|
|
|
602
607
|
container_statement_ranges,
|
|
603
608
|
&mut child_statements,
|
|
604
609
|
);
|
|
610
|
+
token_span = widen_token_span(token_span, &child_range);
|
|
605
611
|
// A Kotlin lambda's parameters sit beside its statements.
|
|
606
612
|
if is_container
|
|
607
613
|
&& child.is_named()
|
|
@@ -625,13 +631,19 @@ fn collect_tokens<'a>(
|
|
|
625
631
|
}
|
|
626
632
|
}
|
|
627
633
|
|
|
628
|
-
let
|
|
629
|
-
start_token_index,
|
|
630
|
-
end_token_index: tokens.len(),
|
|
634
|
+
let span = token_span.unwrap_or(SourceSpan {
|
|
631
635
|
start_index: node.start_byte(),
|
|
632
636
|
end_index: node.end_byte(),
|
|
633
637
|
start_line: node.start_position().row + 1,
|
|
634
638
|
end_line: node.end_position().row + 1,
|
|
639
|
+
});
|
|
640
|
+
let range = TokenRange {
|
|
641
|
+
start_token_index,
|
|
642
|
+
end_token_index: tokens.len(),
|
|
643
|
+
start_index: span.start_index,
|
|
644
|
+
end_index: span.end_index,
|
|
645
|
+
start_line: span.start_line,
|
|
646
|
+
end_line: span.end_line,
|
|
635
647
|
};
|
|
636
648
|
if is_duplicate_block(node) {
|
|
637
649
|
block_ranges.push(TokenRange { ..range });
|
|
@@ -649,6 +661,28 @@ fn collect_tokens<'a>(
|
|
|
649
661
|
);
|
|
650
662
|
}
|
|
651
663
|
|
|
664
|
+
#[derive(Clone, Copy)]
|
|
665
|
+
struct SourceSpan {
|
|
666
|
+
start_index: usize,
|
|
667
|
+
end_index: usize,
|
|
668
|
+
start_line: usize,
|
|
669
|
+
end_line: usize,
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/// `span` extended to the end of `child`'s tokens, or `child`'s token span when `span` is empty;
|
|
673
|
+
/// a child without tokens leaves it as it is.
|
|
674
|
+
fn widen_token_span(span: Option<SourceSpan>, child: &TokenRange) -> Option<SourceSpan> {
|
|
675
|
+
if child.start_token_index == child.end_token_index {
|
|
676
|
+
return span;
|
|
677
|
+
}
|
|
678
|
+
Some(SourceSpan {
|
|
679
|
+
start_index: span.map_or(child.start_index, |span| span.start_index),
|
|
680
|
+
end_index: child.end_index,
|
|
681
|
+
start_line: span.map_or(child.start_line, |span| span.start_line),
|
|
682
|
+
end_line: child.end_line,
|
|
683
|
+
})
|
|
684
|
+
}
|
|
685
|
+
|
|
652
686
|
/// Comments and dependency declarations contribute no tokens, so no clone consists of them; the
|
|
653
687
|
/// tokens around them stay adjacent, so a clone may still extend across one.
|
|
654
688
|
fn is_tokenless(node: Node<'_>, code: &Source<'_>) -> bool {
|
package/native/src/functions.rs
CHANGED
|
@@ -71,11 +71,13 @@ pub fn count_parameters(node: Node<'_>, code: &Source<'_>) -> usize {
|
|
|
71
71
|
return 0;
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
-
// A Java bare lambda parameter (`x -> x + 1`) puts a lone identifier
|
|
75
|
-
// a C# one (`x => x + 1`) is an
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
74
|
+
// A Java bare lambda parameter (`x -> x + 1`, `_ -> 1`) puts a lone identifier or unnamed
|
|
75
|
+
// `underscore_pattern` in the `parameters` field; a C# one (`x => x + 1`) is an
|
|
76
|
+
// `implicit_parameter` leaf.
|
|
77
|
+
if matches!(
|
|
78
|
+
parameters_node.kind_name(),
|
|
79
|
+
"identifier" | "underscore_pattern" | "implicit_parameter"
|
|
80
|
+
) {
|
|
79
81
|
return 1;
|
|
80
82
|
}
|
|
81
83
|
// Kotlin default values (`x: Int = 0`) are siblings of their `parameter`, not children.
|
|
@@ -127,12 +129,7 @@ pub fn count_parameters(node: Node<'_>, code: &Source<'_>) -> usize {
|
|
|
127
129
|
1
|
|
128
130
|
};
|
|
129
131
|
}
|
|
130
|
-
|
|
131
|
-
let anonymous_variadic_count = all_children(parameters_node)
|
|
132
|
-
.iter()
|
|
133
|
-
.filter(|child| !child.is_named() && node_text(**child, code) == "...")
|
|
134
|
-
.count();
|
|
135
|
-
count + anonymous_variadic_count + usize::from(!csharp_params_array_ids.is_empty())
|
|
132
|
+
count + usize::from(!csharp_params_array_ids.is_empty())
|
|
136
133
|
}
|
|
137
134
|
|
|
138
135
|
/// C/C++ `int f(void)` has a `parameter_declaration` whose type is a bare `void` with no declarator.
|
|
@@ -907,7 +904,8 @@ pub fn find_string_literal_content(literal: Node<'_>, code: &Source<'_>) -> Opti
|
|
|
907
904
|
"string_content"
|
|
908
905
|
| "string_fragment"
|
|
909
906
|
| "interpreted_string_literal_content"
|
|
910
|
-
| "raw_string_literal_content"
|
|
907
|
+
| "raw_string_literal_content"
|
|
908
|
+
| "format_specifier" => {
|
|
911
909
|
has_content_node = true;
|
|
912
910
|
content.push_str(node_text(*child, code));
|
|
913
911
|
}
|
package/native/src/languages.rs
CHANGED
|
@@ -434,9 +434,8 @@ const RUST_NCSS_NODES: &[&str] = &[
|
|
|
434
434
|
"else_clause",
|
|
435
435
|
"match_arm",
|
|
436
436
|
];
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
const RUST_NCSS_CONTAINERS: &[&str] = &["block", "else_clause"];
|
|
437
|
+
|
|
438
|
+
const RUST_NCSS_CONTAINERS: &[&str] = &["block"];
|
|
440
439
|
|
|
441
440
|
const JAVA_NCSS_NODES: &[&str] = &[
|
|
442
441
|
"package_declaration",
|
package/native/src/ncss.rs
CHANGED
|
@@ -8,9 +8,9 @@ pub const COMMENT_NODE_TYPES: &[&str] = &["comment", "line_comment", "block_comm
|
|
|
8
8
|
|
|
9
9
|
/// Nodes never counted positionally inside NCSS containers: metadata, empty statements, Ruby
|
|
10
10
|
/// heredoc bodies (tree-sitter emits them as siblings of the statement that opened the heredoc),
|
|
11
|
-
///
|
|
12
|
-
///
|
|
13
|
-
///
|
|
11
|
+
/// TRANSPARENT_WRAPPER_TYPES (their children count instead), Kotlin enum entries (Java enum
|
|
12
|
+
/// constants are not counted either), and Kotlin lambda parameters (siblings of the lambda's
|
|
13
|
+
/// statements). Kotlin's `try_expression` is excluded contextually below because Rust
|
|
14
14
|
/// shares its name: Rust's `?` operator must keep counting as a tail expression.
|
|
15
15
|
const POSITIONAL_EXCLUSION_TYPES: &[&str] = &[
|
|
16
16
|
"attribute_item",
|
|
@@ -18,6 +18,7 @@ const POSITIONAL_EXCLUSION_TYPES: &[&str] = &[
|
|
|
18
18
|
"empty_statement",
|
|
19
19
|
"heredoc_body",
|
|
20
20
|
"parenthesized_statements",
|
|
21
|
+
"attributed_declaration",
|
|
21
22
|
"annotation",
|
|
22
23
|
"file_annotation",
|
|
23
24
|
"shebang",
|
|
@@ -92,6 +93,12 @@ pub fn ncss_contribution(
|
|
|
92
93
|
contribution += 1;
|
|
93
94
|
}
|
|
94
95
|
|
|
96
|
+
// A Rust `else if` clause also stands for the `if` that other grammars nest in their else
|
|
97
|
+
// clause and charge separately.
|
|
98
|
+
if crate::util::is_else_if_clause(node) {
|
|
99
|
+
contribution += 1;
|
|
100
|
+
}
|
|
101
|
+
|
|
95
102
|
// A bare else branch (Java/Go/Kotlin `alternative:` without an else-clause wrapper) counts 1
|
|
96
103
|
// like the `else` keyword does in PMD; an `else if` chain charges the nested if separately on
|
|
97
104
|
// top.
|
|
@@ -117,15 +124,19 @@ fn counts_through_node_type(node: Node<'_>, countable: &FxHashSet<&'static str>)
|
|
|
117
124
|
true
|
|
118
125
|
}
|
|
119
126
|
|
|
120
|
-
///
|
|
121
|
-
///
|
|
127
|
+
/// Ruby's `(foo; bar)` statement parentheses and Rust's attributes-plus-declaration pairs: their
|
|
128
|
+
/// children count when the wrapper itself sits in a container.
|
|
129
|
+
const TRANSPARENT_WRAPPER_TYPES: &[&str] = &["parenthesized_statements", "attributed_declaration"];
|
|
130
|
+
|
|
131
|
+
/// Direct container children count positionally, as do the children of TRANSPARENT_WRAPPER_TYPES
|
|
132
|
+
/// in that position.
|
|
122
133
|
fn is_in_container_position(
|
|
123
134
|
parent: Option<Node<'_>>,
|
|
124
135
|
containers: &FxHashSet<&'static str>,
|
|
125
136
|
) -> bool {
|
|
126
137
|
let mut ancestor = parent;
|
|
127
138
|
while let Some(current) = ancestor {
|
|
128
|
-
if current.kind_name()
|
|
139
|
+
if !TRANSPARENT_WRAPPER_TYPES.contains(¤t.kind_name()) {
|
|
129
140
|
return containers.contains(current.kind_name());
|
|
130
141
|
}
|
|
131
142
|
ancestor = current.parent_node();
|
|
@@ -238,12 +249,18 @@ fn counts_contextually(node: Node<'_>, parent: Option<Node<'_>>) -> bool {
|
|
|
238
249
|
child.kind_name() == "declaration" || child.kind_name() == "function_definition"
|
|
239
250
|
});
|
|
240
251
|
}
|
|
241
|
-
// A Rust item-position macro invocation (`foo! {}` at module level) has no
|
|
242
|
-
// wrapper; the semicolon form does and already counts through it.
|
|
243
|
-
if node.kind_name() == "macro_invocation"
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
252
|
+
// A Rust item-position macro invocation (`foo! {}` at module level, possibly attributed) has no
|
|
253
|
+
// expression_statement wrapper; the semicolon form does and already counts through it.
|
|
254
|
+
if node.kind_name() == "macro_invocation" {
|
|
255
|
+
let item_parent = match parent {
|
|
256
|
+
Some(wrapper) if wrapper.kind_name() == "attributed_declaration" => {
|
|
257
|
+
wrapper.parent_node()
|
|
258
|
+
}
|
|
259
|
+
_ => parent,
|
|
260
|
+
};
|
|
261
|
+
return item_parent.is_some_and(|item_parent| {
|
|
262
|
+
matches!(item_parent.kind_name(), "source_file" | "declaration_list")
|
|
263
|
+
});
|
|
247
264
|
}
|
|
248
265
|
// Go struct fields and interface members count like other languages' member declarations, but
|
|
249
266
|
// only inside a named type declaration; inline anonymous types (`var x struct{ ... }`,
|
package/native/src/util.rs
CHANGED
|
@@ -155,6 +155,12 @@ pub fn to_int32(value: i64) -> i32 {
|
|
|
155
155
|
value as i32
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
+
/// A Rust `else if`: tree-sitter-rust flattens the chain into `else_clause`s that hold the
|
|
159
|
+
/// condition of the `if` they continue, while a final `else` holds only its block.
|
|
160
|
+
pub fn is_else_if_clause(node: Node<'_>) -> bool {
|
|
161
|
+
node.kind_name() == "else_clause" && node.child_by_field_name("condition").is_some()
|
|
162
|
+
}
|
|
163
|
+
|
|
158
164
|
/// Kotlin's `try { } catch { }` shares its node kind with Rust's `?` operator; only the Kotlin form
|
|
159
165
|
/// starts with the `try` keyword.
|
|
160
166
|
pub fn is_kotlin_try_expression(node: Node<'_>) -> bool {
|
package/package.json
CHANGED
|
@@ -1,37 +1,25 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-gauge",
|
|
3
|
-
"version": "10.
|
|
3
|
+
"version": "10.4.0",
|
|
4
4
|
"description": "Measure code metrics with tree-sitter.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
7
|
-
"tree-sitter",
|
|
8
|
-
"metrics",
|
|
9
|
-
"loc",
|
|
10
7
|
"cognitive-complexity",
|
|
11
|
-
"ncss",
|
|
12
|
-
"duplication",
|
|
13
8
|
"csharp",
|
|
9
|
+
"duplication",
|
|
14
10
|
"kotlin",
|
|
15
|
-
"
|
|
11
|
+
"loc",
|
|
12
|
+
"metrics",
|
|
13
|
+
"ncss",
|
|
14
|
+
"refactoring",
|
|
15
|
+
"tree-sitter"
|
|
16
16
|
],
|
|
17
|
+
"license": "Apache-2.0",
|
|
18
|
+
"author": "WillBooster Inc.",
|
|
17
19
|
"repository": {
|
|
18
20
|
"type": "git",
|
|
19
21
|
"url": "git+https://github.com/WillBooster/code-gauge.git"
|
|
20
22
|
},
|
|
21
|
-
"license": "Apache-2.0",
|
|
22
|
-
"author": "WillBooster Inc.",
|
|
23
|
-
"type": "module",
|
|
24
|
-
"exports": {
|
|
25
|
-
".": {
|
|
26
|
-
"types": "./dist/index.d.ts",
|
|
27
|
-
"workerd": "./dist/worker.js",
|
|
28
|
-
"require": "./dist/index.cjs",
|
|
29
|
-
"import": "./dist/index.js"
|
|
30
|
-
}
|
|
31
|
-
},
|
|
32
|
-
"main": "dist/index.cjs",
|
|
33
|
-
"module": "dist/index.js",
|
|
34
|
-
"types": "dist/index.d.ts",
|
|
35
23
|
"bin": "dist/cli.js",
|
|
36
24
|
"files": [
|
|
37
25
|
"dist/",
|
|
@@ -43,6 +31,22 @@
|
|
|
43
31
|
"scripts/buildNative.mjs",
|
|
44
32
|
"THIRD-PARTY-NOTICES.txt"
|
|
45
33
|
],
|
|
34
|
+
"type": "module",
|
|
35
|
+
"main": "dist/index.cjs",
|
|
36
|
+
"module": "dist/index.js",
|
|
37
|
+
"types": "dist/index.d.ts",
|
|
38
|
+
"exports": {
|
|
39
|
+
".": {
|
|
40
|
+
"types": "./dist/index.d.ts",
|
|
41
|
+
"workerd": "./dist/worker.js",
|
|
42
|
+
"require": "./dist/index.cjs",
|
|
43
|
+
"import": "./dist/index.js"
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public",
|
|
48
|
+
"registry": "https://registry.npmjs.org/"
|
|
49
|
+
},
|
|
46
50
|
"scripts": {
|
|
47
51
|
"benchmark": "node scripts/benchmark.mjs",
|
|
48
52
|
"build": "build-ts lib -i src/index.ts -i src/cli.ts -i src/worker.ts --external ../native/code-gauge.wasm",
|
|
@@ -68,37 +72,29 @@
|
|
|
68
72
|
"@tsconfig/bun": "1.0.10",
|
|
69
73
|
"@tsconfig/node-lts": "24.0.0",
|
|
70
74
|
"@tsconfig/node-ts": "23.6.4",
|
|
71
|
-
"@types/bun": "1.4.
|
|
75
|
+
"@types/bun": "1.4.4",
|
|
72
76
|
"@types/node": "25.9.4",
|
|
73
|
-
"@willbooster/
|
|
74
|
-
"@willbooster/
|
|
75
|
-
"
|
|
76
|
-
"build-ts": "21.0.25",
|
|
77
|
+
"@willbooster/oxlint-config": "1.4.9",
|
|
78
|
+
"@willbooster/wb": "23.0.2",
|
|
79
|
+
"build-ts": "21.0.31",
|
|
77
80
|
"conventional-changelog-conventionalcommits": "9.3.1",
|
|
78
|
-
"lefthook": "2.
|
|
79
|
-
"
|
|
80
|
-
"oxlint": "
|
|
81
|
-
"oxlint-tsgolint": "7.0.2003",
|
|
81
|
+
"lefthook": "2.2.2",
|
|
82
|
+
"oxlint": "1.89.0",
|
|
83
|
+
"oxlint-tsgolint": "7.0.2005",
|
|
82
84
|
"semantic-release": "25.0.5",
|
|
83
|
-
"sort-package-json": "4.0.0",
|
|
84
|
-
"typescript": "7.0.2",
|
|
85
85
|
"vitest": "4.1.9",
|
|
86
86
|
"wrangler": "4.135.0"
|
|
87
87
|
},
|
|
88
88
|
"engines": {
|
|
89
89
|
"node": ">=24"
|
|
90
90
|
},
|
|
91
|
-
"publishConfig": {
|
|
92
|
-
"access": "public",
|
|
93
|
-
"registry": "https://registry.npmjs.org/"
|
|
94
|
-
},
|
|
95
91
|
"optionalDependencies": {
|
|
96
|
-
"code-gauge-linux-x64-gnu": "10.
|
|
97
|
-
"code-gauge-linux-arm64-gnu": "10.
|
|
98
|
-
"code-gauge-linux-x64-musl": "10.
|
|
99
|
-
"code-gauge-linux-arm64-musl": "10.
|
|
100
|
-
"code-gauge-darwin-x64": "10.
|
|
101
|
-
"code-gauge-darwin-arm64": "10.
|
|
102
|
-
"code-gauge-win32-x64-msvc": "10.
|
|
92
|
+
"code-gauge-linux-x64-gnu": "10.4.0",
|
|
93
|
+
"code-gauge-linux-arm64-gnu": "10.4.0",
|
|
94
|
+
"code-gauge-linux-x64-musl": "10.4.0",
|
|
95
|
+
"code-gauge-linux-arm64-musl": "10.4.0",
|
|
96
|
+
"code-gauge-darwin-x64": "10.4.0",
|
|
97
|
+
"code-gauge-darwin-arm64": "10.4.0",
|
|
98
|
+
"code-gauge-win32-x64-msvc": "10.4.0"
|
|
103
99
|
}
|
|
104
100
|
}
|