code-gauge 4.7.1 → 4.8.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 +1 -1
- package/THIRD-PARTY-NOTICES.txt +595 -29
- package/dist/cli.cjs +1 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/cliConfig.cjs +1 -1
- package/dist/cliConfig.cjs.map +1 -1
- package/dist/cliConfig.js +1 -1
- package/dist/cliConfig.js.map +1 -1
- package/dist/crossFileDuplication.cjs +1 -1
- package/dist/crossFileDuplication.cjs.map +1 -1
- package/dist/crossFileDuplication.js +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/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/duplicateSelection.cjs +1 -1
- package/dist/duplicateSelection.cjs.map +1 -1
- package/dist/duplicateSelection.js +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/git.cjs +1 -1
- package/dist/git.cjs.map +1 -1
- package/dist/git.js +1 -1
- package/dist/git.js.map +1 -1
- package/dist/languages.cjs +1 -1
- package/dist/languages.cjs.map +1 -1
- package/dist/languages.js +1 -1
- package/dist/languages.js.map +1 -1
- package/dist/regressionGate.cjs +1 -1
- package/dist/regressionGate.cjs.map +1 -1
- package/dist/regressionGate.js +1 -1
- package/dist/regressionGate.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/native/Cargo.lock +47 -15
- package/native/Cargo.toml +9 -9
- package/native/code-gauge.wasm +0 -0
- package/native/src/complexity.rs +26 -33
- package/native/src/dep_degree.rs +43 -32
- package/native/src/duplication.rs +55 -40
- package/native/src/functions.rs +20 -62
- package/native/src/languages.rs +28 -7
- package/native/src/measure.rs +16 -27
- package/native/src/ncss.rs +32 -23
- package/native/src/tree_index.rs +9 -0
- package/native/src/util.rs +12 -57
- package/package.json +10 -10
package/native/src/ncss.rs
CHANGED
|
@@ -4,21 +4,14 @@ use tree_sitter::Node;
|
|
|
4
4
|
use crate::tree_index::NodeExt;
|
|
5
5
|
use crate::util::find_children_by_field_name;
|
|
6
6
|
|
|
7
|
-
pub const COMMENT_NODE_TYPES: &[&str] = &[
|
|
8
|
-
"comment",
|
|
9
|
-
"line_comment",
|
|
10
|
-
"block_comment",
|
|
11
|
-
"multiline_comment",
|
|
12
|
-
];
|
|
7
|
+
pub const COMMENT_NODE_TYPES: &[&str] = &["comment", "line_comment", "block_comment"];
|
|
13
8
|
|
|
14
9
|
/// Nodes never counted positionally inside NCSS containers: metadata, empty statements, Ruby
|
|
15
10
|
/// heredoc bodies (tree-sitter emits them as siblings of the statement that opened the heredoc),
|
|
16
|
-
/// Ruby statement parentheses (transparent wrappers whose children count instead), Kotlin
|
|
17
|
-
///
|
|
18
|
-
///
|
|
19
|
-
///
|
|
20
|
-
/// expression) and `label` (a Rust block label `'outer:` stands for the labeled statement the
|
|
21
|
-
/// other languages count, while a Kotlin `outer@` is a childless token beside its statement).
|
|
11
|
+
/// Ruby statement parentheses (transparent wrappers whose children count instead), Kotlin enum
|
|
12
|
+
/// entries (Java enum constants are not counted either), and Kotlin lambda parameters (siblings of
|
|
13
|
+
/// the lambda's statements). Kotlin's `try_expression` is excluded contextually below because Rust
|
|
14
|
+
/// shares its name: Rust's `?` operator must keep counting as a tail expression.
|
|
22
15
|
const POSITIONAL_EXCLUSION_TYPES: &[&str] = &[
|
|
23
16
|
"attribute_item",
|
|
24
17
|
"inner_attribute_item",
|
|
@@ -27,8 +20,9 @@ const POSITIONAL_EXCLUSION_TYPES: &[&str] = &[
|
|
|
27
20
|
"parenthesized_statements",
|
|
28
21
|
"annotation",
|
|
29
22
|
"file_annotation",
|
|
30
|
-
"
|
|
23
|
+
"shebang",
|
|
31
24
|
"enum_entry",
|
|
25
|
+
"lambda_parameters",
|
|
32
26
|
];
|
|
33
27
|
|
|
34
28
|
/// TypeScript interface members count like Java interface members, but the same node types appear
|
|
@@ -66,6 +60,7 @@ pub fn ncss_contribution(
|
|
|
66
60
|
parent: Option<Node<'_>>,
|
|
67
61
|
countable: &FxHashSet<&'static str>,
|
|
68
62
|
containers: &FxHashSet<&'static str>,
|
|
63
|
+
bare_body_parents: &FxHashSet<&'static str>,
|
|
69
64
|
) -> u64 {
|
|
70
65
|
if !node.is_named()
|
|
71
66
|
|| COMMENT_NODE_TYPES.contains(&node.kind_name())
|
|
@@ -74,7 +69,7 @@ pub fn ncss_contribution(
|
|
|
74
69
|
return 0;
|
|
75
70
|
}
|
|
76
71
|
// A Kotlin accessor without a body (`private set`) only changes visibility and declares
|
|
77
|
-
// nothing of its own
|
|
72
|
+
// nothing of its own, so it must not count through its node type.
|
|
78
73
|
if (node.kind_name() == "getter" || node.kind_name() == "setter")
|
|
79
74
|
&& !crate::functions::is_implemented_function(node)
|
|
80
75
|
{
|
|
@@ -82,11 +77,13 @@ pub fn ncss_contribution(
|
|
|
82
77
|
}
|
|
83
78
|
|
|
84
79
|
let mut contribution = 0;
|
|
85
|
-
|
|
86
|
-
|
|
80
|
+
// A Kotlin lambda holds its statements directly, so it is a container, yet it still counts where
|
|
81
|
+
// it stands like any other expression.
|
|
82
|
+
let positional = (is_in_container_position(parent, containers)
|
|
83
|
+
|| is_bare_body(node, parent, bare_body_parents))
|
|
84
|
+
&& (!containers.contains(node.kind_name()) || node.kind_name() == "lambda_literal")
|
|
87
85
|
&& !POSITIONAL_EXCLUSION_TYPES.contains(&node.kind_name())
|
|
88
|
-
&& !crate::util::is_kotlin_try_expression(node)
|
|
89
|
-
&& !(node.kind_name() == "label" && node.child_count() == 0);
|
|
86
|
+
&& !crate::util::is_kotlin_try_expression(node);
|
|
90
87
|
if (counts_through_node_type(node, countable)
|
|
91
88
|
|| positional
|
|
92
89
|
|| counts_contextually(node, parent))
|
|
@@ -95,12 +92,11 @@ pub fn ncss_contribution(
|
|
|
95
92
|
contribution += 1;
|
|
96
93
|
}
|
|
97
94
|
|
|
98
|
-
// A bare else branch (Java/Go `alternative:` without an else-clause wrapper
|
|
99
|
-
//
|
|
100
|
-
//
|
|
95
|
+
// A bare else branch (Java/Go/Kotlin `alternative:` without an else-clause wrapper) counts 1
|
|
96
|
+
// like the `else` keyword does in PMD; an `else if` chain charges the nested if separately on
|
|
97
|
+
// top.
|
|
101
98
|
if IF_NODE_TYPES.contains(&node.kind_name()) {
|
|
102
|
-
contribution += count_bare_alternatives(node)
|
|
103
|
-
+ u64::from(crate::util::kotlin_else_body(node).is_some());
|
|
99
|
+
contribution += count_bare_alternatives(node);
|
|
104
100
|
}
|
|
105
101
|
|
|
106
102
|
contribution
|
|
@@ -137,6 +133,19 @@ fn is_in_container_position(
|
|
|
137
133
|
false
|
|
138
134
|
}
|
|
139
135
|
|
|
136
|
+
/// A braceless branch or loop body (`if (x) foo()`) stands for the single statement a braced body
|
|
137
|
+
/// would hold.
|
|
138
|
+
fn is_bare_body(
|
|
139
|
+
node: Node<'_>,
|
|
140
|
+
parent: Option<Node<'_>>,
|
|
141
|
+
bare_body_parents: &FxHashSet<&'static str>,
|
|
142
|
+
) -> bool {
|
|
143
|
+
parent.is_some_and(|parent| bare_body_parents.contains(parent.kind_name()))
|
|
144
|
+
&& ["consequence", "alternative", "body"]
|
|
145
|
+
.iter()
|
|
146
|
+
.any(|field_name| is_field_of_parent(node, parent, field_name))
|
|
147
|
+
}
|
|
148
|
+
|
|
140
149
|
/// `export const x = 1` nests a countable declaration inside `export_statement`; only the inner
|
|
141
150
|
/// declaration counts, mirroring how PMD counts one statement per declared entity.
|
|
142
151
|
fn is_declaration_wrapper(node: Node<'_>, countable: &FxHashSet<&'static str>) -> bool {
|
package/native/src/tree_index.rs
CHANGED
|
@@ -12,6 +12,13 @@ thread_local! {
|
|
|
12
12
|
static PARENTS: RefCell<FxHashMap<usize, Node<'static>>> = RefCell::new(FxHashMap::default());
|
|
13
13
|
/// Node kind names of the indexed tree's language, indexed by kind id.
|
|
14
14
|
static KIND_NAMES: Cell<&'static [&'static str]> = const { Cell::new(&[]) };
|
|
15
|
+
/// Name of the indexed tree's language.
|
|
16
|
+
static LANGUAGE_NAME: Cell<&'static str> = const { Cell::new("") };
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/// The language of the tree indexed on this thread, or "" when none is.
|
|
20
|
+
pub fn language_name() -> &'static str {
|
|
21
|
+
LANGUAGE_NAME.get()
|
|
15
22
|
}
|
|
16
23
|
|
|
17
24
|
/// Makes NodeExt lookups O(1) while the tree is being measured: tree-sitter's `Node::parent`
|
|
@@ -30,6 +37,7 @@ impl<'t> TreeIndex<'t> {
|
|
|
30
37
|
// Constructed first so that the depth error below also clears the partial index on drop.
|
|
31
38
|
let index = TreeIndex { tree: PhantomData };
|
|
32
39
|
KIND_NAMES.set(language.kind_names());
|
|
40
|
+
LANGUAGE_NAME.set(language.name);
|
|
33
41
|
PARENTS.with_borrow_mut(|parents| {
|
|
34
42
|
assert!(
|
|
35
43
|
parents.is_empty(),
|
|
@@ -65,6 +73,7 @@ impl Drop for TreeIndex<'_> {
|
|
|
65
73
|
fn drop(&mut self) {
|
|
66
74
|
PARENTS.with_borrow_mut(|parents| parents.clear());
|
|
67
75
|
KIND_NAMES.set(&[]);
|
|
76
|
+
LANGUAGE_NAME.set("");
|
|
68
77
|
}
|
|
69
78
|
}
|
|
70
79
|
|
package/native/src/util.rs
CHANGED
|
@@ -53,30 +53,12 @@ pub fn node_text<'a>(node: Node<'_>, code: &Source<'a>) -> &'a str {
|
|
|
53
53
|
&code.code[code.utf8_offset(node.start_byte())..code.utf8_offset(node.end_byte())]
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
/// Kotlin spells
|
|
57
|
-
///
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
&& node
|
|
62
|
-
parent.kind_name() == "callable_reference"
|
|
63
|
-
&& parent
|
|
64
|
-
.named_child(0)
|
|
65
|
-
.is_some_and(|first| first.id() == node.id())
|
|
66
|
-
})
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/// Whether the node is a leaf for token-level walks. Kotlin soft keywords used as names (`value`,
|
|
70
|
-
/// `data`, `get`, ...) parse as a `simple_identifier` — or its aliases `interpolated_identifier`
|
|
71
|
-
/// (`"$value"`) and `type_identifier` (`value::size`) — wrapping an anonymous keyword token, so a
|
|
72
|
-
/// plain leaf check would see the keyword instead of the identifier. Every other grammar's
|
|
73
|
-
/// identifier kinds are already leaves.
|
|
74
|
-
pub fn is_identifier_leaf(node: Node<'_>) -> bool {
|
|
75
|
-
node.child_count() == 0
|
|
76
|
-
|| matches!(
|
|
77
|
-
node.kind_name(),
|
|
78
|
-
"simple_identifier" | "interpolated_identifier" | "type_identifier"
|
|
79
|
-
)
|
|
56
|
+
/// Kotlin spells `true`, `false`, and `null` as plain identifiers, which the other grammars give
|
|
57
|
+
/// literal node kinds of their own (an identifier with that text, e.g. Go's `null`, is a variable).
|
|
58
|
+
pub fn is_kotlin_keyword_literal(node: Node<'_>, code: &Source<'_>) -> bool {
|
|
59
|
+
crate::tree_index::language_name() == "kotlin"
|
|
60
|
+
&& node.kind_name() == "identifier"
|
|
61
|
+
&& matches!(node_text(node, code), "true" | "false" | "null")
|
|
80
62
|
}
|
|
81
63
|
|
|
82
64
|
pub fn named_children<'t>(node: Node<'t>) -> Vec<Node<'t>> {
|
|
@@ -91,8 +73,8 @@ pub fn all_children<'t>(node: Node<'t>) -> Vec<Node<'t>> {
|
|
|
91
73
|
|
|
92
74
|
/// Children carrying the field, with node-tree-sitter's vendored-core semantics: extra children
|
|
93
75
|
/// (error-recovery nodes, comments) inherit the field of the preceding structural sibling.
|
|
94
|
-
///
|
|
95
|
-
///
|
|
76
|
+
/// The core instead reports no field for extras (ts_node_field_name_for_child returns early for
|
|
77
|
+
/// them), which would desynchronize field-based extraction on malformed source.
|
|
96
78
|
pub fn find_children_by_field_name<'t>(node: Node<'t>, field_name: &str) -> Vec<Node<'t>> {
|
|
97
79
|
let mut children = Vec::new();
|
|
98
80
|
let mut preceding_structural_field: Option<&'static str> = None;
|
|
@@ -166,38 +148,11 @@ pub fn to_int32(value: i64) -> i32 {
|
|
|
166
148
|
value as i32
|
|
167
149
|
}
|
|
168
150
|
|
|
169
|
-
/// The body following a Kotlin `if_expression`'s bare `else` keyword (the grammar has no else
|
|
170
|
-
/// clause node and no fields), or None for other languages' if nodes and else-less ifs.
|
|
171
|
-
pub fn kotlin_else_body(if_node: Node<'_>) -> Option<Node<'_>> {
|
|
172
|
-
if if_node.kind_name() != "if_expression" {
|
|
173
|
-
return None;
|
|
174
|
-
}
|
|
175
|
-
let children = all_children(if_node);
|
|
176
|
-
let else_index = children
|
|
177
|
-
.iter()
|
|
178
|
-
.position(|child| !child.is_named() && child.kind_name() == "else")?;
|
|
179
|
-
children[else_index + 1..]
|
|
180
|
-
.iter()
|
|
181
|
-
.copied()
|
|
182
|
-
.find(|child| child.kind_name() == "control_structure_body")
|
|
183
|
-
}
|
|
184
|
-
|
|
185
151
|
/// Kotlin's `try { } catch { }` shares its node kind with Rust's `?` operator; only the Kotlin form
|
|
186
|
-
///
|
|
152
|
+
/// starts with the `try` keyword.
|
|
187
153
|
pub fn is_kotlin_try_expression(node: Node<'_>) -> bool {
|
|
188
154
|
node.kind_name() == "try_expression"
|
|
189
|
-
&&
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
"statements" | "catch_block" | "finally_block"
|
|
193
|
-
)
|
|
194
|
-
})
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
/// Whether a Kotlin else body is a braceless `else if`: the nested if sits directly in the
|
|
198
|
-
/// control_structure_body, whereas a braced `else { if ... }` wraps it in `statements`.
|
|
199
|
-
pub fn is_kotlin_else_if_body(else_body: Node<'_>) -> bool {
|
|
200
|
-
named_children(else_body)
|
|
201
|
-
.iter()
|
|
202
|
-
.any(|child| child.kind_name() == "if_expression")
|
|
155
|
+
&& node
|
|
156
|
+
.child(0)
|
|
157
|
+
.is_some_and(|keyword| !keyword.is_named() && keyword.kind_name() == "try")
|
|
203
158
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-gauge",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.8.0",
|
|
4
4
|
"description": "Measure code metrics with tree-sitter.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -73,8 +73,8 @@
|
|
|
73
73
|
"@types/node": "25.9.4",
|
|
74
74
|
"@willbooster/oxfmt-config": "1.2.2",
|
|
75
75
|
"@willbooster/oxlint-config": "1.4.8",
|
|
76
|
-
"@willbooster/wb": "22.
|
|
77
|
-
"build-ts": "21.0.
|
|
76
|
+
"@willbooster/wb": "22.8.0",
|
|
77
|
+
"build-ts": "21.0.19",
|
|
78
78
|
"conventional-changelog-conventionalcommits": "9.3.1",
|
|
79
79
|
"lefthook": "2.1.14",
|
|
80
80
|
"oxfmt": "0.68.0",
|
|
@@ -94,12 +94,12 @@
|
|
|
94
94
|
"registry": "https://registry.npmjs.org/"
|
|
95
95
|
},
|
|
96
96
|
"optionalDependencies": {
|
|
97
|
-
"code-gauge-linux-x64-gnu": "4.
|
|
98
|
-
"code-gauge-linux-arm64-gnu": "4.
|
|
99
|
-
"code-gauge-linux-x64-musl": "4.
|
|
100
|
-
"code-gauge-linux-arm64-musl": "4.
|
|
101
|
-
"code-gauge-darwin-x64": "4.
|
|
102
|
-
"code-gauge-darwin-arm64": "4.
|
|
103
|
-
"code-gauge-win32-x64-msvc": "4.
|
|
97
|
+
"code-gauge-linux-x64-gnu": "4.8.0",
|
|
98
|
+
"code-gauge-linux-arm64-gnu": "4.8.0",
|
|
99
|
+
"code-gauge-linux-x64-musl": "4.8.0",
|
|
100
|
+
"code-gauge-linux-arm64-musl": "4.8.0",
|
|
101
|
+
"code-gauge-darwin-x64": "4.8.0",
|
|
102
|
+
"code-gauge-darwin-arm64": "4.8.0",
|
|
103
|
+
"code-gauge-win32-x64-msvc": "4.8.0"
|
|
104
104
|
}
|
|
105
105
|
}
|