code-gauge 4.7.0 → 4.7.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/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/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/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 +1 -0
- package/native/Cargo.toml +2 -1
- package/native/code-gauge.wasm +0 -0
- package/native/src/complexity.rs +134 -230
- package/native/src/dep_degree.rs +42 -39
- package/native/src/duplication.rs +65 -63
- package/native/src/functions.rs +169 -152
- package/native/src/languages.rs +20 -0
- package/native/src/lib.rs +10 -5
- package/native/src/measure.rs +109 -123
- package/native/src/napi.rs +61 -2
- package/native/src/ncss.rs +79 -84
- package/native/src/near_miss.rs +7 -7
- package/native/src/tree_index.rs +110 -0
- package/native/src/util.rs +17 -12
- package/native/src/worker_pool.rs +53 -0
- package/package.json +8 -8
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
|
// The Kotlin grammar has no fields; an implemented function or accessor holds a
|
|
40
41
|
// `function_body` child.
|
|
41
|
-
if node.
|
|
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() == "parameter_with_optional_type")
|
|
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,43 @@ 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 holder.
|
|
341
|
-
holder = holder.
|
|
346
|
+
while holder.kind_name() == "prefix_expression" {
|
|
347
|
+
holder = holder.parent_node()?;
|
|
342
348
|
}
|
|
343
|
-
if holder.
|
|
349
|
+
if holder.kind_name() == "property_declaration" {
|
|
344
350
|
return find_kotlin_property_name(holder, code);
|
|
345
351
|
}
|
|
346
|
-
if holder.
|
|
352
|
+
if holder.kind_name() == "assignment" {
|
|
347
353
|
return find_kotlin_assignment_name(holder, code);
|
|
348
354
|
}
|
|
349
355
|
}
|
|
350
356
|
// A `lambda { }` / `proc { }` block is measured, but the call around it is what gets bound.
|
|
351
|
-
if (node.
|
|
357
|
+
if (node.kind_name() == "block" || node.kind_name() == "do_block")
|
|
358
|
+
&& is_ruby_lambda_call(parent, code)
|
|
359
|
+
{
|
|
352
360
|
let call = unwrap_transparent_value_wrappers(parent);
|
|
353
|
-
return match call.
|
|
354
|
-
Some(holder) if holder.
|
|
361
|
+
return match call.parent_node() {
|
|
362
|
+
Some(holder) if holder.kind_name() == "assignment" => {
|
|
355
363
|
find_ruby_assignment_name(holder, code)
|
|
356
364
|
}
|
|
357
|
-
Some(holder) if holder.
|
|
365
|
+
Some(holder) if holder.kind_name() == "pair" => find_pair_key_name(holder, code),
|
|
358
366
|
Some(holder) if is_value_group(holder) => find_parallel_assignment_name(call, code),
|
|
359
367
|
_ => None,
|
|
360
368
|
};
|
|
@@ -364,36 +372,36 @@ pub fn find_function_name(node: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
|
364
372
|
// after the key; an assignment
|
|
365
373
|
// (`obj.run = () => {}`, `run = () => {}`, Rust/C++ `self.cb = |x| x`, C++ `N::run = [] {}`,
|
|
366
374
|
// C# `this.Run = () => 1`) after its target.
|
|
367
|
-
if parent.
|
|
375
|
+
if parent.kind_name() == "pair" {
|
|
368
376
|
return find_pair_key_name(parent, code);
|
|
369
377
|
}
|
|
370
378
|
// A Go keyed composite-literal element (`S{run: func() {}}`) names its value after the key.
|
|
371
|
-
if parent.
|
|
379
|
+
if parent.kind_name() == "literal_element" {
|
|
372
380
|
return find_go_keyed_element_name(parent, code);
|
|
373
381
|
}
|
|
374
382
|
// A Rust struct-literal field (`S { cb: || 1 }`) names its closure after the field.
|
|
375
|
-
if parent.
|
|
383
|
+
if parent.kind_name() == "field_initializer" {
|
|
376
384
|
return parent
|
|
377
385
|
.child_by_field_name("field")
|
|
378
386
|
.map(|field| node_text(field, code).to_string());
|
|
379
387
|
}
|
|
380
388
|
// A C++20 designated initializer (`S s{.run = []{}}`) likewise names its value after the field;
|
|
381
389
|
// an array designator (`{[0] = ...}`) names nothing, like a subscript assignment target.
|
|
382
|
-
if parent.
|
|
390
|
+
if parent.kind_name() == "initializer_pair" {
|
|
383
391
|
return find_children_by_field_name(parent, "designator")
|
|
384
392
|
.last()
|
|
385
|
-
.filter(|designator| designator.
|
|
393
|
+
.filter(|designator| designator.kind_name() == "field_designator")
|
|
386
394
|
.and_then(|designator| designator.named_child(0))
|
|
387
395
|
.map(|field| node_text(field, code).to_string());
|
|
388
396
|
}
|
|
389
|
-
if parent.
|
|
397
|
+
if parent.kind_name() == "assignment_expression" {
|
|
390
398
|
return find_assignment_target_name(parent, code);
|
|
391
399
|
}
|
|
392
400
|
|
|
393
401
|
// A JavaScript class field (`handle = () => {}`) names its property through the `property`
|
|
394
402
|
// field; TypeScript's `public_field_definition` exposes the same thing as `name`. A computed
|
|
395
403
|
// 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.
|
|
404
|
+
let field_name = if parent.kind_name() == "field_definition" {
|
|
397
405
|
"property"
|
|
398
406
|
} else {
|
|
399
407
|
"name"
|
|
@@ -405,7 +413,7 @@ pub fn find_function_name(node: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
|
405
413
|
if !is_value_of_parent(bound, parent) {
|
|
406
414
|
return None;
|
|
407
415
|
}
|
|
408
|
-
match name.
|
|
416
|
+
match name.kind_name() {
|
|
409
417
|
"computed_property_name" => None,
|
|
410
418
|
"string" => find_string_literal_content(name, code),
|
|
411
419
|
_ => Some(node_text(name, code).to_string()),
|
|
@@ -416,7 +424,7 @@ pub fn find_function_name(node: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
|
416
424
|
/// computed key (`[k]: ...`), an interpolated string or symbol, or an empty string names nothing.
|
|
417
425
|
fn find_pair_key_name(pair: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
418
426
|
let key = pair.child_by_field_name("key")?;
|
|
419
|
-
match key.
|
|
427
|
+
match key.kind_name() {
|
|
420
428
|
// A numeric key (`{ 1: () => {} }`) is as stable a property name as an identifier, signed
|
|
421
429
|
// (`{ -1: ... }`) or not; any other expression is computed and names nothing.
|
|
422
430
|
"property_identifier" | "hash_key_symbol" => Some(node_text(key, code).to_string()),
|
|
@@ -432,7 +440,7 @@ fn find_pair_key_name(pair: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
|
432
440
|
for part in binding_children(key) {
|
|
433
441
|
if named_children(part)
|
|
434
442
|
.iter()
|
|
435
|
-
.any(|child| child.
|
|
443
|
+
.any(|child| child.kind_name() == "interpolation")
|
|
436
444
|
{
|
|
437
445
|
return None;
|
|
438
446
|
}
|
|
@@ -450,7 +458,7 @@ fn find_pair_key_name(pair: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
|
450
458
|
fn core_constraint_type(constraint: Node<'_>) -> Node<'_> {
|
|
451
459
|
let mut current = constraint;
|
|
452
460
|
while matches!(
|
|
453
|
-
current.
|
|
461
|
+
current.kind_name(),
|
|
454
462
|
"type_constraint" | "negated_type" | "type_elem" | "interface_type"
|
|
455
463
|
) {
|
|
456
464
|
match named_children(current).as_slice() {
|
|
@@ -474,7 +482,7 @@ fn signed_number_name(key: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
|
474
482
|
.find(|child| !child.is_named())
|
|
475
483
|
.map(|child| node_text(child, code))
|
|
476
484
|
.unwrap_or_default();
|
|
477
|
-
let text = match operand.
|
|
485
|
+
let text = match operand.kind_name() {
|
|
478
486
|
"rune_literal" => find_string_literal_content(operand, code)?,
|
|
479
487
|
_ => node_text(operand, code).to_string(),
|
|
480
488
|
};
|
|
@@ -490,7 +498,7 @@ fn is_signed_number(key: Node<'_>, code: &Source<'_>) -> bool {
|
|
|
490
498
|
signed
|
|
491
499
|
&& named_children(key).first().is_some_and(|operand| {
|
|
492
500
|
matches!(
|
|
493
|
-
operand.
|
|
501
|
+
operand.kind_name(),
|
|
494
502
|
"number"
|
|
495
503
|
| "integer"
|
|
496
504
|
| "float"
|
|
@@ -506,14 +514,14 @@ fn is_signed_number(key: Node<'_>, code: &Source<'_>) -> bool {
|
|
|
506
514
|
/// value the last. An unkeyed element sits under a `literal_value` instead and names nothing.
|
|
507
515
|
fn find_go_keyed_element_name(value_element: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
508
516
|
let keyed = value_element
|
|
509
|
-
.
|
|
510
|
-
.filter(|parent| parent.
|
|
517
|
+
.parent_node()
|
|
518
|
+
.filter(|parent| parent.kind_name() == "keyed_element")?;
|
|
511
519
|
let elements = named_children(keyed);
|
|
512
520
|
if elements.len() < 2 || elements.last()?.id() != value_element.id() {
|
|
513
521
|
return None;
|
|
514
522
|
}
|
|
515
523
|
let key = named_children(*elements.first()?).into_iter().next()?;
|
|
516
|
-
match key.
|
|
524
|
+
match key.kind_name() {
|
|
517
525
|
"identifier" | "field_identifier" if !has_value_keys(keyed, code) => {
|
|
518
526
|
Some(node_text(key, code).to_string())
|
|
519
527
|
}
|
|
@@ -536,7 +544,7 @@ fn find_go_keyed_element_name(value_element: Node<'_>, code: &Source<'_>) -> Opt
|
|
|
536
544
|
/// `key` holds, so it names nothing, exactly like a computed property key.
|
|
537
545
|
fn has_value_keys(keyed: Node<'_>, code: &Source<'_>) -> bool {
|
|
538
546
|
keyed
|
|
539
|
-
.
|
|
547
|
+
.parent_node()
|
|
540
548
|
.and_then(|body| key_type_of_literal_body(body, code))
|
|
541
549
|
.is_some_and(|declared| is_value_keyed_type(declared, code))
|
|
542
550
|
}
|
|
@@ -557,7 +565,7 @@ fn value_keyed_type(declared: Node<'_>, code: &Source<'_>, depth: usize) -> bool
|
|
|
557
565
|
return false;
|
|
558
566
|
}
|
|
559
567
|
let resolved = resolve_named_type(declared, code);
|
|
560
|
-
match resolved.
|
|
568
|
+
match resolved.kind_name() {
|
|
561
569
|
"map_type" | "slice_type" | "array_type" | "implicit_length_array_type" => true,
|
|
562
570
|
"type_constraint" | "interface_type" | "type_elem" | "negated_type" => {
|
|
563
571
|
// Method requirements restrict what a type does, not what it is, so only the type terms
|
|
@@ -575,14 +583,14 @@ fn value_keyed_type(declared: Node<'_>, code: &Source<'_>, depth: usize) -> bool
|
|
|
575
583
|
/// Whether a constraint element only requires methods, directly or through an embedded interface
|
|
576
584
|
/// that does; such an element leaves the underlying type free.
|
|
577
585
|
fn requires_methods_only(declared: Node<'_>, code: &Source<'_>, depth: usize) -> bool {
|
|
578
|
-
if matches!(declared.
|
|
586
|
+
if matches!(declared.kind_name(), "method_elem" | "method_spec") {
|
|
579
587
|
return true;
|
|
580
588
|
}
|
|
581
589
|
if depth >= MAX_CONSTRAINT_DEPTH {
|
|
582
590
|
return false;
|
|
583
591
|
}
|
|
584
592
|
let resolved = resolve_named_type(declared, code);
|
|
585
|
-
match resolved.
|
|
593
|
+
match resolved.kind_name() {
|
|
586
594
|
"interface_type" | "type_constraint" | "type_elem" => {
|
|
587
595
|
let terms = binding_children(resolved);
|
|
588
596
|
!terms.is_empty()
|
|
@@ -605,7 +613,7 @@ fn resolve_named_type<'t>(declared: Node<'t>, code: &Source<'t>) -> Node<'t> {
|
|
|
605
613
|
let mut visited = Vec::new();
|
|
606
614
|
while !visited.contains(¤t.id()) {
|
|
607
615
|
visited.push(current.id());
|
|
608
|
-
let next = match current.
|
|
616
|
+
let next = match current.kind_name() {
|
|
609
617
|
"generic_type" => current.child_by_field_name("type"),
|
|
610
618
|
// Go allows parentheses around a type; they name the type they hold.
|
|
611
619
|
// Parentheses name the type they hold, and a nested literal of a pointer element type
|
|
@@ -623,7 +631,7 @@ fn resolve_named_type<'t>(declared: Node<'t>, code: &Source<'t>) -> Node<'t> {
|
|
|
623
631
|
|
|
624
632
|
/// The type a name stands for, resolved through the scopes enclosing it, innermost first.
|
|
625
633
|
fn lookup_declared_type<'t>(declared: Node<'t>, code: &Source<'t>) -> Option<Node<'t>> {
|
|
626
|
-
if declared.
|
|
634
|
+
if declared.kind_name() != "type_identifier" {
|
|
627
635
|
return None;
|
|
628
636
|
}
|
|
629
637
|
let name = node_text(declared, code);
|
|
@@ -636,7 +644,7 @@ fn lookup_declared_type<'t>(declared: Node<'t>, code: &Source<'t>) -> Option<Nod
|
|
|
636
644
|
// what the literal is written against.
|
|
637
645
|
if let Some(constraint) = named_children(current)
|
|
638
646
|
.into_iter()
|
|
639
|
-
.filter(|child| child.
|
|
647
|
+
.filter(|child| child.kind_name() == "type_parameter_list")
|
|
640
648
|
.flat_map(|list| declared_type_parameters(list))
|
|
641
649
|
.find(|(parameter_name, _)| node_text(*parameter_name, code) == name)
|
|
642
650
|
.and_then(|(_, constraint)| constraint)
|
|
@@ -645,7 +653,7 @@ fn lookup_declared_type<'t>(declared: Node<'t>, code: &Source<'t>) -> Option<Nod
|
|
|
645
653
|
}
|
|
646
654
|
// A method's receiver carries the parameters of the type it is declared on
|
|
647
655
|
// (`func (r R[T]) ...`), so the name resolves through that type's declaration.
|
|
648
|
-
if current.
|
|
656
|
+
if current.kind_name() == "method_declaration" {
|
|
649
657
|
if let Some(constraint) = receiver_parameter_constraint(current, name, code) {
|
|
650
658
|
return Some(core_constraint_type(constraint));
|
|
651
659
|
}
|
|
@@ -653,7 +661,7 @@ fn lookup_declared_type<'t>(declared: Node<'t>, code: &Source<'t>) -> Option<Nod
|
|
|
653
661
|
if let Some(found) = find_type_spec(current, name, code) {
|
|
654
662
|
return found.child_by_field_name("type");
|
|
655
663
|
}
|
|
656
|
-
scope = current.
|
|
664
|
+
scope = current.parent_node();
|
|
657
665
|
}
|
|
658
666
|
None
|
|
659
667
|
}
|
|
@@ -662,9 +670,9 @@ fn lookup_declared_type<'t>(declared: Node<'t>, code: &Source<'t>) -> Option<Nod
|
|
|
662
670
|
fn find_type_spec<'t>(scope: Node<'t>, name: &str, code: &Source<'t>) -> Option<Node<'t>> {
|
|
663
671
|
named_children(scope)
|
|
664
672
|
.into_iter()
|
|
665
|
-
.filter(|child| child.
|
|
673
|
+
.filter(|child| child.kind_name() == "type_declaration")
|
|
666
674
|
.flat_map(named_children)
|
|
667
|
-
.filter(|spec| spec.
|
|
675
|
+
.filter(|spec| spec.kind_name() == "type_spec" || spec.kind_name() == "type_alias")
|
|
668
676
|
.find(|spec| {
|
|
669
677
|
spec.child_by_field_name("name")
|
|
670
678
|
.is_some_and(|declared_name| node_text(declared_name, code) == name)
|
|
@@ -685,7 +693,7 @@ fn receiver_parameter_constraint<'t>(
|
|
|
685
693
|
// A pointer or parenthesized receiver (`func (r *R[T])`, `func (r (R[T]))`) wraps the
|
|
686
694
|
// instantiation, in either order.
|
|
687
695
|
.map(unwrap_receiver_type)
|
|
688
|
-
.find(|declared| declared.
|
|
696
|
+
.find(|declared| declared.kind_name() == "generic_type")?;
|
|
689
697
|
let arguments = binding_children(instantiation.child_by_field_name("type_arguments")?);
|
|
690
698
|
let position = arguments.iter().position(|argument| {
|
|
691
699
|
let argument = named_children(*argument)
|
|
@@ -704,7 +712,7 @@ fn receiver_parameter_constraint<'t>(
|
|
|
704
712
|
/// The type a receiver names, past the pointer and parenthesis wrappers it may carry.
|
|
705
713
|
fn unwrap_receiver_type(declared: Node<'_>) -> Node<'_> {
|
|
706
714
|
let mut current = declared;
|
|
707
|
-
while matches!(current.
|
|
715
|
+
while matches!(current.kind_name(), "pointer_type" | "parenthesized_type") {
|
|
708
716
|
match named_children(current).into_iter().next() {
|
|
709
717
|
Some(inner) => current = inner,
|
|
710
718
|
None => break,
|
|
@@ -718,7 +726,7 @@ fn unwrap_receiver_type(declared: Node<'_>) -> Node<'_> {
|
|
|
718
726
|
fn declared_type_parameters<'t>(list: Node<'t>) -> Vec<(Node<'t>, Option<Node<'t>>)> {
|
|
719
727
|
binding_children(list)
|
|
720
728
|
.into_iter()
|
|
721
|
-
.filter(|declaration| declaration.
|
|
729
|
+
.filter(|declaration| declaration.kind_name() == "type_parameter_declaration")
|
|
722
730
|
.flat_map(|declaration| {
|
|
723
731
|
let constraint = declaration.child_by_field_name("type");
|
|
724
732
|
find_children_by_field_name(declaration, "name")
|
|
@@ -734,24 +742,24 @@ fn declared_type_parameters<'t>(list: Node<'t>) -> Vec<(Node<'t>, Option<Node<'t
|
|
|
734
742
|
/// {k: f}}` nests a map, `[]S{{run: f}}` a struct). A literal nested in a struct field keeps the
|
|
735
743
|
/// struct reading, since the field's type is not written at the literal.
|
|
736
744
|
fn key_type_of_literal_body<'t>(body: Node<'t>, code: &Source<'t>) -> Option<Node<'t>> {
|
|
737
|
-
let parent = body.
|
|
738
|
-
if parent.
|
|
745
|
+
let parent = body.parent_node()?;
|
|
746
|
+
if parent.kind_name() == "composite_literal" {
|
|
739
747
|
return parent.child_by_field_name("type");
|
|
740
748
|
}
|
|
741
|
-
if parent.
|
|
749
|
+
if parent.kind_name() != "literal_element" {
|
|
742
750
|
return None;
|
|
743
751
|
}
|
|
744
|
-
let mut container = parent.
|
|
745
|
-
if container.
|
|
746
|
-
container = container.
|
|
752
|
+
let mut container = parent.parent_node()?;
|
|
753
|
+
if container.kind_name() == "keyed_element" {
|
|
754
|
+
container = container.parent_node()?;
|
|
747
755
|
}
|
|
748
|
-
if container.
|
|
756
|
+
if container.kind_name() != "literal_value" {
|
|
749
757
|
return None;
|
|
750
758
|
}
|
|
751
759
|
// The container's own type may be a name, which the element type is read through.
|
|
752
760
|
let declared = key_type_of_literal_body(container, code)?;
|
|
753
761
|
let holder = resolve_named_type(declared, code);
|
|
754
|
-
let element = match holder.
|
|
762
|
+
let element = match holder.kind_name() {
|
|
755
763
|
"map_type" => holder.child_by_field_name("value"),
|
|
756
764
|
"slice_type" | "array_type" | "implicit_length_array_type" => {
|
|
757
765
|
holder.child_by_field_name("element")
|
|
@@ -768,7 +776,7 @@ fn instantiated_type_argument<'t>(
|
|
|
768
776
|
declared: Node<'t>,
|
|
769
777
|
code: &Source<'t>,
|
|
770
778
|
) -> Option<Node<'t>> {
|
|
771
|
-
if element.
|
|
779
|
+
if element.kind_name() != "type_identifier" || declared.kind_name() != "generic_type" {
|
|
772
780
|
return None;
|
|
773
781
|
}
|
|
774
782
|
let base = declared.child_by_field_name("type")?;
|
|
@@ -793,7 +801,7 @@ fn find_declared_type_spec<'t>(from: Node<'t>, name: &str, code: &Source<'t>) ->
|
|
|
793
801
|
if let Some(spec) = find_type_spec(current, name, code) {
|
|
794
802
|
return Some(spec);
|
|
795
803
|
}
|
|
796
|
-
scope = current.
|
|
804
|
+
scope = current.parent_node();
|
|
797
805
|
}
|
|
798
806
|
None
|
|
799
807
|
}
|
|
@@ -804,7 +812,10 @@ fn find_declared_type_spec<'t>(from: Node<'t>, name: &str, code: &Source<'t>) ->
|
|
|
804
812
|
/// `string_content` (Python nests escapes inside it). Interpolation makes the key unstable.
|
|
805
813
|
fn find_string_literal_content(literal: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
806
814
|
let children = named_children(literal);
|
|
807
|
-
if children
|
|
815
|
+
if children
|
|
816
|
+
.iter()
|
|
817
|
+
.any(|child| child.kind_name() == "interpolation")
|
|
818
|
+
{
|
|
808
819
|
return None;
|
|
809
820
|
}
|
|
810
821
|
// Go exposes no content node at all: it names only the escapes, so the concatenation is
|
|
@@ -812,7 +823,7 @@ fn find_string_literal_content(literal: Node<'_>, code: &Source<'_>) -> Option<S
|
|
|
812
823
|
let mut content = String::new();
|
|
813
824
|
let mut has_content_node = false;
|
|
814
825
|
for child in &children {
|
|
815
|
-
match child.
|
|
826
|
+
match child.kind_name() {
|
|
816
827
|
"string_content" | "string_fragment" => {
|
|
817
828
|
has_content_node = true;
|
|
818
829
|
content.push_str(node_text(*child, code));
|
|
@@ -860,7 +871,7 @@ fn find_assignment_target_name(assignment: Node<'_>, code: &Source<'_>) -> Optio
|
|
|
860
871
|
return None;
|
|
861
872
|
}
|
|
862
873
|
let target = assignment.child_by_field_name("left")?;
|
|
863
|
-
let name = match target.
|
|
874
|
+
let name = match target.kind_name() {
|
|
864
875
|
"identifier" => target,
|
|
865
876
|
"member_expression" => target.child_by_field_name("property")?,
|
|
866
877
|
"field_expression" => target.child_by_field_name("field")?,
|
|
@@ -882,8 +893,8 @@ fn find_kotlin_assignment_name(assignment: Node<'_>, code: &Source<'_>) -> Optio
|
|
|
882
893
|
let target = first_named_child_of_kind(assignment, "directly_assignable_expression")?;
|
|
883
894
|
let children = named_children(target);
|
|
884
895
|
let holder = match children.last()? {
|
|
885
|
-
last if last.
|
|
886
|
-
last if last.
|
|
896
|
+
last if last.kind_name() == "navigation_suffix" => *last,
|
|
897
|
+
last if last.kind_name() == "simple_identifier" && children.len() == 1 => target,
|
|
887
898
|
_ => return None,
|
|
888
899
|
};
|
|
889
900
|
first_named_child_of_kind(holder, "simple_identifier")
|
|
@@ -895,17 +906,17 @@ fn find_kotlin_assignment_name(assignment: Node<'_>, code: &Source<'_>) -> Optio
|
|
|
895
906
|
/// Kotlin functions by their identifier child, Kotlin secondary constructors and C# destructors
|
|
896
907
|
/// after their class, and C# operators like C++ ones.
|
|
897
908
|
fn find_member_function_name(node: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
898
|
-
match node.
|
|
909
|
+
match node.kind_name() {
|
|
899
910
|
"accessor_declaration" => {
|
|
900
911
|
let keyword = node_text(node.child_by_field_name("name")?, code);
|
|
901
|
-
let owner = node.
|
|
912
|
+
let owner = node.parent_node()?.parent_node()?;
|
|
902
913
|
Some(format!("{}.{keyword}", csharp_property_name(owner, code)?))
|
|
903
914
|
}
|
|
904
915
|
"property_declaration" | "indexer_declaration" => {
|
|
905
916
|
Some(format!("{}.get", csharp_property_name(node, code)?))
|
|
906
917
|
}
|
|
907
918
|
"getter" | "setter" => {
|
|
908
|
-
let keyword = if node.
|
|
919
|
+
let keyword = if node.kind_name() == "getter" {
|
|
909
920
|
"get"
|
|
910
921
|
} else {
|
|
911
922
|
"set"
|
|
@@ -920,13 +931,15 @@ fn find_member_function_name(node: Node<'_>, code: &Source<'_>) -> Option<String
|
|
|
920
931
|
.map(|name| node_text(name, code).to_string())
|
|
921
932
|
}
|
|
922
933
|
"secondary_constructor" => {
|
|
923
|
-
let mut ancestor = node.
|
|
934
|
+
let mut ancestor = node.parent_node();
|
|
924
935
|
while let Some(current) = ancestor {
|
|
925
|
-
if current.
|
|
936
|
+
if current.kind_name() == "class_declaration"
|
|
937
|
+
|| current.kind_name() == "object_declaration"
|
|
938
|
+
{
|
|
926
939
|
return first_named_child_of_kind(current, "type_identifier")
|
|
927
940
|
.map(|name| node_text(name, code).to_string());
|
|
928
941
|
}
|
|
929
|
-
ancestor = current.
|
|
942
|
+
ancestor = current.parent_node();
|
|
930
943
|
}
|
|
931
944
|
None
|
|
932
945
|
}
|
|
@@ -948,7 +961,7 @@ fn find_member_function_name(node: Node<'_>, code: &Source<'_>) -> Option<String
|
|
|
948
961
|
|
|
949
962
|
/// A C# property or indexer (`this`) name.
|
|
950
963
|
fn csharp_property_name(owner: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
951
|
-
match owner.
|
|
964
|
+
match owner.kind_name() {
|
|
952
965
|
"indexer_declaration" => Some("this".to_string()),
|
|
953
966
|
_ => Some(node_text(owner.child_by_field_name("name")?, code).to_string()),
|
|
954
967
|
}
|
|
@@ -959,18 +972,18 @@ fn csharp_property_name(owner: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
|
959
972
|
/// sibling after the property, any preceding accessor, and any comments between them.
|
|
960
973
|
fn find_kotlin_accessor_owner_name(accessor: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
961
974
|
if let Some(name) = accessor
|
|
962
|
-
.
|
|
975
|
+
.parent_node()
|
|
963
976
|
.and_then(|parent| find_kotlin_property_name(parent, code))
|
|
964
977
|
{
|
|
965
978
|
return Some(name);
|
|
966
979
|
}
|
|
967
980
|
let mut sibling = accessor.prev_named_sibling();
|
|
968
981
|
while let Some(current) = sibling {
|
|
969
|
-
if current.
|
|
982
|
+
if current.kind_name() == "property_declaration" {
|
|
970
983
|
return find_kotlin_property_name(current, code);
|
|
971
984
|
}
|
|
972
|
-
if !matches!(current.
|
|
973
|
-
&& !crate::ncss::COMMENT_NODE_TYPES.contains(¤t.
|
|
985
|
+
if !matches!(current.kind_name(), "getter" | "setter")
|
|
986
|
+
&& !crate::ncss::COMMENT_NODE_TYPES.contains(¤t.kind_name())
|
|
974
987
|
{
|
|
975
988
|
return None;
|
|
976
989
|
}
|
|
@@ -981,7 +994,7 @@ fn find_kotlin_accessor_owner_name(accessor: Node<'_>, code: &Source<'_>) -> Opt
|
|
|
981
994
|
|
|
982
995
|
/// The declared name of a Kotlin `property_declaration` (`val name: T`), if it declares one.
|
|
983
996
|
fn find_kotlin_property_name(property: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
984
|
-
if property.
|
|
997
|
+
if property.kind_name() != "property_declaration" {
|
|
985
998
|
return None;
|
|
986
999
|
}
|
|
987
1000
|
let declaration = first_named_child_of_kind(property, "variable_declaration")?;
|
|
@@ -992,7 +1005,7 @@ fn find_kotlin_property_name(property: Node<'_>, code: &Source<'_>) -> Option<St
|
|
|
992
1005
|
fn first_named_child_of_kind<'t>(node: Node<'t>, kind: &str) -> Option<Node<'t>> {
|
|
993
1006
|
named_children(node)
|
|
994
1007
|
.into_iter()
|
|
995
|
-
.find(|child| child.
|
|
1008
|
+
.find(|child| child.kind_name() == kind)
|
|
996
1009
|
}
|
|
997
1010
|
|
|
998
1011
|
/// Whether the node occupies its parent's value position: the `value` field, or no field at all
|
|
@@ -1017,7 +1030,7 @@ fn is_value_of_parent(node: Node<'_>, parent: Node<'_>) -> bool {
|
|
|
1017
1030
|
/// or a mapping is unordered, so it groups nothing positionally.
|
|
1018
1031
|
fn is_value_group(node: Node<'_>) -> bool {
|
|
1019
1032
|
matches!(
|
|
1020
|
-
node.
|
|
1033
|
+
node.kind_name(),
|
|
1021
1034
|
"expression_list" | "right_assignment_list" | "tuple" | "list" | "array"
|
|
1022
1035
|
)
|
|
1023
1036
|
}
|
|
@@ -1025,7 +1038,7 @@ fn is_value_group(node: Node<'_>) -> bool {
|
|
|
1025
1038
|
/// The matching target groups, which a nested value group is aligned against level by level.
|
|
1026
1039
|
fn is_target_group(node: Node<'_>) -> bool {
|
|
1027
1040
|
matches!(
|
|
1028
|
-
node.
|
|
1041
|
+
node.kind_name(),
|
|
1029
1042
|
"pattern_list"
|
|
1030
1043
|
| "left_assignment_list"
|
|
1031
1044
|
| "tuple_pattern"
|
|
@@ -1041,13 +1054,13 @@ fn find_parallel_assignment_name(value: Node<'_>, code: &Source<'_>) -> Option<S
|
|
|
1041
1054
|
let mut positions = Vec::new();
|
|
1042
1055
|
let mut current = value;
|
|
1043
1056
|
let assignment = loop {
|
|
1044
|
-
let parent = current.
|
|
1057
|
+
let parent = current.parent_node()?;
|
|
1045
1058
|
if is_value_group(parent) {
|
|
1046
1059
|
positions.push((parent, current));
|
|
1047
1060
|
current = parent;
|
|
1048
1061
|
continue;
|
|
1049
1062
|
}
|
|
1050
|
-
if parent.
|
|
1063
|
+
if parent.kind_name() == "assignment"
|
|
1051
1064
|
&& parent.child_by_field_name("right")?.id() == current.id()
|
|
1052
1065
|
{
|
|
1053
1066
|
break parent;
|
|
@@ -1059,9 +1072,9 @@ fn find_parallel_assignment_name(value: Node<'_>, code: &Source<'_>) -> Option<S
|
|
|
1059
1072
|
// Ruby spells a fully parenthesized target list as a `left_assignment_list` holding one
|
|
1060
1073
|
// destructured group (`(a, b) = f, g`), which aligns against that inner one. A Python
|
|
1061
1074
|
// singleton tuple is a real destructuring level instead, so it is left alone.
|
|
1062
|
-
while target.
|
|
1075
|
+
while target.kind_name() == "left_assignment_list" {
|
|
1063
1076
|
match binding_children(target).as_slice() {
|
|
1064
|
-
[only] if only.
|
|
1077
|
+
[only] if only.kind_name() == "destructured_left_assignment" => target = *only,
|
|
1065
1078
|
_ => break,
|
|
1066
1079
|
}
|
|
1067
1080
|
}
|
|
@@ -1086,7 +1099,7 @@ fn aligned_target<'t>(values: Node<'_>, value: Node<'_>, targets: Node<'t>) -> O
|
|
|
1086
1099
|
let splat_after = value_list[index + 1..].iter().any(is_splat);
|
|
1087
1100
|
let trailing = value_list.len() - 1 - index;
|
|
1088
1101
|
let unpacks = matches!(
|
|
1089
|
-
targets.
|
|
1102
|
+
targets.kind_name(),
|
|
1090
1103
|
"pattern_list" | "tuple_pattern" | "list_pattern"
|
|
1091
1104
|
);
|
|
1092
1105
|
let targets = binding_children(targets);
|
|
@@ -1123,10 +1136,12 @@ fn aligned_target<'t>(values: Node<'_>, value: Node<'_>, targets: Node<'t>) -> O
|
|
|
1123
1136
|
// it, by failing the assignment, while Ruby fills the trailing targets from the left
|
|
1124
1137
|
// when it underflows.
|
|
1125
1138
|
let reaches_trailing_targets = written_values + 1 >= targets.len()
|
|
1126
|
-
|| value_list
|
|
1139
|
+
|| value_list
|
|
1140
|
+
.iter()
|
|
1141
|
+
.any(|child| child.kind_name() == "list_splat");
|
|
1127
1142
|
if reaches_trailing_targets {
|
|
1128
1143
|
targets.get(targets.len() - 1 - trailing).copied()
|
|
1129
|
-
} else if !splat_before && targets[splat].
|
|
1144
|
+
} else if !splat_before && targets[splat].kind_name() == "rest_assignment" {
|
|
1130
1145
|
// Ruby empties the star and fills the trailing targets from the left when the
|
|
1131
1146
|
// values run out, so each one binds the next target (`a, *r, c, d = x, f` binds
|
|
1132
1147
|
// `f` to `c`); Python fails such an assignment instead.
|
|
@@ -1142,7 +1157,7 @@ fn aligned_target<'t>(values: Node<'_>, value: Node<'_>, targets: Node<'t>) -> O
|
|
|
1142
1157
|
/// A splat on either side of a parallel assignment (`*xs`, `*rest`).
|
|
1143
1158
|
fn is_splat(node: &Node<'_>) -> bool {
|
|
1144
1159
|
matches!(
|
|
1145
|
-
node.
|
|
1160
|
+
node.kind_name(),
|
|
1146
1161
|
"splat_argument" | "rest_assignment" | "list_splat" | "list_splat_pattern"
|
|
1147
1162
|
)
|
|
1148
1163
|
}
|
|
@@ -1150,7 +1165,7 @@ fn is_splat(node: &Node<'_>) -> bool {
|
|
|
1150
1165
|
fn binding_children<'t>(node: Node<'t>) -> Vec<Node<'t>> {
|
|
1151
1166
|
named_children(node)
|
|
1152
1167
|
.into_iter()
|
|
1153
|
-
.filter(|child| !crate::ncss::COMMENT_NODE_TYPES.contains(&child.
|
|
1168
|
+
.filter(|child| !crate::ncss::COMMENT_NODE_TYPES.contains(&child.kind_name()))
|
|
1154
1169
|
.collect()
|
|
1155
1170
|
}
|
|
1156
1171
|
|
|
@@ -1158,7 +1173,7 @@ fn binding_children<'t>(node: Node<'t>) -> Vec<Node<'t>> {
|
|
|
1158
1173
|
/// global variable; the method of a Ruby attribute writer (`self.run = ...`); or a Python attribute
|
|
1159
1174
|
/// (`obj.run = ...`). A subscript or a splat has no stable name and binds none.
|
|
1160
1175
|
fn find_assignment_target_text(target: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
1161
|
-
match target.
|
|
1176
|
+
match target.kind_name() {
|
|
1162
1177
|
"identifier" | "constant" | "instance_variable" | "class_variable" | "global_variable" => {
|
|
1163
1178
|
Some(node_text(target, code).to_string())
|
|
1164
1179
|
}
|
|
@@ -1179,7 +1194,7 @@ fn find_ruby_assignment_name(assignment: Node<'_>, code: &Source<'_>) -> Option<
|
|
|
1179
1194
|
let left = assignment.child_by_field_name("left")?;
|
|
1180
1195
|
let mut target = left;
|
|
1181
1196
|
if matches!(
|
|
1182
|
-
left.
|
|
1197
|
+
left.kind_name(),
|
|
1183
1198
|
"left_assignment_list" | "destructured_left_assignment"
|
|
1184
1199
|
) {
|
|
1185
1200
|
while is_target_group(target) {
|
|
@@ -1192,11 +1207,11 @@ fn find_ruby_assignment_name(assignment: Node<'_>, code: &Source<'_>) -> Option<
|
|
|
1192
1207
|
}
|
|
1193
1208
|
|
|
1194
1209
|
fn is_ruby_lambda_call(node: Node<'_>, code: &Source<'_>) -> bool {
|
|
1195
|
-
if node.
|
|
1210
|
+
if node.kind_name() != "call" || node.child_by_field_name("receiver").is_some() {
|
|
1196
1211
|
return false;
|
|
1197
1212
|
}
|
|
1198
1213
|
node.child_by_field_name("method").is_some_and(|method| {
|
|
1199
|
-
method.
|
|
1214
|
+
method.kind_name() == "identifier"
|
|
1200
1215
|
&& (node_text(method, code) == "lambda" || node_text(method, code) == "proc")
|
|
1201
1216
|
})
|
|
1202
1217
|
}
|
|
@@ -1206,22 +1221,23 @@ fn find_go_func_literal_name(
|
|
|
1206
1221
|
expression_list: Node<'_>,
|
|
1207
1222
|
code: &Source<'_>,
|
|
1208
1223
|
) -> Option<String> {
|
|
1209
|
-
let holder = expression_list.
|
|
1224
|
+
let holder = expression_list.parent_node()?;
|
|
1210
1225
|
// Comments interleave with expressions in the list but have no matching binding target.
|
|
1211
1226
|
let values: Vec<Node<'_>> = named_children(expression_list)
|
|
1212
1227
|
.into_iter()
|
|
1213
|
-
.filter(|child| child.
|
|
1228
|
+
.filter(|child| child.kind_name() != "comment")
|
|
1214
1229
|
.collect();
|
|
1215
1230
|
let value_index = values.iter().position(|child| child.id() == node.id())?;
|
|
1216
1231
|
|
|
1217
|
-
if holder.
|
|
1232
|
+
if holder.kind_name() == "assignment_statement" && !is_plain_assignment(holder, code) {
|
|
1218
1233
|
return None;
|
|
1219
1234
|
}
|
|
1220
|
-
if holder.
|
|
1235
|
+
if holder.kind_name() == "short_var_declaration" || holder.kind_name() == "assignment_statement"
|
|
1236
|
+
{
|
|
1221
1237
|
let targets = holder.child_by_field_name("left").map(|left| {
|
|
1222
1238
|
named_children(left)
|
|
1223
1239
|
.into_iter()
|
|
1224
|
-
.filter(|child| child.
|
|
1240
|
+
.filter(|child| child.kind_name() != "comment")
|
|
1225
1241
|
.collect::<Vec<_>>()
|
|
1226
1242
|
});
|
|
1227
1243
|
return as_go_binding_name(
|
|
@@ -1233,7 +1249,7 @@ fn find_go_func_literal_name(
|
|
|
1233
1249
|
);
|
|
1234
1250
|
}
|
|
1235
1251
|
|
|
1236
|
-
if holder.
|
|
1252
|
+
if holder.kind_name() == "var_spec" {
|
|
1237
1253
|
let target = find_children_by_field_name(holder, "name")
|
|
1238
1254
|
.get(value_index)
|
|
1239
1255
|
.copied();
|
|
@@ -1247,10 +1263,10 @@ fn find_go_func_literal_name(
|
|
|
1247
1263
|
/// (`m.run = func...`) names its field.
|
|
1248
1264
|
fn as_go_binding_name(target: Option<Node<'_>>, code: &Source<'_>) -> Option<String> {
|
|
1249
1265
|
match target {
|
|
1250
|
-
Some(target) if target.
|
|
1266
|
+
Some(target) if target.kind_name() == "identifier" && node_text(target, code) != "_" => {
|
|
1251
1267
|
Some(node_text(target, code).to_string())
|
|
1252
1268
|
}
|
|
1253
|
-
Some(target) if target.
|
|
1269
|
+
Some(target) if target.kind_name() == "selector_expression" => target
|
|
1254
1270
|
.child_by_field_name("field")
|
|
1255
1271
|
.map(|field| node_text(field, code).to_string()),
|
|
1256
1272
|
_ => None,
|
|
@@ -1260,12 +1276,12 @@ fn as_go_binding_name(target: Option<Node<'_>>, code: &Source<'_>) -> Option<Str
|
|
|
1260
1276
|
fn find_wrapped_component_name(node: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
1261
1277
|
let mut current = node;
|
|
1262
1278
|
loop {
|
|
1263
|
-
let arguments_node = current.
|
|
1264
|
-
let call_node = arguments_node.and_then(|arguments| arguments.
|
|
1279
|
+
let arguments_node = current.parent_node();
|
|
1280
|
+
let call_node = arguments_node.and_then(|arguments| arguments.parent_node());
|
|
1265
1281
|
let (Some(arguments_node), Some(call_node)) = (arguments_node, call_node) else {
|
|
1266
1282
|
return None;
|
|
1267
1283
|
};
|
|
1268
|
-
if arguments_node.
|
|
1284
|
+
if arguments_node.kind_name() != "arguments" || call_node.kind_name() != "call_expression" {
|
|
1269
1285
|
return None;
|
|
1270
1286
|
}
|
|
1271
1287
|
|
|
@@ -1273,8 +1289,8 @@ fn find_wrapped_component_name(node: Node<'_>, code: &Source<'_>) -> Option<Stri
|
|
|
1273
1289
|
return None;
|
|
1274
1290
|
}
|
|
1275
1291
|
|
|
1276
|
-
if let Some(declarator_node) = call_node.
|
|
1277
|
-
if declarator_node.
|
|
1292
|
+
if let Some(declarator_node) = call_node.parent_node() {
|
|
1293
|
+
if declarator_node.kind_name() == "variable_declarator" {
|
|
1278
1294
|
return declarator_node
|
|
1279
1295
|
.child_by_field_name("name")
|
|
1280
1296
|
.map(|name| node_text(name, code).to_string());
|
|
@@ -1299,16 +1315,16 @@ fn is_react_component_wrapper_call(node: Node<'_>, code: &Source<'_>) -> bool {
|
|
|
1299
1315
|
/// variable the closure itself rather than something constructed from it.
|
|
1300
1316
|
fn declares_deduced_type(declarator: Node<'_>) -> bool {
|
|
1301
1317
|
declarator
|
|
1302
|
-
.
|
|
1318
|
+
.parent_node()
|
|
1303
1319
|
.and_then(|declaration| declaration.child_by_field_name("type"))
|
|
1304
|
-
.is_some_and(|declared| declared.
|
|
1320
|
+
.is_some_and(|declared| declared.kind_name() == "placeholder_type_specifier")
|
|
1305
1321
|
}
|
|
1306
1322
|
|
|
1307
1323
|
/// Unwraps a C/C++ declarator chain to the declared name.
|
|
1308
1324
|
fn unwrap_declarator_name(declarator: Option<Node<'_>>, code: &Source<'_>) -> Option<String> {
|
|
1309
1325
|
let mut current = declarator;
|
|
1310
1326
|
while let Some(node) = current {
|
|
1311
|
-
match node.
|
|
1327
|
+
match node.kind_name() {
|
|
1312
1328
|
"identifier" | "field_identifier" | "type_identifier" | "destructor_name"
|
|
1313
1329
|
| "operator_name" => {
|
|
1314
1330
|
return Some(node_text(node, code).to_string());
|
|
@@ -1339,7 +1355,8 @@ fn next_declarator(node: Node<'_>) -> Option<Node<'_>> {
|
|
|
1339
1355
|
if let Some(direct) = node.child_by_field_name("declarator") {
|
|
1340
1356
|
return Some(direct);
|
|
1341
1357
|
}
|
|
1342
|
-
if node.
|
|
1358
|
+
if node.kind_name() == "reference_declarator" || node.kind_name() == "parenthesized_declarator"
|
|
1359
|
+
{
|
|
1343
1360
|
return node.named_child(0);
|
|
1344
1361
|
}
|
|
1345
1362
|
None
|