code-gauge 10.0.0 → 10.2.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 +56 -34
- package/THIRD-PARTY-NOTICES.txt +8 -8
- package/dist/checkCommand.cjs +2 -2
- package/dist/checkCommand.cjs.map +1 -1
- package/dist/checkCommand.d.ts +2 -5
- package/dist/checkCommand.js +2 -2
- package/dist/checkCommand.js.map +1 -1
- package/dist/crossFileDuplication.cjs +1 -1
- package/dist/crossFileDuplication.cjs.map +1 -1
- package/dist/crossFileDuplication.d.ts +14 -2
- 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.d.ts +6 -4
- package/dist/crossFileNearMiss.js +1 -1
- package/dist/crossFileNearMiss.js.map +1 -1
- package/dist/duplication.cjs +1 -1
- package/dist/duplication.cjs.map +1 -1
- package/dist/duplication.d.ts +22 -7
- package/dist/duplication.js +1 -1
- package/dist/duplication.js.map +1 -1
- package/dist/nativeMetrics.cjs +1 -1
- package/dist/nativeMetrics.cjs.map +1 -1
- package/dist/nativeMetrics.d.ts +1 -1
- package/dist/nativeMetrics.js +1 -1
- package/dist/nativeMetrics.js.map +1 -1
- package/dist/thresholdCheck.cjs +1 -1
- package/dist/thresholdCheck.cjs.map +1 -1
- package/dist/thresholdCheck.d.ts +10 -8
- package/dist/thresholdCheck.js +1 -1
- package/dist/thresholdCheck.js.map +1 -1
- package/dist/thresholds.cjs +1 -1
- package/dist/thresholds.cjs.map +1 -1
- package/dist/thresholds.d.ts +5 -3
- package/dist/thresholds.js +1 -1
- package/dist/thresholds.js.map +1 -1
- package/dist/types.d.ts +29 -7
- package/dist/wasmBinding.cjs +1 -1
- package/dist/wasmBinding.js +1 -1
- package/native/Cargo.lock +16 -16
- package/native/Cargo.toml +8 -8
- package/native/code-gauge.wasm +0 -0
- package/native/src/complexity.rs +10 -1
- package/native/src/container.rs +158 -7
- package/native/src/dep_degree.rs +13 -14
- package/native/src/duplication.rs +213 -85
- package/native/src/functions.rs +112 -24
- package/native/src/languages.rs +5 -0
- package/native/src/lib.rs +1 -1
- package/native/src/measure.rs +9 -4
- package/native/src/near_miss.rs +303 -52
- package/native/src/types.rs +3 -0
- package/package.json +8 -8
package/native/Cargo.lock
CHANGED
|
@@ -484,9 +484,9 @@ checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
|
|
|
484
484
|
|
|
485
485
|
[[package]]
|
|
486
486
|
name = "willbooster-tree-sitter"
|
|
487
|
-
version = "1.
|
|
487
|
+
version = "1.4.3"
|
|
488
488
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
489
|
-
checksum = "
|
|
489
|
+
checksum = "334d00dd58bc471ce142f80bd05c8dde5d6a5961d23527aa4b3ad640793550ef"
|
|
490
490
|
dependencies = [
|
|
491
491
|
"cc",
|
|
492
492
|
"regex",
|
|
@@ -497,9 +497,9 @@ dependencies = [
|
|
|
497
497
|
|
|
498
498
|
[[package]]
|
|
499
499
|
name = "willbooster-tree-sitter-c"
|
|
500
|
-
version = "
|
|
500
|
+
version = "4.0.2"
|
|
501
501
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
502
|
-
checksum = "
|
|
502
|
+
checksum = "5eb6af34735987dd02518fced2bd8f6a0b81aff19220290020787c1e12c19e7f"
|
|
503
503
|
dependencies = [
|
|
504
504
|
"cc",
|
|
505
505
|
"tree-sitter-language",
|
|
@@ -507,9 +507,9 @@ dependencies = [
|
|
|
507
507
|
|
|
508
508
|
[[package]]
|
|
509
509
|
name = "willbooster-tree-sitter-c-sharp"
|
|
510
|
-
version = "
|
|
510
|
+
version = "3.0.1"
|
|
511
511
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
512
|
-
checksum = "
|
|
512
|
+
checksum = "8c5aa3c4b1bfcdad92d0703c77964c8b805800d3ee8087dddae1d336da96e495"
|
|
513
513
|
dependencies = [
|
|
514
514
|
"cc",
|
|
515
515
|
"tree-sitter-language",
|
|
@@ -517,9 +517,9 @@ dependencies = [
|
|
|
517
517
|
|
|
518
518
|
[[package]]
|
|
519
519
|
name = "willbooster-tree-sitter-cpp"
|
|
520
|
-
version = "
|
|
520
|
+
version = "2.0.2"
|
|
521
521
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
522
|
-
checksum = "
|
|
522
|
+
checksum = "fff5f9f592d51e52f1701438c8ee8b91376e15f42584e4e01f4ecb1120b1f354"
|
|
523
523
|
dependencies = [
|
|
524
524
|
"cc",
|
|
525
525
|
"tree-sitter-language",
|
|
@@ -527,9 +527,9 @@ dependencies = [
|
|
|
527
527
|
|
|
528
528
|
[[package]]
|
|
529
529
|
name = "willbooster-tree-sitter-javascript"
|
|
530
|
-
version = "
|
|
530
|
+
version = "4.0.2"
|
|
531
531
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
532
|
-
checksum = "
|
|
532
|
+
checksum = "60a035c947e2036a42ef8060872a213dc76489b3a82f2e4730b6ed54bd10a7bb"
|
|
533
533
|
dependencies = [
|
|
534
534
|
"cc",
|
|
535
535
|
"tree-sitter-language",
|
|
@@ -537,9 +537,9 @@ dependencies = [
|
|
|
537
537
|
|
|
538
538
|
[[package]]
|
|
539
539
|
name = "willbooster-tree-sitter-kotlin"
|
|
540
|
-
version = "
|
|
540
|
+
version = "4.0.2"
|
|
541
541
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
542
|
-
checksum = "
|
|
542
|
+
checksum = "58fb49a0177bad0032df8ff897e5c6b66f3ed044e3133a81ebc1583303f6c7aa"
|
|
543
543
|
dependencies = [
|
|
544
544
|
"cc",
|
|
545
545
|
"tree-sitter-language",
|
|
@@ -547,9 +547,9 @@ dependencies = [
|
|
|
547
547
|
|
|
548
548
|
[[package]]
|
|
549
549
|
name = "willbooster-tree-sitter-rust"
|
|
550
|
-
version = "
|
|
550
|
+
version = "2.0.2"
|
|
551
551
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
552
|
-
checksum = "
|
|
552
|
+
checksum = "740d80b0a761326575e52bf678749e1d08430e988b45d18f5db71bc20ad3a128"
|
|
553
553
|
dependencies = [
|
|
554
554
|
"cc",
|
|
555
555
|
"tree-sitter-language",
|
|
@@ -557,9 +557,9 @@ dependencies = [
|
|
|
557
557
|
|
|
558
558
|
[[package]]
|
|
559
559
|
name = "willbooster-tree-sitter-typescript"
|
|
560
|
-
version = "
|
|
560
|
+
version = "7.0.0"
|
|
561
561
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
562
|
-
checksum = "
|
|
562
|
+
checksum = "289945de1713b7f13625e2d9646e138f37be74d38d8f58de0cfe669d9fe9ba0c"
|
|
563
563
|
dependencies = [
|
|
564
564
|
"cc",
|
|
565
565
|
"tree-sitter-language",
|
package/native/Cargo.toml
CHANGED
|
@@ -20,18 +20,18 @@ rustc-hash = "2"
|
|
|
20
20
|
# `willbooster-*` crates are WillBooster's forks of the runtime and grammars. Every grammar crate
|
|
21
21
|
# depends only on `tree-sitter-language`, never on upstream's `tree-sitter` crate, whose
|
|
22
22
|
# `links = "tree-sitter"` would conflict with the forked runtime's.
|
|
23
|
-
tree-sitter = { package = "willbooster-tree-sitter", version = "1" }
|
|
24
|
-
tree-sitter-c = { package = "willbooster-tree-sitter-c", version = "=
|
|
25
|
-
tree-sitter-c-sharp = { package = "willbooster-tree-sitter-c-sharp", version = "=
|
|
26
|
-
tree-sitter-cpp = { package = "willbooster-tree-sitter-cpp", version = "=
|
|
23
|
+
tree-sitter = { package = "willbooster-tree-sitter", version = "1.4.3" }
|
|
24
|
+
tree-sitter-c = { package = "willbooster-tree-sitter-c", version = "=4.0.2" }
|
|
25
|
+
tree-sitter-c-sharp = { package = "willbooster-tree-sitter-c-sharp", version = "=3.0.1" }
|
|
26
|
+
tree-sitter-cpp = { package = "willbooster-tree-sitter-cpp", version = "=2.0.2" }
|
|
27
27
|
tree-sitter-go = "=0.25.0"
|
|
28
28
|
tree-sitter-java = "=0.23.5"
|
|
29
|
-
tree-sitter-javascript = { package = "willbooster-tree-sitter-javascript", version = "=
|
|
30
|
-
tree-sitter-kotlin = { package = "willbooster-tree-sitter-kotlin", version = "=
|
|
29
|
+
tree-sitter-javascript = { package = "willbooster-tree-sitter-javascript", version = "=4.0.2" }
|
|
30
|
+
tree-sitter-kotlin = { package = "willbooster-tree-sitter-kotlin", version = "=4.0.2" }
|
|
31
31
|
tree-sitter-python = "=0.25.0"
|
|
32
32
|
tree-sitter-ruby = "=0.23.1"
|
|
33
|
-
tree-sitter-rust = { package = "willbooster-tree-sitter-rust", version = "=
|
|
34
|
-
tree-sitter-typescript = { package = "willbooster-tree-sitter-typescript", version = "=
|
|
33
|
+
tree-sitter-rust = { package = "willbooster-tree-sitter-rust", version = "=2.0.2" }
|
|
34
|
+
tree-sitter-typescript = { package = "willbooster-tree-sitter-typescript", version = "=7.0.0" }
|
|
35
35
|
|
|
36
36
|
# The WebAssembly build (src/wasm.rs) exports a plain C ABI instead of N-API.
|
|
37
37
|
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
package/native/code-gauge.wasm
CHANGED
|
Binary file
|
package/native/src/complexity.rs
CHANGED
|
@@ -639,7 +639,8 @@ fn find_boolean_operator_text<'a>(binary_node: Node<'_>, code: &Source<'a>) -> O
|
|
|
639
639
|
}
|
|
640
640
|
|
|
641
641
|
/// Java `guard`, C# `when_clause`, Ruby `if_guard`, a Python `case` guard (an `if_clause` under a
|
|
642
|
-
/// `case_clause`),
|
|
642
|
+
/// `case_clause`), a Kotlin guard (the `guard` field of a `when_entry`), and Rust guards inside
|
|
643
|
+
/// `match_pattern`. A Python comprehension filter shares
|
|
643
644
|
/// the `if_clause` type but is a per-element predicate of one expression, not an extra execution
|
|
644
645
|
/// path: Sonar ports differ on it (complexipy charges it), and code-gauge does not, unlike
|
|
645
646
|
/// ternaries and conditions, which are charged. A C# exception filter (`catch (E e) when (...)`, a `catch_filter_clause`) is
|
|
@@ -656,6 +657,14 @@ fn is_pattern_guard(node: Node<'_>, parent: Option<Node<'_>>) -> bool {
|
|
|
656
657
|
if kind == "if_clause" {
|
|
657
658
|
return parent.is_some_and(|parent| parent.kind_name() == "case_clause");
|
|
658
659
|
}
|
|
660
|
+
if parent.is_some_and(|parent| {
|
|
661
|
+
parent.kind_name() == "when_entry"
|
|
662
|
+
&& parent
|
|
663
|
+
.child_by_field_name("guard")
|
|
664
|
+
.is_some_and(|guard| guard.id() == node.id())
|
|
665
|
+
}) {
|
|
666
|
+
return true;
|
|
667
|
+
}
|
|
659
668
|
kind == "match_pattern"
|
|
660
669
|
&& all_children(node)
|
|
661
670
|
.iter()
|
package/native/src/container.rs
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
use rustc_hash::FxHashSet;
|
|
2
2
|
use tree_sitter::Node;
|
|
3
3
|
|
|
4
|
-
use crate::functions::
|
|
4
|
+
use crate::functions::{
|
|
5
|
+
find_pair_key_name, find_parallel_assignment_target, find_single_assignment_target,
|
|
6
|
+
find_string_literal_content, find_value_binding, is_value_group, next_declarator,
|
|
7
|
+
unwrap_transparent_value_wrappers,
|
|
8
|
+
};
|
|
5
9
|
use crate::tree_index::NodeExt;
|
|
6
10
|
use crate::util::{named_children, node_text, Source};
|
|
7
11
|
|
|
@@ -88,20 +92,57 @@ fn explicit_ruby_owner(object: Option<Node<'_>>, code: &Source<'_>) -> Option<St
|
|
|
88
92
|
(text != "self").then(|| text.to_string())
|
|
89
93
|
}
|
|
90
94
|
|
|
91
|
-
/// The name of the declaration whose member a
|
|
92
|
-
///
|
|
93
|
-
/// declarator, the object of a Ruby singleton
|
|
95
|
+
/// The name of the declaration whose member a function is: the nearest class-like declaration
|
|
96
|
+
/// enclosing it or, for a function written as a value, the declaration binding it; the owner the
|
|
97
|
+
/// function names itself (a Go receiver, a C++ qualified declarator, the object of a Ruby singleton
|
|
98
|
+
/// method); or the path a JavaScript object literal holding it is bound to.
|
|
94
99
|
pub fn find_container_name(
|
|
95
100
|
node: Node<'_>,
|
|
96
101
|
function_nodes: &FxHashSet<&'static str>,
|
|
97
102
|
code: &Source<'_>,
|
|
98
103
|
inline_namespaces: &InlineNamespaces<'_>,
|
|
99
104
|
) -> Option<String> {
|
|
100
|
-
|
|
101
|
-
//
|
|
102
|
-
|
|
105
|
+
let is_declared = is_function_declaration(node);
|
|
106
|
+
// A function expression with a name of its own (`pick = function impl() {}`) is reported
|
|
107
|
+
// under that name, which is no member of what binds the expression.
|
|
108
|
+
if !is_declared && node.child_by_field_name("name").is_some() {
|
|
103
109
|
return None;
|
|
104
110
|
}
|
|
111
|
+
let (holder, bound) = find_value_binding(node, code)?;
|
|
112
|
+
let member = if holder.kind_name() == "pair" {
|
|
113
|
+
holder
|
|
114
|
+
} else {
|
|
115
|
+
bound
|
|
116
|
+
};
|
|
117
|
+
if let Some(object) = member
|
|
118
|
+
.parent_node()
|
|
119
|
+
.filter(|parent| parent.kind_name() == "object")
|
|
120
|
+
{
|
|
121
|
+
return object_owner(object, function_nodes, code, inline_namespaces);
|
|
122
|
+
}
|
|
123
|
+
// A function written as a value belongs to whatever it is passed or assigned to, which the
|
|
124
|
+
// syntax can follow only where a declaration binds it (a class field, a constant of a
|
|
125
|
+
// namespace); elsewhere it keeps the name of what it is bound to and has no owner.
|
|
126
|
+
if !is_declared {
|
|
127
|
+
// A parallel assignment binds the value to the target at its position.
|
|
128
|
+
if is_value_group(holder) {
|
|
129
|
+
let (assignment, target) = find_parallel_assignment_target(bound)?;
|
|
130
|
+
return assigns_attribute(assignment, target)
|
|
131
|
+
.then(|| enclosing_owner(assignment, function_nodes, code, inline_namespaces))
|
|
132
|
+
.flatten();
|
|
133
|
+
}
|
|
134
|
+
if !declares_binding(holder, bound) {
|
|
135
|
+
return None;
|
|
136
|
+
}
|
|
137
|
+
// A C++ variable defined outside its class (`int (*Rules::pick)(int) = [] {}`) spells
|
|
138
|
+
// its owner, as a function defined there does.
|
|
139
|
+
return match find_qualified_declarator(holder) {
|
|
140
|
+
Some(qualified) => {
|
|
141
|
+
cpp_spelled_owner(holder, qualified, function_nodes, code, inline_namespaces)
|
|
142
|
+
}
|
|
143
|
+
None => enclosing_owner(holder, function_nodes, code, inline_namespaces),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
105
146
|
if let Some(receiver) = node.child_by_field_name("receiver") {
|
|
106
147
|
return go_receiver_type(receiver, code);
|
|
107
148
|
}
|
|
@@ -113,6 +154,116 @@ pub fn find_container_name(
|
|
|
113
154
|
.or_else(|| enclosing_owner(node, function_nodes, code, inline_namespaces))
|
|
114
155
|
}
|
|
115
156
|
|
|
157
|
+
/// Whether `holder` declares the name its value `bound` is bound to, as a field, a property, or a
|
|
158
|
+
/// variable does.
|
|
159
|
+
fn declares_binding(holder: Node<'_>, bound: Node<'_>) -> bool {
|
|
160
|
+
let kind = holder.kind_name();
|
|
161
|
+
if kind == "assignment" {
|
|
162
|
+
return find_single_assignment_target(holder)
|
|
163
|
+
.is_some_and(|target| assigns_attribute(holder, target))
|
|
164
|
+
&& holder
|
|
165
|
+
.child_by_field_name("right")
|
|
166
|
+
.is_some_and(|value| value.id() == bound.id());
|
|
167
|
+
}
|
|
168
|
+
kind.ends_with("_declarator")
|
|
169
|
+
|| kind.ends_with("_declaration")
|
|
170
|
+
|| kind.ends_with("_definition")
|
|
171
|
+
|| matches!(kind, "const_item" | "static_item")
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/// Whether an assignment declares `target` as an attribute of the class around it: a Python class
|
|
175
|
+
/// does by assigning to a plain name and a Ruby one a constant, while a plain name assigned in a
|
|
176
|
+
/// Ruby class body is a local variable of that body.
|
|
177
|
+
fn assigns_attribute(assignment: Node<'_>, target: Node<'_>) -> bool {
|
|
178
|
+
match target.kind_name() {
|
|
179
|
+
// Only Python wraps an assignment in an `expression_statement`, a chain (`e = f = ...`)
|
|
180
|
+
// in that of its outermost assignment.
|
|
181
|
+
"identifier" => {
|
|
182
|
+
let mut outermost = assignment;
|
|
183
|
+
while let Some(outer) = outermost
|
|
184
|
+
.parent_node()
|
|
185
|
+
.filter(|outer| outer.kind_name() == "assignment")
|
|
186
|
+
{
|
|
187
|
+
outermost = outer;
|
|
188
|
+
}
|
|
189
|
+
outermost
|
|
190
|
+
.parent_node()
|
|
191
|
+
.is_some_and(|statement| statement.kind_name() == "expression_statement")
|
|
192
|
+
}
|
|
193
|
+
"constant" => true,
|
|
194
|
+
_ => false,
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/// The path a JavaScript object literal is bound to, which its members are reached through:
|
|
199
|
+
/// `api` for `const api = { ... }`, `Rules.handlers` for a field of a class, `api.admin` for the
|
|
200
|
+
/// value of a property, `module.exports` for an assignment. An object passed or returned is bound
|
|
201
|
+
/// to nothing the syntax names.
|
|
202
|
+
fn object_owner(
|
|
203
|
+
object: Node<'_>,
|
|
204
|
+
function_nodes: &FxHashSet<&'static str>,
|
|
205
|
+
code: &Source<'_>,
|
|
206
|
+
inline_namespaces: &InlineNamespaces<'_>,
|
|
207
|
+
) -> Option<String> {
|
|
208
|
+
let bound = unwrap_transparent_value_wrappers(object);
|
|
209
|
+
let holder = bound.parent_node()?;
|
|
210
|
+
let is_value = |field: &str| {
|
|
211
|
+
holder
|
|
212
|
+
.child_by_field_name(field)
|
|
213
|
+
.is_some_and(|value| value.id() == bound.id())
|
|
214
|
+
};
|
|
215
|
+
match holder.kind_name() {
|
|
216
|
+
"variable_declarator" | "field_definition" | "public_field_definition"
|
|
217
|
+
if is_value("value") =>
|
|
218
|
+
{
|
|
219
|
+
let name = holder
|
|
220
|
+
.child_by_field_name("name")
|
|
221
|
+
.or_else(|| holder.child_by_field_name("property"))?;
|
|
222
|
+
// A field may be named by a literal (`"handlers" = {}`); a computed name or a
|
|
223
|
+
// destructuring pattern names nothing.
|
|
224
|
+
let name = match name.kind_name() {
|
|
225
|
+
"string" => find_string_literal_content(name, code)?,
|
|
226
|
+
"number" => node_text(name, code).to_string(),
|
|
227
|
+
kind if kind.ends_with("identifier") => node_text(name, code).to_string(),
|
|
228
|
+
_ => return None,
|
|
229
|
+
};
|
|
230
|
+
Some(
|
|
231
|
+
match enclosing_owner(holder, function_nodes, code, inline_namespaces) {
|
|
232
|
+
Some(owner) => format!("{owner}.{name}"),
|
|
233
|
+
None => name,
|
|
234
|
+
},
|
|
235
|
+
)
|
|
236
|
+
}
|
|
237
|
+
"pair" if is_value("value") => {
|
|
238
|
+
let outer = holder
|
|
239
|
+
.parent_node()
|
|
240
|
+
.filter(|outer| outer.kind_name() == "object")?;
|
|
241
|
+
let owner = object_owner(outer, function_nodes, code, inline_namespaces)?;
|
|
242
|
+
Some(format!("{owner}.{}", find_pair_key_name(holder, code)?))
|
|
243
|
+
}
|
|
244
|
+
"assignment_expression" if is_value("right") => holder
|
|
245
|
+
.child_by_field_name("left")
|
|
246
|
+
.and_then(|target| member_path(target, code)),
|
|
247
|
+
_ => None,
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/// A name or a chain of property accesses on one (`exports`, `Rules.prototype`, `this.handlers`),
|
|
252
|
+
/// spelled without the comments and line breaks its source may hold.
|
|
253
|
+
fn member_path(node: Node<'_>, code: &Source<'_>) -> Option<String> {
|
|
254
|
+
match node.kind_name() {
|
|
255
|
+
"identifier" | "this" => Some(node_text(node, code).to_string()),
|
|
256
|
+
"member_expression" => {
|
|
257
|
+
let object = member_path(node.child_by_field_name("object")?, code)?;
|
|
258
|
+
let property = node
|
|
259
|
+
.child_by_field_name("property")
|
|
260
|
+
.filter(|property| property.kind_name() == "property_identifier")?;
|
|
261
|
+
Some(format!("{object}.{}", node_text(property, code)))
|
|
262
|
+
}
|
|
263
|
+
_ => None,
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
116
267
|
/// A Go method is declared outside its type and names it as its receiver, `(r *Rules)` or
|
|
117
268
|
/// `(r Rules[T])`.
|
|
118
269
|
fn go_receiver_type(receiver: Node<'_>, code: &Source<'_>) -> Option<String> {
|
package/native/src/dep_degree.rs
CHANGED
|
@@ -72,7 +72,6 @@ const DECLARATOR_WRAPPER_TYPES: &[&str] = &[
|
|
|
72
72
|
"parenthesized_declarator",
|
|
73
73
|
"function_declarator",
|
|
74
74
|
"attributed_declarator",
|
|
75
|
-
"pointer_type_declarator",
|
|
76
75
|
"qualified_identifier",
|
|
77
76
|
];
|
|
78
77
|
|
|
@@ -149,16 +148,10 @@ pub fn measure_dep_degree(
|
|
|
149
148
|
pairs
|
|
150
149
|
}
|
|
151
150
|
|
|
152
|
-
///
|
|
153
|
-
///
|
|
154
|
-
/// declarator wrappers; every other `type_identifier` names a type, not a variable. Kotlin spells
|
|
155
|
-
/// type names as identifiers: in a `user_type`, or as a type parameter's fieldless name (C# puts its
|
|
156
|
-
/// type parameter in a `name` field).
|
|
151
|
+
/// Kotlin spells type names as identifiers: in a `user_type`, or as a type parameter's fieldless
|
|
152
|
+
/// name (C# puts its type parameter in a `name` field).
|
|
157
153
|
fn is_variable_leaf(leaf: &DepDegreeLeaf<'_>) -> bool {
|
|
158
154
|
let node = leaf.node;
|
|
159
|
-
if node.kind_name() == "type_identifier" {
|
|
160
|
-
return is_member_pointer_name(node);
|
|
161
|
-
}
|
|
162
155
|
VARIABLE_NODE_TYPES.contains(&node.kind_name())
|
|
163
156
|
&& !node.parent_node().is_some_and(|parent| {
|
|
164
157
|
parent.kind_name() == "user_type"
|
|
@@ -166,15 +159,21 @@ fn is_variable_leaf(leaf: &DepDegreeLeaf<'_>) -> bool {
|
|
|
166
159
|
})
|
|
167
160
|
}
|
|
168
161
|
|
|
162
|
+
/// Whether the identifier is the name a C++ member pointer declares (`int C::* p`, also
|
|
163
|
+
/// `int C::* arr[1]`): its `pointer_declarator`, possibly around further declarator wrappers, is
|
|
164
|
+
/// the `name` of the `qualified_identifier` spelling the class.
|
|
169
165
|
fn is_member_pointer_name(node: Node<'_>) -> bool {
|
|
170
166
|
let mut current = node;
|
|
171
167
|
while let Some(parent) = current.parent_node() {
|
|
172
|
-
|
|
173
|
-
|
|
168
|
+
// The kind is tested before the field is looked up: this runs for every variable read,
|
|
169
|
+
// and scanning the children of a high-arity parent (a list of arguments) each time would
|
|
170
|
+
// cost the square of its length.
|
|
171
|
+
if parent.kind_name() == "qualified_identifier" {
|
|
172
|
+
return current.kind_name() == "pointer_declarator"
|
|
173
|
+
&& field_name_in_parent(current, parent) == Some("name");
|
|
174
174
|
}
|
|
175
175
|
// The climb follows the declared-name position only, exactly like unwrap_declarator_wrappers.
|
|
176
176
|
if !DECLARATOR_WRAPPER_TYPES.contains(&parent.kind_name())
|
|
177
|
-
|| parent.kind_name() == "qualified_identifier"
|
|
178
177
|
|| field_name_in_parent(current, parent).is_some_and(|field| field != "declarator")
|
|
179
178
|
{
|
|
180
179
|
return false;
|
|
@@ -338,7 +337,7 @@ fn is_definition_field(holder: Node<'_>, field_name: Option<&str>) -> bool {
|
|
|
338
337
|
|
|
339
338
|
/// Climbs from the identifier through the C/C++ declarator wrappers it is the declared name of
|
|
340
339
|
/// (`reference_declarator` and `parenthesized_declarator` expose no field, so their name is the
|
|
341
|
-
/// fieldless child; a member pointer's `
|
|
340
|
+
/// fieldless child; a member pointer's `pointer_declarator` is the `name` of a
|
|
342
341
|
/// `qualified_identifier`; an `array_declarator` size or a nested parameter has another field and
|
|
343
342
|
/// stops the climb) to the outermost wrapper and its field in the declaration.
|
|
344
343
|
fn unwrap_declarator_wrappers<'t>(leaf: &DepDegreeLeaf<'t>) -> (Node<'t>, Option<&'t str>) {
|
|
@@ -400,7 +399,7 @@ fn is_parameter_definition(leaf: &DepDegreeLeaf<'_>) -> bool {
|
|
|
400
399
|
// O(1).
|
|
401
400
|
let continues_member_pointer = declares_member_pointer
|
|
402
401
|
&& parent.kind_name() == "qualified_identifier"
|
|
403
|
-
&& (current.kind_name() == "
|
|
402
|
+
&& (current.kind_name() == "pointer_declarator" || in_member_pointer)
|
|
404
403
|
&& field_name_in_parent(current, parent) == Some("name");
|
|
405
404
|
in_member_pointer = continues_member_pointer;
|
|
406
405
|
if depth >= 1
|