code-gauge 4.1.3 → 4.2.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 +6 -6
- 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/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 +1 -1
- package/native/Cargo.lock +22 -0
- package/native/Cargo.toml +2 -0
- package/native/src/complexity.rs +55 -6
- package/native/src/dep_degree.rs +103 -9
- package/native/src/duplication.rs +197 -18
- package/native/src/functions.rs +202 -9
- package/native/src/languages.rs +142 -0
- package/native/src/measure.rs +76 -10
- package/native/src/ncss.rs +31 -6
- package/native/src/util.rs +59 -0
- package/package.json +10 -8
package/native/src/ncss.rs
CHANGED
|
@@ -3,17 +3,31 @@ use tree_sitter::Node;
|
|
|
3
3
|
|
|
4
4
|
use crate::util::{all_children, find_children_by_field_name};
|
|
5
5
|
|
|
6
|
-
pub const COMMENT_NODE_TYPES: &[&str] = &[
|
|
6
|
+
pub const COMMENT_NODE_TYPES: &[&str] = &[
|
|
7
|
+
"comment",
|
|
8
|
+
"line_comment",
|
|
9
|
+
"block_comment",
|
|
10
|
+
"multiline_comment",
|
|
11
|
+
];
|
|
7
12
|
|
|
8
13
|
/// Nodes never counted positionally inside NCSS containers: metadata, empty statements, Ruby
|
|
9
14
|
/// heredoc bodies (tree-sitter emits them as siblings of the statement that opened the heredoc),
|
|
10
|
-
///
|
|
15
|
+
/// Ruby statement parentheses (transparent wrappers whose children count instead), Kotlin
|
|
16
|
+
/// annotations (siblings of the statement they decorate), and Kotlin enum entries (Java enum
|
|
17
|
+
/// constants are not counted either). Two Kotlin kinds are excluded contextually below because
|
|
18
|
+
/// Rust shares their names: `try_expression` (Rust's `?` operator must keep counting as a tail
|
|
19
|
+
/// expression) and `label` (a Rust block label `'outer:` stands for the labeled statement the
|
|
20
|
+
/// other languages count, while a Kotlin `outer@` is a childless token beside its statement).
|
|
11
21
|
const POSITIONAL_EXCLUSION_TYPES: &[&str] = &[
|
|
12
22
|
"attribute_item",
|
|
13
23
|
"inner_attribute_item",
|
|
14
24
|
"empty_statement",
|
|
15
25
|
"heredoc_body",
|
|
16
26
|
"parenthesized_statements",
|
|
27
|
+
"annotation",
|
|
28
|
+
"file_annotation",
|
|
29
|
+
"shebang_line",
|
|
30
|
+
"enum_entry",
|
|
17
31
|
];
|
|
18
32
|
|
|
19
33
|
/// TypeScript interface members count like Java interface members, but the same node types appear
|
|
@@ -75,21 +89,32 @@ pub fn ncss_contribution(
|
|
|
75
89
|
if !node.is_named() || COMMENT_NODE_TYPES.contains(&node.kind()) || is_for_header_node(node) {
|
|
76
90
|
return 0;
|
|
77
91
|
}
|
|
92
|
+
// A Kotlin accessor without a body (`private set`) only changes visibility and declares
|
|
93
|
+
// nothing of its own; it parses as a sibling of its property and must not count positionally.
|
|
94
|
+
if (node.kind() == "getter" || node.kind() == "setter")
|
|
95
|
+
&& !crate::functions::is_implemented_function(node)
|
|
96
|
+
{
|
|
97
|
+
return 0;
|
|
98
|
+
}
|
|
78
99
|
|
|
79
100
|
let mut contribution = 0;
|
|
80
101
|
let positional = is_in_container_position(node, containers)
|
|
81
102
|
&& !containers.contains(node.kind())
|
|
82
|
-
&& !POSITIONAL_EXCLUSION_TYPES.contains(&node.kind())
|
|
103
|
+
&& !POSITIONAL_EXCLUSION_TYPES.contains(&node.kind())
|
|
104
|
+
&& !crate::util::is_kotlin_try_expression(node)
|
|
105
|
+
&& !(node.kind() == "label" && node.child_count() == 0);
|
|
83
106
|
if (counts_through_node_type(node, countable) || positional || counts_contextually(node))
|
|
84
107
|
&& !is_declaration_wrapper(node, countable)
|
|
85
108
|
{
|
|
86
109
|
contribution += 1;
|
|
87
110
|
}
|
|
88
111
|
|
|
89
|
-
// A bare else branch (Java/Go `alternative:` without an else-clause wrapper
|
|
90
|
-
// `else` keyword does in PMD; an `else if` chain charges the
|
|
112
|
+
// A bare else branch (Java/Go `alternative:` without an else-clause wrapper, or Kotlin's bare
|
|
113
|
+
// `else` keyword) counts 1 like the `else` keyword does in PMD; an `else if` chain charges the
|
|
114
|
+
// nested if separately on top.
|
|
91
115
|
if IF_NODE_TYPES.contains(&node.kind()) {
|
|
92
|
-
contribution += count_bare_alternatives(node)
|
|
116
|
+
contribution += count_bare_alternatives(node)
|
|
117
|
+
+ u64::from(crate::util::kotlin_else_body(node).is_some());
|
|
93
118
|
}
|
|
94
119
|
|
|
95
120
|
contribution
|
package/native/src/util.rs
CHANGED
|
@@ -51,6 +51,32 @@ pub fn node_text<'a>(node: Node<'_>, code: &Source<'a>) -> &'a str {
|
|
|
51
51
|
&code.code[code.utf8_offset(node.start_byte())..code.utf8_offset(node.end_byte())]
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
+
/// Kotlin spells the bound receiver of a callable reference (`xs::size`) as a `type_identifier`,
|
|
55
|
+
/// the same kind as an unbound type (`List::size`); the receiver position is what distinguishes
|
|
56
|
+
/// it, and only a visible definition then tells a variable from a type.
|
|
57
|
+
pub fn is_kotlin_callable_receiver(node: Node<'_>) -> bool {
|
|
58
|
+
node.kind() == "type_identifier"
|
|
59
|
+
&& node.parent().is_some_and(|parent| {
|
|
60
|
+
parent.kind() == "callable_reference"
|
|
61
|
+
&& parent
|
|
62
|
+
.named_child(0)
|
|
63
|
+
.is_some_and(|first| first.id() == node.id())
|
|
64
|
+
})
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/// Whether the node is a leaf for token-level walks. Kotlin soft keywords used as names (`value`,
|
|
68
|
+
/// `data`, `get`, ...) parse as a `simple_identifier` — or its aliases `interpolated_identifier`
|
|
69
|
+
/// (`"$value"`) and `type_identifier` (`value::size`) — wrapping an anonymous keyword token, so a
|
|
70
|
+
/// plain leaf check would see the keyword instead of the identifier. Every other grammar's
|
|
71
|
+
/// identifier kinds are already leaves.
|
|
72
|
+
pub fn is_identifier_leaf(node: Node<'_>) -> bool {
|
|
73
|
+
node.child_count() == 0
|
|
74
|
+
|| matches!(
|
|
75
|
+
node.kind(),
|
|
76
|
+
"simple_identifier" | "interpolated_identifier" | "type_identifier"
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
|
|
54
80
|
pub fn named_children<'t>(node: Node<'t>) -> Vec<Node<'t>> {
|
|
55
81
|
let mut cursor = node.walk();
|
|
56
82
|
node.named_children(&mut cursor).collect()
|
|
@@ -137,3 +163,36 @@ pub fn is_js_whitespace(character: char) -> bool {
|
|
|
137
163
|
pub fn to_int32(value: i64) -> i32 {
|
|
138
164
|
value as i32
|
|
139
165
|
}
|
|
166
|
+
|
|
167
|
+
/// The body following a Kotlin `if_expression`'s bare `else` keyword (the grammar has no else
|
|
168
|
+
/// clause node and no fields), or None for other languages' if nodes and else-less ifs.
|
|
169
|
+
pub fn kotlin_else_body(if_node: Node<'_>) -> Option<Node<'_>> {
|
|
170
|
+
if if_node.kind() != "if_expression" {
|
|
171
|
+
return None;
|
|
172
|
+
}
|
|
173
|
+
let children = all_children(if_node);
|
|
174
|
+
let else_index = children
|
|
175
|
+
.iter()
|
|
176
|
+
.position(|child| !child.is_named() && child.kind() == "else")?;
|
|
177
|
+
children[else_index + 1..]
|
|
178
|
+
.iter()
|
|
179
|
+
.copied()
|
|
180
|
+
.find(|child| child.kind() == "control_structure_body")
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/// Kotlin's `try { } catch { }` shares its node kind with Rust's `?` operator; only the Kotlin form
|
|
184
|
+
/// holds a body or clause child.
|
|
185
|
+
pub fn is_kotlin_try_expression(node: Node<'_>) -> bool {
|
|
186
|
+
node.kind() == "try_expression"
|
|
187
|
+
&& named_children(node)
|
|
188
|
+
.iter()
|
|
189
|
+
.any(|child| matches!(child.kind(), "statements" | "catch_block" | "finally_block"))
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/// Whether a Kotlin else body is a braceless `else if`: the nested if sits directly in the
|
|
193
|
+
/// control_structure_body, whereas a braced `else { if ... }` wraps it in `statements`.
|
|
194
|
+
pub fn is_kotlin_else_if_body(else_body: Node<'_>) -> bool {
|
|
195
|
+
named_children(else_body)
|
|
196
|
+
.iter()
|
|
197
|
+
.any(|child| child.kind() == "if_expression")
|
|
198
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "code-gauge",
|
|
3
|
-
"version": "4.1
|
|
3
|
+
"version": "4.2.1",
|
|
4
4
|
"description": "Measure code metrics with tree-sitter.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cli",
|
|
@@ -10,6 +10,8 @@
|
|
|
10
10
|
"cognitive-complexity",
|
|
11
11
|
"ncss",
|
|
12
12
|
"duplication",
|
|
13
|
+
"csharp",
|
|
14
|
+
"kotlin",
|
|
13
15
|
"refactoring"
|
|
14
16
|
],
|
|
15
17
|
"repository": {
|
|
@@ -87,12 +89,12 @@
|
|
|
87
89
|
"registry": "https://registry.npmjs.org/"
|
|
88
90
|
},
|
|
89
91
|
"optionalDependencies": {
|
|
90
|
-
"code-gauge-linux-x64-gnu": "4.1
|
|
91
|
-
"code-gauge-linux-arm64-gnu": "4.1
|
|
92
|
-
"code-gauge-linux-x64-musl": "4.1
|
|
93
|
-
"code-gauge-linux-arm64-musl": "4.1
|
|
94
|
-
"code-gauge-darwin-x64": "4.1
|
|
95
|
-
"code-gauge-darwin-arm64": "4.1
|
|
96
|
-
"code-gauge-win32-x64-msvc": "4.1
|
|
92
|
+
"code-gauge-linux-x64-gnu": "4.2.1",
|
|
93
|
+
"code-gauge-linux-arm64-gnu": "4.2.1",
|
|
94
|
+
"code-gauge-linux-x64-musl": "4.2.1",
|
|
95
|
+
"code-gauge-linux-arm64-musl": "4.2.1",
|
|
96
|
+
"code-gauge-darwin-x64": "4.2.1",
|
|
97
|
+
"code-gauge-darwin-arm64": "4.2.1",
|
|
98
|
+
"code-gauge-win32-x64-msvc": "4.2.1"
|
|
97
99
|
}
|
|
98
100
|
}
|