code-gauge 4.7.0 → 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 +3 -3
- package/dist/diffCommand.cjs.map +1 -1
- package/dist/diffCommand.js +3 -3
- 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/metrics.cjs +1 -1
- package/dist/metrics.cjs.map +1 -1
- package/dist/metrics.d.ts +8 -3
- package/dist/metrics.js +1 -1
- package/dist/metrics.js.map +1 -1
- package/dist/nativeMetrics.cjs +3 -3
- package/dist/nativeMetrics.cjs.map +1 -1
- package/dist/nativeMetrics.d.ts +19 -3
- package/dist/nativeMetrics.js +3 -3
- package/dist/nativeMetrics.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.d.ts +2 -2
- package/dist/scan.js +1 -1
- package/dist/scan.js.map +1 -1
- package/dist/wasmBinding.cjs +1 -1
- package/dist/wasmBinding.cjs.map +1 -1
- package/dist/wasmBinding.js +1 -1
- package/dist/wasmBinding.js.map +1 -1
- package/native/Cargo.lock +48 -15
- package/native/Cargo.toml +11 -10
- package/native/code-gauge.wasm +0 -0
- package/native/src/complexity.rs +152 -255
- package/native/src/dep_degree.rs +79 -65
- package/native/src/duplication.rs +114 -97
- package/native/src/functions.rs +181 -206
- package/native/src/languages.rs +48 -7
- package/native/src/lib.rs +10 -5
- package/native/src/measure.rs +125 -150
- package/native/src/napi.rs +61 -2
- package/native/src/ncss.rs +108 -104
- package/native/src/near_miss.rs +7 -7
- package/native/src/tree_index.rs +119 -0
- package/native/src/util.rs +15 -55
- package/native/src/worker_pool.rs +53 -0
- package/package.json +10 -10
package/native/src/functions.rs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
use
|
|
1
|
+
use rustc_hash::FxHashSet;
|
|
2
2
|
use tree_sitter::Node;
|
|
3
3
|
|
|
4
|
+
use crate::tree_index::NodeExt;
|
|
4
5
|
use crate::util::{all_children, find_children_by_field_name, named_children, node_text, Source};
|
|
5
6
|
|
|
6
7
|
/// Declarations without a body have no control flow, so they are signatures, not functions: C++
|
|
@@ -24,31 +25,34 @@ const BODY_REQUIRED_FUNCTION_TYPES: &[&str] = &[
|
|
|
24
25
|
];
|
|
25
26
|
|
|
26
27
|
pub fn is_implemented_function(node: Node<'_>) -> bool {
|
|
27
|
-
if !BODY_REQUIRED_FUNCTION_TYPES.contains(&node.
|
|
28
|
+
if !BODY_REQUIRED_FUNCTION_TYPES.contains(&node.kind_name())
|
|
28
29
|
|| node.child_by_field_name("body").is_some()
|
|
29
30
|
{
|
|
30
31
|
return true;
|
|
31
32
|
}
|
|
32
33
|
|
|
33
|
-
if node.
|
|
34
|
+
if node.kind_name() == "property_declaration" || node.kind_name() == "indexer_declaration" {
|
|
34
35
|
return node
|
|
35
36
|
.child_by_field_name("value")
|
|
36
|
-
.is_some_and(|value| value.
|
|
37
|
+
.is_some_and(|value| value.kind_name() == "arrow_expression_clause");
|
|
37
38
|
}
|
|
38
39
|
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
if node.
|
|
40
|
+
// Kotlin has no `body` field; an implemented function or accessor holds a `function_body`
|
|
41
|
+
// child.
|
|
42
|
+
if node.kind_name() == "getter"
|
|
43
|
+
|| node.kind_name() == "setter"
|
|
44
|
+
|| node.kind_name() == "function_declaration"
|
|
45
|
+
{
|
|
42
46
|
return named_children(node)
|
|
43
47
|
.iter()
|
|
44
|
-
.any(|child| child.
|
|
48
|
+
.any(|child| child.kind_name() == "function_body");
|
|
45
49
|
}
|
|
46
50
|
|
|
47
51
|
// C++ constructor/destructor function-try-blocks carry their `try_statement` outside the
|
|
48
52
|
// `body` field; they are implementations, unlike `= 0`/`= default`/`= delete` members.
|
|
49
53
|
named_children(node)
|
|
50
54
|
.iter()
|
|
51
|
-
.any(|child| child.
|
|
55
|
+
.any(|child| child.kind_name() == "try_statement")
|
|
52
56
|
}
|
|
53
57
|
|
|
54
58
|
/// Counts declared parameters of a function/method, ignoring punctuation and comments.
|
|
@@ -64,29 +68,31 @@ pub fn count_parameters(node: Node<'_>, code: &Source<'_>) -> usize {
|
|
|
64
68
|
|
|
65
69
|
// A Java bare lambda parameter (`x -> x + 1`) puts a lone identifier in the `parameters` field;
|
|
66
70
|
// a C# one (`x => x + 1`) is an `implicit_parameter` leaf.
|
|
67
|
-
if parameters_node.
|
|
71
|
+
if parameters_node.kind_name() == "identifier"
|
|
72
|
+
|| parameters_node.kind_name() == "implicit_parameter"
|
|
73
|
+
{
|
|
68
74
|
return 1;
|
|
69
75
|
}
|
|
70
76
|
// Kotlin default values (`x: Int = 0`) are siblings of their `parameter`, not children.
|
|
71
|
-
if parameters_node.
|
|
77
|
+
if parameters_node.kind_name() == "function_value_parameters" {
|
|
72
78
|
return named_children(parameters_node)
|
|
73
79
|
.iter()
|
|
74
|
-
.filter(|child| child.
|
|
80
|
+
.filter(|child| child.kind_name() == "parameter")
|
|
75
81
|
.count();
|
|
76
82
|
}
|
|
77
83
|
// A Kotlin setter declares its single parameter directly (`set(value) { ... }`).
|
|
78
|
-
if parameters_node.
|
|
84
|
+
if parameters_node.kind_name() == "setter" {
|
|
79
85
|
return 1;
|
|
80
86
|
}
|
|
81
87
|
// A C# `params` array is spelled out as `type`/`name` fields of the parameter list itself.
|
|
82
|
-
let csharp_params_array_ids:
|
|
88
|
+
let csharp_params_array_ids: FxHashSet<usize> = ["type", "name"]
|
|
83
89
|
.iter()
|
|
84
90
|
.flat_map(|field| find_children_by_field_name(parameters_node, field))
|
|
85
91
|
.map(|child| child.id())
|
|
86
92
|
.collect();
|
|
87
93
|
|
|
88
94
|
// Ruby block-locals after `;` (`{ |x; memo| ... }`) occupy `locals` fields and receive no arguments.
|
|
89
|
-
let block_local_ids:
|
|
95
|
+
let block_local_ids: FxHashSet<usize> = find_children_by_field_name(parameters_node, "locals")
|
|
90
96
|
.iter()
|
|
91
97
|
.map(|child| child.id())
|
|
92
98
|
.collect();
|
|
@@ -95,22 +101,22 @@ pub fn count_parameters(node: Node<'_>, code: &Source<'_>) -> usize {
|
|
|
95
101
|
// C/C++ `f(void)` declares none, and a Ruby block parameter (`&blk`) binds the block, which
|
|
96
102
|
// call sites pass outside the argument list.
|
|
97
103
|
for child in named_children(parameters_node) {
|
|
98
|
-
if crate::ncss::COMMENT_NODE_TYPES.contains(&child.
|
|
99
|
-
|| child.
|
|
104
|
+
if crate::ncss::COMMENT_NODE_TYPES.contains(&child.kind_name())
|
|
105
|
+
|| child.kind_name() == "attribute_list"
|
|
100
106
|
|| csharp_params_array_ids.contains(&child.id())
|
|
101
|
-
|| child.
|
|
102
|
-
|| child.
|
|
103
|
-
|| child.
|
|
107
|
+
|| child.kind_name() == "self_parameter"
|
|
108
|
+
|| child.kind_name() == "receiver_parameter"
|
|
109
|
+
|| child.kind_name() == "block_parameter"
|
|
104
110
|
// Python's PEP 570/3102 markers (`/`, `*`) separate parameter kinds but bind nothing.
|
|
105
|
-
|| child.
|
|
106
|
-
|| child.
|
|
111
|
+
|| child.kind_name() == "positional_separator"
|
|
112
|
+
|| child.kind_name() == "keyword_separator"
|
|
107
113
|
|| block_local_ids.contains(&child.id())
|
|
108
114
|
|| is_void_parameter(child, code)
|
|
109
115
|
{
|
|
110
116
|
continue;
|
|
111
117
|
}
|
|
112
118
|
// Go declares several names per declaration (`a, b int`); each name is a parameter.
|
|
113
|
-
count += if child.
|
|
119
|
+
count += if child.kind_name() == "parameter_declaration" {
|
|
114
120
|
find_children_by_field_name(child, "name").len().max(1)
|
|
115
121
|
} else {
|
|
116
122
|
1
|
|
@@ -126,7 +132,7 @@ pub fn count_parameters(node: Node<'_>, code: &Source<'_>) -> usize {
|
|
|
126
132
|
|
|
127
133
|
/// C/C++ `int f(void)` has a `parameter_declaration` whose type is a bare `void` with no declarator.
|
|
128
134
|
fn is_void_parameter(node: Node<'_>, code: &Source<'_>) -> bool {
|
|
129
|
-
node.
|
|
135
|
+
node.kind_name() == "parameter_declaration"
|
|
130
136
|
&& node.child_by_field_name("declarator").is_none()
|
|
131
137
|
&& node
|
|
132
138
|
.child_by_field_name("type")
|
|
@@ -139,20 +145,20 @@ fn find_parameters_node(node: Node<'_>) -> Option<Node<'_>> {
|
|
|
139
145
|
}
|
|
140
146
|
|
|
141
147
|
// A C# indexer accessor (`this[int i] { get { ... } }`) takes the indexer's parameters.
|
|
142
|
-
if node.
|
|
148
|
+
if node.kind_name() == "accessor_declaration" {
|
|
143
149
|
return node
|
|
144
|
-
.
|
|
145
|
-
.and_then(|list| list.
|
|
146
|
-
.filter(|owner| owner.
|
|
150
|
+
.parent_node()
|
|
151
|
+
.and_then(|list| list.parent_node())
|
|
152
|
+
.filter(|owner| owner.kind_name() == "indexer_declaration")
|
|
147
153
|
.and_then(|owner| owner.child_by_field_name("parameters"));
|
|
148
154
|
}
|
|
149
155
|
|
|
150
156
|
// A Java compact constructor implicitly takes the record's components, declared on the
|
|
151
157
|
// `record_declaration` two levels up (via `class_body`).
|
|
152
|
-
if node.
|
|
158
|
+
if node.kind_name() == "compact_constructor_declaration" {
|
|
153
159
|
return node
|
|
154
|
-
.
|
|
155
|
-
.and_then(|parent| parent.
|
|
160
|
+
.parent_node()
|
|
161
|
+
.and_then(|parent| parent.parent_node())
|
|
156
162
|
.and_then(|grandparent| grandparent.child_by_field_name("parameters"));
|
|
157
163
|
}
|
|
158
164
|
|
|
@@ -166,17 +172,17 @@ fn find_parameters_node(node: Node<'_>) -> Option<Node<'_>> {
|
|
|
166
172
|
}
|
|
167
173
|
|
|
168
174
|
// A Kotlin setter's parameter (`set(value)`) sits directly under the setter node.
|
|
169
|
-
if node.
|
|
175
|
+
if node.kind_name() == "setter"
|
|
170
176
|
&& named_children(node)
|
|
171
177
|
.iter()
|
|
172
|
-
.any(|child| child.
|
|
178
|
+
.any(|child| child.kind_name() == "identifier")
|
|
173
179
|
{
|
|
174
180
|
return Some(node);
|
|
175
181
|
}
|
|
176
182
|
|
|
177
183
|
named_children(node).into_iter().find(|child| {
|
|
178
184
|
matches!(
|
|
179
|
-
child.
|
|
185
|
+
child.kind_name(),
|
|
180
186
|
"formal_parameters"
|
|
181
187
|
| "parameter_list"
|
|
182
188
|
| "function_value_parameters"
|
|
@@ -185,20 +191,20 @@ fn find_parameters_node(node: Node<'_>) -> Option<Node<'_>> {
|
|
|
185
191
|
})
|
|
186
192
|
}
|
|
187
193
|
|
|
188
|
-
pub fn collect_nodes<'t>(root: Node<'t>,
|
|
194
|
+
pub fn collect_nodes<'t>(root: Node<'t>, matches: impl Fn(&str) -> bool) -> Vec<Node<'t>> {
|
|
189
195
|
let mut nodes = Vec::new();
|
|
190
196
|
|
|
191
|
-
fn visit<'t>(node: Node<'t>,
|
|
192
|
-
if
|
|
197
|
+
fn visit<'t>(node: Node<'t>, matches: &impl Fn(&str) -> bool, nodes: &mut Vec<Node<'t>>) {
|
|
198
|
+
if matches(node.kind_name()) {
|
|
193
199
|
nodes.push(node);
|
|
194
200
|
}
|
|
195
201
|
|
|
196
202
|
for child in named_children(node) {
|
|
197
|
-
visit(child,
|
|
203
|
+
visit(child, matches, nodes);
|
|
198
204
|
}
|
|
199
205
|
}
|
|
200
206
|
|
|
201
|
-
visit(root,
|
|
207
|
+
visit(root, &matches, &mut nodes);
|
|
202
208
|
nodes
|
|
203
209
|
}
|
|
204
210
|
|
|
@@ -209,11 +215,11 @@ pub fn collect_nodes<'t>(root: Node<'t>, node_types: &HashSet<&'static str>) ->
|
|
|
209
215
|
/// `parenthesized_statements` wraps a lone value only when it holds exactly one statement.
|
|
210
216
|
fn wrapped_transparent_value(wrapper: Node<'_>) -> Option<Node<'_>> {
|
|
211
217
|
// A C-style cast (Java, C#, C/C++) names its type first, so its value comes from the field.
|
|
212
|
-
if wrapper.
|
|
218
|
+
if wrapper.kind_name() == "cast_expression" {
|
|
213
219
|
return wrapper.child_by_field_name("value");
|
|
214
220
|
}
|
|
215
221
|
if !matches!(
|
|
216
|
-
wrapper.
|
|
222
|
+
wrapper.kind_name(),
|
|
217
223
|
"type_assertion"
|
|
218
224
|
| "parenthesized_statements"
|
|
219
225
|
| "parenthesized_expression"
|
|
@@ -230,8 +236,8 @@ fn wrapped_transparent_value(wrapper: Node<'_>) -> Option<Node<'_>> {
|
|
|
230
236
|
let children = named_children(wrapper);
|
|
231
237
|
let mut values = children
|
|
232
238
|
.into_iter()
|
|
233
|
-
.filter(|child| !crate::ncss::COMMENT_NODE_TYPES.contains(&child.
|
|
234
|
-
match wrapper.
|
|
239
|
+
.filter(|child| !crate::ncss::COMMENT_NODE_TYPES.contains(&child.kind_name()));
|
|
240
|
+
match wrapper.kind_name() {
|
|
235
241
|
"type_assertion" => values.next_back(),
|
|
236
242
|
"parenthesized_statements" => {
|
|
237
243
|
let value = values.next()?;
|
|
@@ -246,7 +252,7 @@ fn wrapped_transparent_value(wrapper: Node<'_>) -> Option<Node<'_>> {
|
|
|
246
252
|
/// names the value.
|
|
247
253
|
fn unwrap_transparent_value_wrappers(node: Node<'_>) -> Node<'_> {
|
|
248
254
|
let mut bound = node;
|
|
249
|
-
while let Some(wrapper) = bound.
|
|
255
|
+
while let Some(wrapper) = bound.parent_node().filter(|wrapper| {
|
|
250
256
|
wrapped_transparent_value(*wrapper).is_some_and(|value| value.id() == bound.id())
|
|
251
257
|
}) {
|
|
252
258
|
bound = wrapper;
|
|
@@ -279,13 +285,13 @@ pub fn find_function_name(node: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
|
279
285
|
}
|
|
280
286
|
|
|
281
287
|
let bound = unwrap_transparent_value_wrappers(node);
|
|
282
|
-
let parent = bound.
|
|
288
|
+
let parent = bound.parent_node()?;
|
|
283
289
|
|
|
284
290
|
// A Rust closure bound to a simple `let` identifier takes that identifier as its name.
|
|
285
|
-
if node.
|
|
291
|
+
if node.kind_name() == "closure_expression" && parent.kind_name() == "let_declaration" {
|
|
286
292
|
let pattern_node = parent.child_by_field_name("pattern");
|
|
287
293
|
return match pattern_node {
|
|
288
|
-
Some(pattern) if pattern.
|
|
294
|
+
Some(pattern) if pattern.kind_name() == "identifier" => {
|
|
289
295
|
Some(node_text(pattern, code).to_string())
|
|
290
296
|
}
|
|
291
297
|
_ => None,
|
|
@@ -296,17 +302,17 @@ pub fn find_function_name(node: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
|
296
302
|
// as does one that direct-initializes a deduced variable (`auto f{[] {}}`), whose type is the
|
|
297
303
|
// closure itself. With a written type (`std::thread worker([] {})`) the lambda is a constructor
|
|
298
304
|
// argument, and the constructor stores whatever it likes, so it names nothing.
|
|
299
|
-
if node.
|
|
300
|
-
let declaration = match parent.
|
|
305
|
+
if node.kind_name() == "lambda_expression" {
|
|
306
|
+
let declaration = match parent.kind_name() {
|
|
301
307
|
"init_declarator" => Some(parent),
|
|
302
308
|
"argument_list" | "initializer_list" if binding_children(parent).len() == 1 => parent
|
|
303
|
-
.
|
|
304
|
-
.filter(|holder| holder.
|
|
309
|
+
.parent_node()
|
|
310
|
+
.filter(|holder| holder.kind_name() == "init_declarator")
|
|
305
311
|
.filter(|holder| declares_deduced_type(*holder))
|
|
306
312
|
// `auto f = {[] {}}` deduces a list holding the closure, not the closure itself;
|
|
307
313
|
// only the direct form `auto f{[] {}}` makes the variable the closure.
|
|
308
314
|
.filter(|holder| {
|
|
309
|
-
parent.
|
|
315
|
+
parent.kind_name() == "argument_list"
|
|
310
316
|
|| !all_children(*holder)
|
|
311
317
|
.iter()
|
|
312
318
|
.any(|child| !child.is_named() && node_text(*child, code) == "=")
|
|
@@ -320,41 +326,46 @@ pub fn find_function_name(node: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
|
320
326
|
|
|
321
327
|
// A Go func literal bound via `add := func...`, `var add = func...`, or `add = func...` takes
|
|
322
328
|
// the identifier (or selector field) at the same list position.
|
|
323
|
-
if node.
|
|
329
|
+
if node.kind_name() == "func_literal" && parent.kind_name() == "expression_list" {
|
|
324
330
|
return find_go_func_literal_name(bound, parent, code);
|
|
325
331
|
}
|
|
326
332
|
|
|
327
333
|
// Ruby and Python lambdas assigned to a name take that name.
|
|
328
|
-
if node.
|
|
334
|
+
if node.kind_name() == "lambda" && parent.kind_name() == "assignment" {
|
|
329
335
|
return find_ruby_assignment_name(parent, code);
|
|
330
336
|
}
|
|
331
|
-
if node.
|
|
337
|
+
if node.kind_name() == "lambda" && is_value_group(parent) {
|
|
332
338
|
return find_parallel_assignment_name(bound, code);
|
|
333
339
|
}
|
|
334
340
|
|
|
335
341
|
// A Kotlin lambda or anonymous function initializing a property (`val f = { ... }`, also through
|
|
336
342
|
// a label or annotation prefix) takes the property name; one assigned to a variable or member
|
|
337
343
|
// (`run = { ... }`, `obj.run = { ... }`) takes the assigned name.
|
|
338
|
-
if node.
|
|
344
|
+
if node.kind_name() == "lambda_literal" || node.kind_name() == "anonymous_function" {
|
|
339
345
|
let mut holder = parent;
|
|
340
|
-
while
|
|
341
|
-
holder
|
|
346
|
+
while matches!(
|
|
347
|
+
holder.kind_name(),
|
|
348
|
+
"labeled_expression" | "annotated_expression"
|
|
349
|
+
) {
|
|
350
|
+
holder = holder.parent_node()?;
|
|
342
351
|
}
|
|
343
|
-
if holder.
|
|
352
|
+
if holder.kind_name() == "property_declaration" {
|
|
344
353
|
return find_kotlin_property_name(holder, code);
|
|
345
354
|
}
|
|
346
|
-
if holder.
|
|
347
|
-
return
|
|
355
|
+
if holder.kind_name() == "assignment" {
|
|
356
|
+
return find_assignment_target_name(holder, code);
|
|
348
357
|
}
|
|
349
358
|
}
|
|
350
359
|
// A `lambda { }` / `proc { }` block is measured, but the call around it is what gets bound.
|
|
351
|
-
if (node.
|
|
360
|
+
if (node.kind_name() == "block" || node.kind_name() == "do_block")
|
|
361
|
+
&& is_ruby_lambda_call(parent, code)
|
|
362
|
+
{
|
|
352
363
|
let call = unwrap_transparent_value_wrappers(parent);
|
|
353
|
-
return match call.
|
|
354
|
-
Some(holder) if holder.
|
|
364
|
+
return match call.parent_node() {
|
|
365
|
+
Some(holder) if holder.kind_name() == "assignment" => {
|
|
355
366
|
find_ruby_assignment_name(holder, code)
|
|
356
367
|
}
|
|
357
|
-
Some(holder) if holder.
|
|
368
|
+
Some(holder) if holder.kind_name() == "pair" => find_pair_key_name(holder, code),
|
|
358
369
|
Some(holder) if is_value_group(holder) => find_parallel_assignment_name(call, code),
|
|
359
370
|
_ => None,
|
|
360
371
|
};
|
|
@@ -364,36 +375,36 @@ pub fn find_function_name(node: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
|
364
375
|
// after the key; an assignment
|
|
365
376
|
// (`obj.run = () => {}`, `run = () => {}`, Rust/C++ `self.cb = |x| x`, C++ `N::run = [] {}`,
|
|
366
377
|
// C# `this.Run = () => 1`) after its target.
|
|
367
|
-
if parent.
|
|
378
|
+
if parent.kind_name() == "pair" {
|
|
368
379
|
return find_pair_key_name(parent, code);
|
|
369
380
|
}
|
|
370
381
|
// A Go keyed composite-literal element (`S{run: func() {}}`) names its value after the key.
|
|
371
|
-
if parent.
|
|
382
|
+
if parent.kind_name() == "literal_element" {
|
|
372
383
|
return find_go_keyed_element_name(parent, code);
|
|
373
384
|
}
|
|
374
385
|
// A Rust struct-literal field (`S { cb: || 1 }`) names its closure after the field.
|
|
375
|
-
if parent.
|
|
386
|
+
if parent.kind_name() == "field_initializer" {
|
|
376
387
|
return parent
|
|
377
388
|
.child_by_field_name("field")
|
|
378
389
|
.map(|field| node_text(field, code).to_string());
|
|
379
390
|
}
|
|
380
391
|
// A C++20 designated initializer (`S s{.run = []{}}`) likewise names its value after the field;
|
|
381
392
|
// an array designator (`{[0] = ...}`) names nothing, like a subscript assignment target.
|
|
382
|
-
if parent.
|
|
393
|
+
if parent.kind_name() == "initializer_pair" {
|
|
383
394
|
return find_children_by_field_name(parent, "designator")
|
|
384
395
|
.last()
|
|
385
|
-
.filter(|designator| designator.
|
|
396
|
+
.filter(|designator| designator.kind_name() == "field_designator")
|
|
386
397
|
.and_then(|designator| designator.named_child(0))
|
|
387
398
|
.map(|field| node_text(field, code).to_string());
|
|
388
399
|
}
|
|
389
|
-
if parent.
|
|
400
|
+
if parent.kind_name() == "assignment_expression" {
|
|
390
401
|
return find_assignment_target_name(parent, code);
|
|
391
402
|
}
|
|
392
403
|
|
|
393
404
|
// A JavaScript class field (`handle = () => {}`) names its property through the `property`
|
|
394
405
|
// field; TypeScript's `public_field_definition` exposes the same thing as `name`. A computed
|
|
395
406
|
// key is as unstable here as in an object literal, and a string key is read the same way.
|
|
396
|
-
let field_name = if parent.
|
|
407
|
+
let field_name = if parent.kind_name() == "field_definition" {
|
|
397
408
|
"property"
|
|
398
409
|
} else {
|
|
399
410
|
"name"
|
|
@@ -405,7 +416,7 @@ pub fn find_function_name(node: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
|
405
416
|
if !is_value_of_parent(bound, parent) {
|
|
406
417
|
return None;
|
|
407
418
|
}
|
|
408
|
-
match name.
|
|
419
|
+
match name.kind_name() {
|
|
409
420
|
"computed_property_name" => None,
|
|
410
421
|
"string" => find_string_literal_content(name, code),
|
|
411
422
|
_ => Some(node_text(name, code).to_string()),
|
|
@@ -416,7 +427,7 @@ pub fn find_function_name(node: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
|
416
427
|
/// computed key (`[k]: ...`), an interpolated string or symbol, or an empty string names nothing.
|
|
417
428
|
fn find_pair_key_name(pair: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
418
429
|
let key = pair.child_by_field_name("key")?;
|
|
419
|
-
match key.
|
|
430
|
+
match key.kind_name() {
|
|
420
431
|
// A numeric key (`{ 1: () => {} }`) is as stable a property name as an identifier, signed
|
|
421
432
|
// (`{ -1: ... }`) or not; any other expression is computed and names nothing.
|
|
422
433
|
"property_identifier" | "hash_key_symbol" => Some(node_text(key, code).to_string()),
|
|
@@ -432,7 +443,7 @@ fn find_pair_key_name(pair: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
|
432
443
|
for part in binding_children(key) {
|
|
433
444
|
if named_children(part)
|
|
434
445
|
.iter()
|
|
435
|
-
.any(|child| child.
|
|
446
|
+
.any(|child| child.kind_name() == "interpolation")
|
|
436
447
|
{
|
|
437
448
|
return None;
|
|
438
449
|
}
|
|
@@ -450,7 +461,7 @@ fn find_pair_key_name(pair: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
|
450
461
|
fn core_constraint_type(constraint: Node<'_>) -> Node<'_> {
|
|
451
462
|
let mut current = constraint;
|
|
452
463
|
while matches!(
|
|
453
|
-
current.
|
|
464
|
+
current.kind_name(),
|
|
454
465
|
"type_constraint" | "negated_type" | "type_elem" | "interface_type"
|
|
455
466
|
) {
|
|
456
467
|
match named_children(current).as_slice() {
|
|
@@ -474,7 +485,7 @@ fn signed_number_name(key: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
|
474
485
|
.find(|child| !child.is_named())
|
|
475
486
|
.map(|child| node_text(child, code))
|
|
476
487
|
.unwrap_or_default();
|
|
477
|
-
let text = match operand.
|
|
488
|
+
let text = match operand.kind_name() {
|
|
478
489
|
"rune_literal" => find_string_literal_content(operand, code)?,
|
|
479
490
|
_ => node_text(operand, code).to_string(),
|
|
480
491
|
};
|
|
@@ -490,7 +501,7 @@ fn is_signed_number(key: Node<'_>, code: &Source<'_>) -> bool {
|
|
|
490
501
|
signed
|
|
491
502
|
&& named_children(key).first().is_some_and(|operand| {
|
|
492
503
|
matches!(
|
|
493
|
-
operand.
|
|
504
|
+
operand.kind_name(),
|
|
494
505
|
"number"
|
|
495
506
|
| "integer"
|
|
496
507
|
| "float"
|
|
@@ -506,14 +517,14 @@ fn is_signed_number(key: Node<'_>, code: &Source<'_>) -> bool {
|
|
|
506
517
|
/// value the last. An unkeyed element sits under a `literal_value` instead and names nothing.
|
|
507
518
|
fn find_go_keyed_element_name(value_element: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
508
519
|
let keyed = value_element
|
|
509
|
-
.
|
|
510
|
-
.filter(|parent| parent.
|
|
520
|
+
.parent_node()
|
|
521
|
+
.filter(|parent| parent.kind_name() == "keyed_element")?;
|
|
511
522
|
let elements = named_children(keyed);
|
|
512
523
|
if elements.len() < 2 || elements.last()?.id() != value_element.id() {
|
|
513
524
|
return None;
|
|
514
525
|
}
|
|
515
526
|
let key = named_children(*elements.first()?).into_iter().next()?;
|
|
516
|
-
match key.
|
|
527
|
+
match key.kind_name() {
|
|
517
528
|
"identifier" | "field_identifier" if !has_value_keys(keyed, code) => {
|
|
518
529
|
Some(node_text(key, code).to_string())
|
|
519
530
|
}
|
|
@@ -536,7 +547,7 @@ fn find_go_keyed_element_name(value_element: Node<'_>, code: &Source<'_>) -> Opt
|
|
|
536
547
|
/// `key` holds, so it names nothing, exactly like a computed property key.
|
|
537
548
|
fn has_value_keys(keyed: Node<'_>, code: &Source<'_>) -> bool {
|
|
538
549
|
keyed
|
|
539
|
-
.
|
|
550
|
+
.parent_node()
|
|
540
551
|
.and_then(|body| key_type_of_literal_body(body, code))
|
|
541
552
|
.is_some_and(|declared| is_value_keyed_type(declared, code))
|
|
542
553
|
}
|
|
@@ -557,7 +568,7 @@ fn value_keyed_type(declared: Node<'_>, code: &Source<'_>, depth: usize) -> bool
|
|
|
557
568
|
return false;
|
|
558
569
|
}
|
|
559
570
|
let resolved = resolve_named_type(declared, code);
|
|
560
|
-
match resolved.
|
|
571
|
+
match resolved.kind_name() {
|
|
561
572
|
"map_type" | "slice_type" | "array_type" | "implicit_length_array_type" => true,
|
|
562
573
|
"type_constraint" | "interface_type" | "type_elem" | "negated_type" => {
|
|
563
574
|
// Method requirements restrict what a type does, not what it is, so only the type terms
|
|
@@ -575,14 +586,14 @@ fn value_keyed_type(declared: Node<'_>, code: &Source<'_>, depth: usize) -> bool
|
|
|
575
586
|
/// Whether a constraint element only requires methods, directly or through an embedded interface
|
|
576
587
|
/// that does; such an element leaves the underlying type free.
|
|
577
588
|
fn requires_methods_only(declared: Node<'_>, code: &Source<'_>, depth: usize) -> bool {
|
|
578
|
-
if matches!(declared.
|
|
589
|
+
if matches!(declared.kind_name(), "method_elem" | "method_spec") {
|
|
579
590
|
return true;
|
|
580
591
|
}
|
|
581
592
|
if depth >= MAX_CONSTRAINT_DEPTH {
|
|
582
593
|
return false;
|
|
583
594
|
}
|
|
584
595
|
let resolved = resolve_named_type(declared, code);
|
|
585
|
-
match resolved.
|
|
596
|
+
match resolved.kind_name() {
|
|
586
597
|
"interface_type" | "type_constraint" | "type_elem" => {
|
|
587
598
|
let terms = binding_children(resolved);
|
|
588
599
|
!terms.is_empty()
|
|
@@ -605,7 +616,7 @@ fn resolve_named_type<'t>(declared: Node<'t>, code: &Source<'t>) -> Node<'t> {
|
|
|
605
616
|
let mut visited = Vec::new();
|
|
606
617
|
while !visited.contains(¤t.id()) {
|
|
607
618
|
visited.push(current.id());
|
|
608
|
-
let next = match current.
|
|
619
|
+
let next = match current.kind_name() {
|
|
609
620
|
"generic_type" => current.child_by_field_name("type"),
|
|
610
621
|
// Go allows parentheses around a type; they name the type they hold.
|
|
611
622
|
// Parentheses name the type they hold, and a nested literal of a pointer element type
|
|
@@ -623,7 +634,7 @@ fn resolve_named_type<'t>(declared: Node<'t>, code: &Source<'t>) -> Node<'t> {
|
|
|
623
634
|
|
|
624
635
|
/// The type a name stands for, resolved through the scopes enclosing it, innermost first.
|
|
625
636
|
fn lookup_declared_type<'t>(declared: Node<'t>, code: &Source<'t>) -> Option<Node<'t>> {
|
|
626
|
-
if declared.
|
|
637
|
+
if declared.kind_name() != "type_identifier" {
|
|
627
638
|
return None;
|
|
628
639
|
}
|
|
629
640
|
let name = node_text(declared, code);
|
|
@@ -636,7 +647,7 @@ fn lookup_declared_type<'t>(declared: Node<'t>, code: &Source<'t>) -> Option<Nod
|
|
|
636
647
|
// what the literal is written against.
|
|
637
648
|
if let Some(constraint) = named_children(current)
|
|
638
649
|
.into_iter()
|
|
639
|
-
.filter(|child| child.
|
|
650
|
+
.filter(|child| child.kind_name() == "type_parameter_list")
|
|
640
651
|
.flat_map(|list| declared_type_parameters(list))
|
|
641
652
|
.find(|(parameter_name, _)| node_text(*parameter_name, code) == name)
|
|
642
653
|
.and_then(|(_, constraint)| constraint)
|
|
@@ -645,7 +656,7 @@ fn lookup_declared_type<'t>(declared: Node<'t>, code: &Source<'t>) -> Option<Nod
|
|
|
645
656
|
}
|
|
646
657
|
// A method's receiver carries the parameters of the type it is declared on
|
|
647
658
|
// (`func (r R[T]) ...`), so the name resolves through that type's declaration.
|
|
648
|
-
if current.
|
|
659
|
+
if current.kind_name() == "method_declaration" {
|
|
649
660
|
if let Some(constraint) = receiver_parameter_constraint(current, name, code) {
|
|
650
661
|
return Some(core_constraint_type(constraint));
|
|
651
662
|
}
|
|
@@ -653,7 +664,7 @@ fn lookup_declared_type<'t>(declared: Node<'t>, code: &Source<'t>) -> Option<Nod
|
|
|
653
664
|
if let Some(found) = find_type_spec(current, name, code) {
|
|
654
665
|
return found.child_by_field_name("type");
|
|
655
666
|
}
|
|
656
|
-
scope = current.
|
|
667
|
+
scope = current.parent_node();
|
|
657
668
|
}
|
|
658
669
|
None
|
|
659
670
|
}
|
|
@@ -662,9 +673,9 @@ fn lookup_declared_type<'t>(declared: Node<'t>, code: &Source<'t>) -> Option<Nod
|
|
|
662
673
|
fn find_type_spec<'t>(scope: Node<'t>, name: &str, code: &Source<'t>) -> Option<Node<'t>> {
|
|
663
674
|
named_children(scope)
|
|
664
675
|
.into_iter()
|
|
665
|
-
.filter(|child| child.
|
|
676
|
+
.filter(|child| child.kind_name() == "type_declaration")
|
|
666
677
|
.flat_map(named_children)
|
|
667
|
-
.filter(|spec| spec.
|
|
678
|
+
.filter(|spec| spec.kind_name() == "type_spec" || spec.kind_name() == "type_alias")
|
|
668
679
|
.find(|spec| {
|
|
669
680
|
spec.child_by_field_name("name")
|
|
670
681
|
.is_some_and(|declared_name| node_text(declared_name, code) == name)
|
|
@@ -685,7 +696,7 @@ fn receiver_parameter_constraint<'t>(
|
|
|
685
696
|
// A pointer or parenthesized receiver (`func (r *R[T])`, `func (r (R[T]))`) wraps the
|
|
686
697
|
// instantiation, in either order.
|
|
687
698
|
.map(unwrap_receiver_type)
|
|
688
|
-
.find(|declared| declared.
|
|
699
|
+
.find(|declared| declared.kind_name() == "generic_type")?;
|
|
689
700
|
let arguments = binding_children(instantiation.child_by_field_name("type_arguments")?);
|
|
690
701
|
let position = arguments.iter().position(|argument| {
|
|
691
702
|
let argument = named_children(*argument)
|
|
@@ -704,7 +715,7 @@ fn receiver_parameter_constraint<'t>(
|
|
|
704
715
|
/// The type a receiver names, past the pointer and parenthesis wrappers it may carry.
|
|
705
716
|
fn unwrap_receiver_type(declared: Node<'_>) -> Node<'_> {
|
|
706
717
|
let mut current = declared;
|
|
707
|
-
while matches!(current.
|
|
718
|
+
while matches!(current.kind_name(), "pointer_type" | "parenthesized_type") {
|
|
708
719
|
match named_children(current).into_iter().next() {
|
|
709
720
|
Some(inner) => current = inner,
|
|
710
721
|
None => break,
|
|
@@ -718,7 +729,7 @@ fn unwrap_receiver_type(declared: Node<'_>) -> Node<'_> {
|
|
|
718
729
|
fn declared_type_parameters<'t>(list: Node<'t>) -> Vec<(Node<'t>, Option<Node<'t>>)> {
|
|
719
730
|
binding_children(list)
|
|
720
731
|
.into_iter()
|
|
721
|
-
.filter(|declaration| declaration.
|
|
732
|
+
.filter(|declaration| declaration.kind_name() == "type_parameter_declaration")
|
|
722
733
|
.flat_map(|declaration| {
|
|
723
734
|
let constraint = declaration.child_by_field_name("type");
|
|
724
735
|
find_children_by_field_name(declaration, "name")
|
|
@@ -734,24 +745,24 @@ fn declared_type_parameters<'t>(list: Node<'t>) -> Vec<(Node<'t>, Option<Node<'t
|
|
|
734
745
|
/// {k: f}}` nests a map, `[]S{{run: f}}` a struct). A literal nested in a struct field keeps the
|
|
735
746
|
/// struct reading, since the field's type is not written at the literal.
|
|
736
747
|
fn key_type_of_literal_body<'t>(body: Node<'t>, code: &Source<'t>) -> Option<Node<'t>> {
|
|
737
|
-
let parent = body.
|
|
738
|
-
if parent.
|
|
748
|
+
let parent = body.parent_node()?;
|
|
749
|
+
if parent.kind_name() == "composite_literal" {
|
|
739
750
|
return parent.child_by_field_name("type");
|
|
740
751
|
}
|
|
741
|
-
if parent.
|
|
752
|
+
if parent.kind_name() != "literal_element" {
|
|
742
753
|
return None;
|
|
743
754
|
}
|
|
744
|
-
let mut container = parent.
|
|
745
|
-
if container.
|
|
746
|
-
container = container.
|
|
755
|
+
let mut container = parent.parent_node()?;
|
|
756
|
+
if container.kind_name() == "keyed_element" {
|
|
757
|
+
container = container.parent_node()?;
|
|
747
758
|
}
|
|
748
|
-
if container.
|
|
759
|
+
if container.kind_name() != "literal_value" {
|
|
749
760
|
return None;
|
|
750
761
|
}
|
|
751
762
|
// The container's own type may be a name, which the element type is read through.
|
|
752
763
|
let declared = key_type_of_literal_body(container, code)?;
|
|
753
764
|
let holder = resolve_named_type(declared, code);
|
|
754
|
-
let element = match holder.
|
|
765
|
+
let element = match holder.kind_name() {
|
|
755
766
|
"map_type" => holder.child_by_field_name("value"),
|
|
756
767
|
"slice_type" | "array_type" | "implicit_length_array_type" => {
|
|
757
768
|
holder.child_by_field_name("element")
|
|
@@ -768,7 +779,7 @@ fn instantiated_type_argument<'t>(
|
|
|
768
779
|
declared: Node<'t>,
|
|
769
780
|
code: &Source<'t>,
|
|
770
781
|
) -> Option<Node<'t>> {
|
|
771
|
-
if element.
|
|
782
|
+
if element.kind_name() != "type_identifier" || declared.kind_name() != "generic_type" {
|
|
772
783
|
return None;
|
|
773
784
|
}
|
|
774
785
|
let base = declared.child_by_field_name("type")?;
|
|
@@ -793,7 +804,7 @@ fn find_declared_type_spec<'t>(from: Node<'t>, name: &str, code: &Source<'t>) ->
|
|
|
793
804
|
if let Some(spec) = find_type_spec(current, name, code) {
|
|
794
805
|
return Some(spec);
|
|
795
806
|
}
|
|
796
|
-
scope = current.
|
|
807
|
+
scope = current.parent_node();
|
|
797
808
|
}
|
|
798
809
|
None
|
|
799
810
|
}
|
|
@@ -804,7 +815,10 @@ fn find_declared_type_spec<'t>(from: Node<'t>, name: &str, code: &Source<'t>) ->
|
|
|
804
815
|
/// `string_content` (Python nests escapes inside it). Interpolation makes the key unstable.
|
|
805
816
|
fn find_string_literal_content(literal: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
806
817
|
let children = named_children(literal);
|
|
807
|
-
if children
|
|
818
|
+
if children
|
|
819
|
+
.iter()
|
|
820
|
+
.any(|child| child.kind_name() == "interpolation")
|
|
821
|
+
{
|
|
808
822
|
return None;
|
|
809
823
|
}
|
|
810
824
|
// Go exposes no content node at all: it names only the escapes, so the concatenation is
|
|
@@ -812,7 +826,7 @@ fn find_string_literal_content(literal: Node<'_>, code: &Source<'_>) -> Option<S
|
|
|
812
826
|
let mut content = String::new();
|
|
813
827
|
let mut has_content_node = false;
|
|
814
828
|
for child in &children {
|
|
815
|
-
match child.
|
|
829
|
+
match child.kind_name() {
|
|
816
830
|
"string_content" | "string_fragment" => {
|
|
817
831
|
has_content_node = true;
|
|
818
832
|
content.push_str(node_text(*child, code));
|
|
@@ -841,8 +855,8 @@ fn find_string_literal_content(literal: Node<'_>, code: &Source<'_>) -> Option<S
|
|
|
841
855
|
}
|
|
842
856
|
|
|
843
857
|
/// A compound assignment (`x += f`) does not bind the function to its target, so only a plain `=`
|
|
844
|
-
/// names it. Grammars with an `operator` field (C/C++, C#, Java) expose it directly; Go
|
|
845
|
-
///
|
|
858
|
+
/// names it. Grammars with an `operator` field (C/C++, C#, Java, Kotlin) expose it directly; Go has
|
|
859
|
+
/// none, so the operator is the assignment's own anonymous token child.
|
|
846
860
|
fn is_plain_assignment(assignment: Node<'_>, code: &Source<'_>) -> bool {
|
|
847
861
|
match assignment.child_by_field_name("operator") {
|
|
848
862
|
Some(operator) => node_text(operator, code) == "=",
|
|
@@ -852,81 +866,63 @@ fn is_plain_assignment(assignment: Node<'_>, code: &Source<'_>) -> bool {
|
|
|
852
866
|
}
|
|
853
867
|
}
|
|
854
868
|
|
|
855
|
-
/// The assigned identifier, or the member name of a JS member, Rust/C++ field, C# member,
|
|
856
|
-
/// field access (`a.b.run` names `run`) or a C++ qualified name (`N::run`);
|
|
857
|
-
/// name nothing.
|
|
869
|
+
/// The assigned identifier, or the member name of a JS member, Rust/C++ field, C# member, Java
|
|
870
|
+
/// field access, or Kotlin navigation (`a.b.run` names `run`) or a C++ qualified name (`N::run`);
|
|
871
|
+
/// subscripts (`o["run"]`) name nothing.
|
|
858
872
|
fn find_assignment_target_name(assignment: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
859
873
|
if !is_plain_assignment(assignment, code) {
|
|
860
874
|
return None;
|
|
861
875
|
}
|
|
862
876
|
let target = assignment.child_by_field_name("left")?;
|
|
863
|
-
let name = match target.
|
|
877
|
+
let name = match target.kind_name() {
|
|
864
878
|
"identifier" => target,
|
|
865
879
|
"member_expression" => target.child_by_field_name("property")?,
|
|
866
880
|
"field_expression" => target.child_by_field_name("field")?,
|
|
867
881
|
"member_access_expression" => target.child_by_field_name("name")?,
|
|
868
882
|
"field_access" => target.child_by_field_name("field")?,
|
|
883
|
+
"navigation_expression" => target.named_child(target.named_child_count() - 1)?,
|
|
869
884
|
"qualified_identifier" => return unwrap_declarator_name(Some(target), code),
|
|
870
885
|
_ => return None,
|
|
871
886
|
};
|
|
872
887
|
Some(node_text(name, code).to_string())
|
|
873
888
|
}
|
|
874
889
|
|
|
875
|
-
/// The Kotlin assignment target: the variable itself or the member of a trailing navigation suffix
|
|
876
|
-
/// (`obj.run` names `run`); a trailing indexing suffix (`arr[0] = { }`) names nothing, like
|
|
877
|
-
/// subscripts in the other languages.
|
|
878
|
-
fn find_kotlin_assignment_name(assignment: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
879
|
-
if !is_plain_assignment(assignment, code) {
|
|
880
|
-
return None;
|
|
881
|
-
}
|
|
882
|
-
let target = first_named_child_of_kind(assignment, "directly_assignable_expression")?;
|
|
883
|
-
let children = named_children(target);
|
|
884
|
-
let holder = match children.last()? {
|
|
885
|
-
last if last.kind() == "navigation_suffix" => *last,
|
|
886
|
-
last if last.kind() == "simple_identifier" && children.len() == 1 => target,
|
|
887
|
-
_ => return None,
|
|
888
|
-
};
|
|
889
|
-
first_named_child_of_kind(holder, "simple_identifier")
|
|
890
|
-
.map(|name| node_text(name, code).to_string())
|
|
891
|
-
}
|
|
892
|
-
|
|
893
890
|
/// Names of C# and Kotlin members whose grammars carry no usable `name` field: accessors are
|
|
894
891
|
/// named after their property (`Count.get`; an expression-bodied property is its own getter),
|
|
895
|
-
/// Kotlin
|
|
896
|
-
///
|
|
892
|
+
/// Kotlin secondary constructors and C# destructors after their class, and C# operators like C++
|
|
893
|
+
/// ones.
|
|
897
894
|
fn find_member_function_name(node: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
898
|
-
match node.
|
|
895
|
+
match node.kind_name() {
|
|
899
896
|
"accessor_declaration" => {
|
|
900
897
|
let keyword = node_text(node.child_by_field_name("name")?, code);
|
|
901
|
-
let owner = node.
|
|
898
|
+
let owner = node.parent_node()?.parent_node()?;
|
|
902
899
|
Some(format!("{}.{keyword}", csharp_property_name(owner, code)?))
|
|
903
900
|
}
|
|
904
901
|
"property_declaration" | "indexer_declaration" => {
|
|
905
902
|
Some(format!("{}.get", csharp_property_name(node, code)?))
|
|
906
903
|
}
|
|
907
904
|
"getter" | "setter" => {
|
|
908
|
-
let keyword = if node.
|
|
905
|
+
let keyword = if node.kind_name() == "getter" {
|
|
909
906
|
"get"
|
|
910
907
|
} else {
|
|
911
908
|
"set"
|
|
912
909
|
};
|
|
913
910
|
Some(format!(
|
|
914
911
|
"{}.{keyword}",
|
|
915
|
-
|
|
912
|
+
find_kotlin_property_name(node.parent_node()?, code)?
|
|
916
913
|
))
|
|
917
914
|
}
|
|
918
|
-
"function_declaration" if node.child_by_field_name("name").is_none() => {
|
|
919
|
-
first_named_child_of_kind(node, "simple_identifier")
|
|
920
|
-
.map(|name| node_text(name, code).to_string())
|
|
921
|
-
}
|
|
922
915
|
"secondary_constructor" => {
|
|
923
|
-
let mut ancestor = node.
|
|
916
|
+
let mut ancestor = node.parent_node();
|
|
924
917
|
while let Some(current) = ancestor {
|
|
925
|
-
if current.
|
|
926
|
-
|
|
918
|
+
if current.kind_name() == "class_declaration"
|
|
919
|
+
|| current.kind_name() == "object_declaration"
|
|
920
|
+
{
|
|
921
|
+
return current
|
|
922
|
+
.child_by_field_name("name")
|
|
927
923
|
.map(|name| node_text(name, code).to_string());
|
|
928
924
|
}
|
|
929
|
-
ancestor = current.
|
|
925
|
+
ancestor = current.parent_node();
|
|
930
926
|
}
|
|
931
927
|
None
|
|
932
928
|
}
|
|
@@ -948,51 +944,26 @@ fn find_member_function_name(node: Node<'_>, code: &Source<'_>) -> Option<String
|
|
|
948
944
|
|
|
949
945
|
/// A C# property or indexer (`this`) name.
|
|
950
946
|
fn csharp_property_name(owner: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
951
|
-
match owner.
|
|
947
|
+
match owner.kind_name() {
|
|
952
948
|
"indexer_declaration" => Some("this".to_string()),
|
|
953
949
|
_ => Some(node_text(owner.child_by_field_name("name")?, code).to_string()),
|
|
954
950
|
}
|
|
955
951
|
}
|
|
956
952
|
|
|
957
|
-
/// The property a Kotlin accessor belongs to: its parent when the accessor follows the initializer
|
|
958
|
-
/// on the same line, otherwise (accessor on its own line) the grammar emits it as a class-body
|
|
959
|
-
/// sibling after the property, any preceding accessor, and any comments between them.
|
|
960
|
-
fn find_kotlin_accessor_owner_name(accessor: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
961
|
-
if let Some(name) = accessor
|
|
962
|
-
.parent()
|
|
963
|
-
.and_then(|parent| find_kotlin_property_name(parent, code))
|
|
964
|
-
{
|
|
965
|
-
return Some(name);
|
|
966
|
-
}
|
|
967
|
-
let mut sibling = accessor.prev_named_sibling();
|
|
968
|
-
while let Some(current) = sibling {
|
|
969
|
-
if current.kind() == "property_declaration" {
|
|
970
|
-
return find_kotlin_property_name(current, code);
|
|
971
|
-
}
|
|
972
|
-
if !matches!(current.kind(), "getter" | "setter")
|
|
973
|
-
&& !crate::ncss::COMMENT_NODE_TYPES.contains(¤t.kind())
|
|
974
|
-
{
|
|
975
|
-
return None;
|
|
976
|
-
}
|
|
977
|
-
sibling = current.prev_named_sibling();
|
|
978
|
-
}
|
|
979
|
-
None
|
|
980
|
-
}
|
|
981
|
-
|
|
982
953
|
/// The declared name of a Kotlin `property_declaration` (`val name: T`), if it declares one.
|
|
983
954
|
fn find_kotlin_property_name(property: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
984
|
-
if property.
|
|
955
|
+
if property.kind_name() != "property_declaration" {
|
|
985
956
|
return None;
|
|
986
957
|
}
|
|
987
958
|
let declaration = first_named_child_of_kind(property, "variable_declaration")?;
|
|
988
|
-
first_named_child_of_kind(declaration, "
|
|
959
|
+
first_named_child_of_kind(declaration, "identifier")
|
|
989
960
|
.map(|name| node_text(name, code).to_string())
|
|
990
961
|
}
|
|
991
962
|
|
|
992
963
|
fn first_named_child_of_kind<'t>(node: Node<'t>, kind: &str) -> Option<Node<'t>> {
|
|
993
964
|
named_children(node)
|
|
994
965
|
.into_iter()
|
|
995
|
-
.find(|child| child.
|
|
966
|
+
.find(|child| child.kind_name() == kind)
|
|
996
967
|
}
|
|
997
968
|
|
|
998
969
|
/// Whether the node occupies its parent's value position: the `value` field, or no field at all
|
|
@@ -1017,7 +988,7 @@ fn is_value_of_parent(node: Node<'_>, parent: Node<'_>) -> bool {
|
|
|
1017
988
|
/// or a mapping is unordered, so it groups nothing positionally.
|
|
1018
989
|
fn is_value_group(node: Node<'_>) -> bool {
|
|
1019
990
|
matches!(
|
|
1020
|
-
node.
|
|
991
|
+
node.kind_name(),
|
|
1021
992
|
"expression_list" | "right_assignment_list" | "tuple" | "list" | "array"
|
|
1022
993
|
)
|
|
1023
994
|
}
|
|
@@ -1025,7 +996,7 @@ fn is_value_group(node: Node<'_>) -> bool {
|
|
|
1025
996
|
/// The matching target groups, which a nested value group is aligned against level by level.
|
|
1026
997
|
fn is_target_group(node: Node<'_>) -> bool {
|
|
1027
998
|
matches!(
|
|
1028
|
-
node.
|
|
999
|
+
node.kind_name(),
|
|
1029
1000
|
"pattern_list"
|
|
1030
1001
|
| "left_assignment_list"
|
|
1031
1002
|
| "tuple_pattern"
|
|
@@ -1041,13 +1012,13 @@ fn find_parallel_assignment_name(value: Node<'_>, code: &Source<'_>) -> Option<S
|
|
|
1041
1012
|
let mut positions = Vec::new();
|
|
1042
1013
|
let mut current = value;
|
|
1043
1014
|
let assignment = loop {
|
|
1044
|
-
let parent = current.
|
|
1015
|
+
let parent = current.parent_node()?;
|
|
1045
1016
|
if is_value_group(parent) {
|
|
1046
1017
|
positions.push((parent, current));
|
|
1047
1018
|
current = parent;
|
|
1048
1019
|
continue;
|
|
1049
1020
|
}
|
|
1050
|
-
if parent.
|
|
1021
|
+
if parent.kind_name() == "assignment"
|
|
1051
1022
|
&& parent.child_by_field_name("right")?.id() == current.id()
|
|
1052
1023
|
{
|
|
1053
1024
|
break parent;
|
|
@@ -1059,9 +1030,9 @@ fn find_parallel_assignment_name(value: Node<'_>, code: &Source<'_>) -> Option<S
|
|
|
1059
1030
|
// Ruby spells a fully parenthesized target list as a `left_assignment_list` holding one
|
|
1060
1031
|
// destructured group (`(a, b) = f, g`), which aligns against that inner one. A Python
|
|
1061
1032
|
// singleton tuple is a real destructuring level instead, so it is left alone.
|
|
1062
|
-
while target.
|
|
1033
|
+
while target.kind_name() == "left_assignment_list" {
|
|
1063
1034
|
match binding_children(target).as_slice() {
|
|
1064
|
-
[only] if only.
|
|
1035
|
+
[only] if only.kind_name() == "destructured_left_assignment" => target = *only,
|
|
1065
1036
|
_ => break,
|
|
1066
1037
|
}
|
|
1067
1038
|
}
|
|
@@ -1086,7 +1057,7 @@ fn aligned_target<'t>(values: Node<'_>, value: Node<'_>, targets: Node<'t>) -> O
|
|
|
1086
1057
|
let splat_after = value_list[index + 1..].iter().any(is_splat);
|
|
1087
1058
|
let trailing = value_list.len() - 1 - index;
|
|
1088
1059
|
let unpacks = matches!(
|
|
1089
|
-
targets.
|
|
1060
|
+
targets.kind_name(),
|
|
1090
1061
|
"pattern_list" | "tuple_pattern" | "list_pattern"
|
|
1091
1062
|
);
|
|
1092
1063
|
let targets = binding_children(targets);
|
|
@@ -1123,10 +1094,12 @@ fn aligned_target<'t>(values: Node<'_>, value: Node<'_>, targets: Node<'t>) -> O
|
|
|
1123
1094
|
// it, by failing the assignment, while Ruby fills the trailing targets from the left
|
|
1124
1095
|
// when it underflows.
|
|
1125
1096
|
let reaches_trailing_targets = written_values + 1 >= targets.len()
|
|
1126
|
-
|| value_list
|
|
1097
|
+
|| value_list
|
|
1098
|
+
.iter()
|
|
1099
|
+
.any(|child| child.kind_name() == "list_splat");
|
|
1127
1100
|
if reaches_trailing_targets {
|
|
1128
1101
|
targets.get(targets.len() - 1 - trailing).copied()
|
|
1129
|
-
} else if !splat_before && targets[splat].
|
|
1102
|
+
} else if !splat_before && targets[splat].kind_name() == "rest_assignment" {
|
|
1130
1103
|
// Ruby empties the star and fills the trailing targets from the left when the
|
|
1131
1104
|
// values run out, so each one binds the next target (`a, *r, c, d = x, f` binds
|
|
1132
1105
|
// `f` to `c`); Python fails such an assignment instead.
|
|
@@ -1142,7 +1115,7 @@ fn aligned_target<'t>(values: Node<'_>, value: Node<'_>, targets: Node<'t>) -> O
|
|
|
1142
1115
|
/// A splat on either side of a parallel assignment (`*xs`, `*rest`).
|
|
1143
1116
|
fn is_splat(node: &Node<'_>) -> bool {
|
|
1144
1117
|
matches!(
|
|
1145
|
-
node.
|
|
1118
|
+
node.kind_name(),
|
|
1146
1119
|
"splat_argument" | "rest_assignment" | "list_splat" | "list_splat_pattern"
|
|
1147
1120
|
)
|
|
1148
1121
|
}
|
|
@@ -1150,7 +1123,7 @@ fn is_splat(node: &Node<'_>) -> bool {
|
|
|
1150
1123
|
fn binding_children<'t>(node: Node<'t>) -> Vec<Node<'t>> {
|
|
1151
1124
|
named_children(node)
|
|
1152
1125
|
.into_iter()
|
|
1153
|
-
.filter(|child| !crate::ncss::COMMENT_NODE_TYPES.contains(&child.
|
|
1126
|
+
.filter(|child| !crate::ncss::COMMENT_NODE_TYPES.contains(&child.kind_name()))
|
|
1154
1127
|
.collect()
|
|
1155
1128
|
}
|
|
1156
1129
|
|
|
@@ -1158,7 +1131,7 @@ fn binding_children<'t>(node: Node<'t>) -> Vec<Node<'t>> {
|
|
|
1158
1131
|
/// global variable; the method of a Ruby attribute writer (`self.run = ...`); or a Python attribute
|
|
1159
1132
|
/// (`obj.run = ...`). A subscript or a splat has no stable name and binds none.
|
|
1160
1133
|
fn find_assignment_target_text(target: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
1161
|
-
match target.
|
|
1134
|
+
match target.kind_name() {
|
|
1162
1135
|
"identifier" | "constant" | "instance_variable" | "class_variable" | "global_variable" => {
|
|
1163
1136
|
Some(node_text(target, code).to_string())
|
|
1164
1137
|
}
|
|
@@ -1179,7 +1152,7 @@ fn find_ruby_assignment_name(assignment: Node<'_>, code: &Source<'_>) -> Option<
|
|
|
1179
1152
|
let left = assignment.child_by_field_name("left")?;
|
|
1180
1153
|
let mut target = left;
|
|
1181
1154
|
if matches!(
|
|
1182
|
-
left.
|
|
1155
|
+
left.kind_name(),
|
|
1183
1156
|
"left_assignment_list" | "destructured_left_assignment"
|
|
1184
1157
|
) {
|
|
1185
1158
|
while is_target_group(target) {
|
|
@@ -1192,11 +1165,11 @@ fn find_ruby_assignment_name(assignment: Node<'_>, code: &Source<'_>) -> Option<
|
|
|
1192
1165
|
}
|
|
1193
1166
|
|
|
1194
1167
|
fn is_ruby_lambda_call(node: Node<'_>, code: &Source<'_>) -> bool {
|
|
1195
|
-
if node.
|
|
1168
|
+
if node.kind_name() != "call" || node.child_by_field_name("receiver").is_some() {
|
|
1196
1169
|
return false;
|
|
1197
1170
|
}
|
|
1198
1171
|
node.child_by_field_name("method").is_some_and(|method| {
|
|
1199
|
-
method.
|
|
1172
|
+
method.kind_name() == "identifier"
|
|
1200
1173
|
&& (node_text(method, code) == "lambda" || node_text(method, code) == "proc")
|
|
1201
1174
|
})
|
|
1202
1175
|
}
|
|
@@ -1206,22 +1179,23 @@ fn find_go_func_literal_name(
|
|
|
1206
1179
|
expression_list: Node<'_>,
|
|
1207
1180
|
code: &Source<'_>,
|
|
1208
1181
|
) -> Option<String> {
|
|
1209
|
-
let holder = expression_list.
|
|
1182
|
+
let holder = expression_list.parent_node()?;
|
|
1210
1183
|
// Comments interleave with expressions in the list but have no matching binding target.
|
|
1211
1184
|
let values: Vec<Node<'_>> = named_children(expression_list)
|
|
1212
1185
|
.into_iter()
|
|
1213
|
-
.filter(|child| child.
|
|
1186
|
+
.filter(|child| child.kind_name() != "comment")
|
|
1214
1187
|
.collect();
|
|
1215
1188
|
let value_index = values.iter().position(|child| child.id() == node.id())?;
|
|
1216
1189
|
|
|
1217
|
-
if holder.
|
|
1190
|
+
if holder.kind_name() == "assignment_statement" && !is_plain_assignment(holder, code) {
|
|
1218
1191
|
return None;
|
|
1219
1192
|
}
|
|
1220
|
-
if holder.
|
|
1193
|
+
if holder.kind_name() == "short_var_declaration" || holder.kind_name() == "assignment_statement"
|
|
1194
|
+
{
|
|
1221
1195
|
let targets = holder.child_by_field_name("left").map(|left| {
|
|
1222
1196
|
named_children(left)
|
|
1223
1197
|
.into_iter()
|
|
1224
|
-
.filter(|child| child.
|
|
1198
|
+
.filter(|child| child.kind_name() != "comment")
|
|
1225
1199
|
.collect::<Vec<_>>()
|
|
1226
1200
|
});
|
|
1227
1201
|
return as_go_binding_name(
|
|
@@ -1233,7 +1207,7 @@ fn find_go_func_literal_name(
|
|
|
1233
1207
|
);
|
|
1234
1208
|
}
|
|
1235
1209
|
|
|
1236
|
-
if holder.
|
|
1210
|
+
if holder.kind_name() == "var_spec" {
|
|
1237
1211
|
let target = find_children_by_field_name(holder, "name")
|
|
1238
1212
|
.get(value_index)
|
|
1239
1213
|
.copied();
|
|
@@ -1247,10 +1221,10 @@ fn find_go_func_literal_name(
|
|
|
1247
1221
|
/// (`m.run = func...`) names its field.
|
|
1248
1222
|
fn as_go_binding_name(target: Option<Node<'_>>, code: &Source<'_>) -> Option<String> {
|
|
1249
1223
|
match target {
|
|
1250
|
-
Some(target) if target.
|
|
1224
|
+
Some(target) if target.kind_name() == "identifier" && node_text(target, code) != "_" => {
|
|
1251
1225
|
Some(node_text(target, code).to_string())
|
|
1252
1226
|
}
|
|
1253
|
-
Some(target) if target.
|
|
1227
|
+
Some(target) if target.kind_name() == "selector_expression" => target
|
|
1254
1228
|
.child_by_field_name("field")
|
|
1255
1229
|
.map(|field| node_text(field, code).to_string()),
|
|
1256
1230
|
_ => None,
|
|
@@ -1260,12 +1234,12 @@ fn as_go_binding_name(target: Option<Node<'_>>, code: &Source<'_>) -> Option<Str
|
|
|
1260
1234
|
fn find_wrapped_component_name(node: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
1261
1235
|
let mut current = node;
|
|
1262
1236
|
loop {
|
|
1263
|
-
let arguments_node = current.
|
|
1264
|
-
let call_node = arguments_node.and_then(|arguments| arguments.
|
|
1237
|
+
let arguments_node = current.parent_node();
|
|
1238
|
+
let call_node = arguments_node.and_then(|arguments| arguments.parent_node());
|
|
1265
1239
|
let (Some(arguments_node), Some(call_node)) = (arguments_node, call_node) else {
|
|
1266
1240
|
return None;
|
|
1267
1241
|
};
|
|
1268
|
-
if arguments_node.
|
|
1242
|
+
if arguments_node.kind_name() != "arguments" || call_node.kind_name() != "call_expression" {
|
|
1269
1243
|
return None;
|
|
1270
1244
|
}
|
|
1271
1245
|
|
|
@@ -1273,8 +1247,8 @@ fn find_wrapped_component_name(node: Node<'_>, code: &Source<'_>) -> Option<Stri
|
|
|
1273
1247
|
return None;
|
|
1274
1248
|
}
|
|
1275
1249
|
|
|
1276
|
-
if let Some(declarator_node) = call_node.
|
|
1277
|
-
if declarator_node.
|
|
1250
|
+
if let Some(declarator_node) = call_node.parent_node() {
|
|
1251
|
+
if declarator_node.kind_name() == "variable_declarator" {
|
|
1278
1252
|
return declarator_node
|
|
1279
1253
|
.child_by_field_name("name")
|
|
1280
1254
|
.map(|name| node_text(name, code).to_string());
|
|
@@ -1299,16 +1273,16 @@ fn is_react_component_wrapper_call(node: Node<'_>, code: &Source<'_>) -> bool {
|
|
|
1299
1273
|
/// variable the closure itself rather than something constructed from it.
|
|
1300
1274
|
fn declares_deduced_type(declarator: Node<'_>) -> bool {
|
|
1301
1275
|
declarator
|
|
1302
|
-
.
|
|
1276
|
+
.parent_node()
|
|
1303
1277
|
.and_then(|declaration| declaration.child_by_field_name("type"))
|
|
1304
|
-
.is_some_and(|declared| declared.
|
|
1278
|
+
.is_some_and(|declared| declared.kind_name() == "placeholder_type_specifier")
|
|
1305
1279
|
}
|
|
1306
1280
|
|
|
1307
1281
|
/// Unwraps a C/C++ declarator chain to the declared name.
|
|
1308
1282
|
fn unwrap_declarator_name(declarator: Option<Node<'_>>, code: &Source<'_>) -> Option<String> {
|
|
1309
1283
|
let mut current = declarator;
|
|
1310
1284
|
while let Some(node) = current {
|
|
1311
|
-
match node.
|
|
1285
|
+
match node.kind_name() {
|
|
1312
1286
|
"identifier" | "field_identifier" | "type_identifier" | "destructor_name"
|
|
1313
1287
|
| "operator_name" => {
|
|
1314
1288
|
return Some(node_text(node, code).to_string());
|
|
@@ -1339,7 +1313,8 @@ fn next_declarator(node: Node<'_>) -> Option<Node<'_>> {
|
|
|
1339
1313
|
if let Some(direct) = node.child_by_field_name("declarator") {
|
|
1340
1314
|
return Some(direct);
|
|
1341
1315
|
}
|
|
1342
|
-
if node.
|
|
1316
|
+
if node.kind_name() == "reference_declarator" || node.kind_name() == "parenthesized_declarator"
|
|
1317
|
+
{
|
|
1343
1318
|
return node.named_child(0);
|
|
1344
1319
|
}
|
|
1345
1320
|
None
|