code-gauge 4.8.0 → 5.1.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 +34 -9
- package/THIRD-PARTY-NOTICES.txt +1152 -798
- package/dist/cli.cjs +3 -3
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +3 -3
- package/dist/cli.js.map +1 -1
- package/dist/cliConfig.cjs +1 -1
- package/dist/cliConfig.cjs.map +1 -1
- package/dist/cliConfig.d.ts +13 -3
- package/dist/cliConfig.js +1 -1
- package/dist/cliConfig.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/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/exclusion.cjs +2 -0
- package/dist/exclusion.cjs.map +1 -0
- package/dist/exclusion.d.ts +30 -0
- package/dist/exclusion.js +2 -0
- package/dist/exclusion.js.map +1 -0
- package/dist/git.cjs +1 -1
- package/dist/git.cjs.map +1 -1
- package/dist/git.d.ts +16 -0
- package/dist/git.js +1 -1
- package/dist/git.js.map +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -1
- package/dist/nativeMetrics.cjs +3 -3
- package/dist/nativeMetrics.cjs.map +1 -1
- package/dist/nativeMetrics.js +3 -3
- package/dist/nativeMetrics.js.map +1 -1
- package/dist/newDuplication.cjs +2 -0
- package/dist/newDuplication.cjs.map +1 -0
- package/dist/newDuplication.d.ts +40 -0
- package/dist/newDuplication.js +2 -0
- package/dist/newDuplication.js.map +1 -0
- package/dist/regressionGate.cjs +1 -1
- package/dist/regressionGate.cjs.map +1 -1
- package/dist/regressionGate.d.ts +8 -6
- 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 +12 -4
- package/dist/scan.js +1 -1
- package/dist/scan.js.map +1 -1
- package/dist/worker.cjs +1 -1
- package/dist/worker.cjs.map +1 -1
- package/dist/worker.js +1 -1
- package/dist/worker.js.map +1 -1
- package/native/Cargo.lock +73 -73
- package/native/Cargo.toml +16 -21
- package/native/code-gauge.wasm +0 -0
- package/native/src/dep_degree.rs +6 -6
- package/native/src/duplication.rs +143 -14
- package/native/src/functions.rs +10 -7
- package/native/src/languages.rs +14 -13
- package/native/src/measure.rs +16 -8
- package/native/src/tree_index.rs +4 -4
- package/native/src/util.rs +9 -2
- package/package.json +9 -8
package/native/Cargo.lock
CHANGED
|
@@ -45,18 +45,18 @@ dependencies = [
|
|
|
45
45
|
"rustc-hash",
|
|
46
46
|
"serde",
|
|
47
47
|
"serde_json",
|
|
48
|
-
"tree-sitter",
|
|
49
|
-
"tree-sitter-c",
|
|
50
|
-
"tree-sitter-c-sharp",
|
|
51
|
-
"tree-sitter-cpp",
|
|
52
48
|
"tree-sitter-go",
|
|
53
49
|
"tree-sitter-java",
|
|
54
|
-
"tree-sitter-javascript",
|
|
55
50
|
"tree-sitter-python",
|
|
56
51
|
"tree-sitter-ruby",
|
|
57
|
-
"tree-sitter
|
|
58
|
-
"tree-sitter-
|
|
52
|
+
"willbooster-tree-sitter",
|
|
53
|
+
"willbooster-tree-sitter-c",
|
|
54
|
+
"willbooster-tree-sitter-c-sharp",
|
|
55
|
+
"willbooster-tree-sitter-cpp",
|
|
56
|
+
"willbooster-tree-sitter-javascript",
|
|
59
57
|
"willbooster-tree-sitter-kotlin",
|
|
58
|
+
"willbooster-tree-sitter-rust",
|
|
59
|
+
"willbooster-tree-sitter-typescript",
|
|
60
60
|
]
|
|
61
61
|
|
|
62
62
|
[[package]]
|
|
@@ -425,141 +425,141 @@ dependencies = [
|
|
|
425
425
|
]
|
|
426
426
|
|
|
427
427
|
[[package]]
|
|
428
|
-
name = "tree-sitter"
|
|
429
|
-
version = "0.25.
|
|
428
|
+
name = "tree-sitter-go"
|
|
429
|
+
version = "0.25.0"
|
|
430
430
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
431
|
-
checksum = "
|
|
431
|
+
checksum = "c8560a4d2f835cc0d4d2c2e03cbd0dde2f6114b43bc491164238d333e28b16ea"
|
|
432
432
|
dependencies = [
|
|
433
433
|
"cc",
|
|
434
|
-
"regex",
|
|
435
|
-
"regex-syntax",
|
|
436
|
-
"serde_json",
|
|
437
|
-
"streaming-iterator",
|
|
438
434
|
"tree-sitter-language",
|
|
439
435
|
]
|
|
440
436
|
|
|
441
437
|
[[package]]
|
|
442
|
-
name = "tree-sitter-
|
|
443
|
-
version = "0.
|
|
438
|
+
name = "tree-sitter-java"
|
|
439
|
+
version = "0.23.5"
|
|
444
440
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
445
|
-
checksum = "
|
|
441
|
+
checksum = "0aa6cbcdc8c679b214e616fd3300da67da0e492e066df01bcf5a5921a71e90d6"
|
|
446
442
|
dependencies = [
|
|
447
443
|
"cc",
|
|
448
|
-
"tree-sitter",
|
|
444
|
+
"tree-sitter-language",
|
|
449
445
|
]
|
|
450
446
|
|
|
451
447
|
[[package]]
|
|
452
|
-
name = "tree-sitter-
|
|
453
|
-
version = "0.
|
|
448
|
+
name = "tree-sitter-language"
|
|
449
|
+
version = "0.1.8"
|
|
454
450
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
455
|
-
checksum = "
|
|
456
|
-
dependencies = [
|
|
457
|
-
"cc",
|
|
458
|
-
"tree-sitter",
|
|
459
|
-
]
|
|
451
|
+
checksum = "ca0d1bf6fdd806e43ae5198f82f527056d359def39e54e67a0f478ac09dac081"
|
|
460
452
|
|
|
461
453
|
[[package]]
|
|
462
|
-
name = "tree-sitter-
|
|
463
|
-
version = "0.
|
|
454
|
+
name = "tree-sitter-python"
|
|
455
|
+
version = "0.25.0"
|
|
464
456
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
465
|
-
checksum = "
|
|
457
|
+
checksum = "6bf85fd39652e740bf60f46f4cda9492c3a9ad75880575bf14960f775cb74a1c"
|
|
466
458
|
dependencies = [
|
|
467
459
|
"cc",
|
|
468
|
-
"tree-sitter",
|
|
460
|
+
"tree-sitter-language",
|
|
469
461
|
]
|
|
470
462
|
|
|
471
463
|
[[package]]
|
|
472
|
-
name = "tree-sitter-
|
|
473
|
-
version = "0.
|
|
464
|
+
name = "tree-sitter-ruby"
|
|
465
|
+
version = "0.23.1"
|
|
474
466
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
475
|
-
checksum = "
|
|
467
|
+
checksum = "be0484ea4ef6bb9c575b4fdabde7e31340a8d2dbc7d52b321ac83da703249f95"
|
|
476
468
|
dependencies = [
|
|
477
469
|
"cc",
|
|
478
|
-
"tree-sitter",
|
|
470
|
+
"tree-sitter-language",
|
|
479
471
|
]
|
|
480
472
|
|
|
481
473
|
[[package]]
|
|
482
|
-
name = "
|
|
483
|
-
version = "0.
|
|
474
|
+
name = "unicode-ident"
|
|
475
|
+
version = "1.0.24"
|
|
484
476
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
485
|
-
checksum = "
|
|
486
|
-
dependencies = [
|
|
487
|
-
"cc",
|
|
488
|
-
"tree-sitter",
|
|
489
|
-
]
|
|
477
|
+
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
|
|
490
478
|
|
|
491
479
|
[[package]]
|
|
492
|
-
name = "
|
|
493
|
-
version = "
|
|
480
|
+
name = "unicode-segmentation"
|
|
481
|
+
version = "1.13.3"
|
|
494
482
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
495
|
-
checksum = "
|
|
483
|
+
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
|
|
484
|
+
|
|
485
|
+
[[package]]
|
|
486
|
+
name = "willbooster-tree-sitter"
|
|
487
|
+
version = "1.0.5"
|
|
488
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
489
|
+
checksum = "7c6ca3007235ade028b793d6090a5ea16618ea3ab9b53427d1f782b15bf64064"
|
|
496
490
|
dependencies = [
|
|
497
491
|
"cc",
|
|
492
|
+
"regex",
|
|
493
|
+
"serde_json",
|
|
494
|
+
"streaming-iterator",
|
|
498
495
|
"tree-sitter-language",
|
|
499
496
|
]
|
|
500
497
|
|
|
501
498
|
[[package]]
|
|
502
|
-
name = "tree-sitter-
|
|
503
|
-
version = "
|
|
499
|
+
name = "willbooster-tree-sitter-c"
|
|
500
|
+
version = "1.1.0"
|
|
504
501
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
505
|
-
checksum = "
|
|
502
|
+
checksum = "5cee6f1a4e0f64f337d28aeaed73879d7e8a87d77c287afb989ce29adea54700"
|
|
503
|
+
dependencies = [
|
|
504
|
+
"cc",
|
|
505
|
+
"tree-sitter-language",
|
|
506
|
+
]
|
|
506
507
|
|
|
507
508
|
[[package]]
|
|
508
|
-
name = "tree-sitter-
|
|
509
|
-
version = "0.
|
|
509
|
+
name = "willbooster-tree-sitter-c-sharp"
|
|
510
|
+
version = "1.0.3"
|
|
510
511
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
511
|
-
checksum = "
|
|
512
|
+
checksum = "99b224dcd59cda9b24b588557cca3827abaccaf10da72d125c31e7cdb2e6f7fb"
|
|
512
513
|
dependencies = [
|
|
513
514
|
"cc",
|
|
514
|
-
"tree-sitter",
|
|
515
|
+
"tree-sitter-language",
|
|
515
516
|
]
|
|
516
517
|
|
|
517
518
|
[[package]]
|
|
518
|
-
name = "tree-sitter-
|
|
519
|
-
version = "
|
|
519
|
+
name = "willbooster-tree-sitter-cpp"
|
|
520
|
+
version = "1.1.1"
|
|
520
521
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
521
|
-
checksum = "
|
|
522
|
+
checksum = "72b92a9fcb0dc27bd4af18e06fe6c085dd14d91f009f60ecd92c0308b9cbd068"
|
|
522
523
|
dependencies = [
|
|
523
524
|
"cc",
|
|
524
|
-
"tree-sitter",
|
|
525
|
+
"tree-sitter-language",
|
|
525
526
|
]
|
|
526
527
|
|
|
527
528
|
[[package]]
|
|
528
|
-
name = "tree-sitter-
|
|
529
|
-
version = "0.
|
|
529
|
+
name = "willbooster-tree-sitter-javascript"
|
|
530
|
+
version = "1.0.3"
|
|
530
531
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
531
|
-
checksum = "
|
|
532
|
+
checksum = "e6c40d41b303b525194daa503a71ec94d8269fe8d1648b2d9138fe902623beff"
|
|
532
533
|
dependencies = [
|
|
533
534
|
"cc",
|
|
534
|
-
"tree-sitter",
|
|
535
|
+
"tree-sitter-language",
|
|
535
536
|
]
|
|
536
537
|
|
|
537
538
|
[[package]]
|
|
538
|
-
name = "tree-sitter-
|
|
539
|
-
version = "
|
|
539
|
+
name = "willbooster-tree-sitter-kotlin"
|
|
540
|
+
version = "1.4.0"
|
|
540
541
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
541
|
-
checksum = "
|
|
542
|
+
checksum = "529ae914939b559754e6dd8b34d2705f04d6dcb73f09cb52f024b49eda9b5b88"
|
|
542
543
|
dependencies = [
|
|
543
544
|
"cc",
|
|
544
|
-
"tree-sitter",
|
|
545
|
+
"tree-sitter-language",
|
|
545
546
|
]
|
|
546
547
|
|
|
547
548
|
[[package]]
|
|
548
|
-
name = "
|
|
549
|
-
version = "1.
|
|
549
|
+
name = "willbooster-tree-sitter-rust"
|
|
550
|
+
version = "1.1.1"
|
|
550
551
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
551
|
-
checksum = "
|
|
552
|
+
checksum = "324c9f18cfc29e4da7335ad8cb76beb6ac1ae91a8037a44724bc55fce04f26f7"
|
|
553
|
+
dependencies = [
|
|
554
|
+
"cc",
|
|
555
|
+
"tree-sitter-language",
|
|
556
|
+
]
|
|
552
557
|
|
|
553
558
|
[[package]]
|
|
554
|
-
name = "
|
|
555
|
-
version = "1.
|
|
559
|
+
name = "willbooster-tree-sitter-typescript"
|
|
560
|
+
version = "1.1.1"
|
|
556
561
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
557
|
-
checksum = "
|
|
558
|
-
|
|
559
|
-
[[package]]
|
|
560
|
-
name = "willbooster-tree-sitter-kotlin"
|
|
561
|
-
version = "0.0.0"
|
|
562
|
-
source = "git+https://github.com/WillBooster/tree-sitter-kotlin?tag=v1.1.0#a05912e6f07e2cedd44bfb117940361089d4c3e3"
|
|
562
|
+
checksum = "f8c51b6c9e10d6ae34c2f84c4d44fff79a0210890ede63fda558a6a0f042eb28"
|
|
563
563
|
dependencies = [
|
|
564
564
|
"cc",
|
|
565
565
|
"tree-sitter-language",
|
package/native/Cargo.toml
CHANGED
|
@@ -16,27 +16,22 @@ regex = "1"
|
|
|
16
16
|
rustc-hash = "2"
|
|
17
17
|
|
|
18
18
|
# Grammar crates are pinned exactly: every metric (and the golden-based test suite) is calibrated
|
|
19
|
-
# against these grammar versions, so upgrades are deliberate, test-verified changes. The
|
|
20
|
-
#
|
|
21
|
-
#
|
|
22
|
-
# `
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
tree-sitter = "=0.
|
|
26
|
-
tree-sitter-
|
|
27
|
-
tree-sitter-
|
|
28
|
-
tree-sitter-
|
|
29
|
-
tree-sitter-
|
|
30
|
-
tree-sitter-
|
|
31
|
-
tree-sitter-
|
|
32
|
-
tree-sitter-
|
|
33
|
-
tree-sitter-
|
|
34
|
-
tree-sitter-
|
|
35
|
-
# 0.21.0 breaks MSVC (its build script passes the GCC-only -Wno-unused-parameter
|
|
36
|
-
# unconditionally; cl.exe fails with D8021); 0.21.2 gates the flag and the golden-based test
|
|
37
|
-
# suite verifies its grammar changes leave every measured metric unchanged.
|
|
38
|
-
tree-sitter-rust = "=0.21.2"
|
|
39
|
-
tree-sitter-typescript = "=0.21.2"
|
|
19
|
+
# against these grammar versions, so upgrades are deliberate, test-verified changes. The
|
|
20
|
+
# `willbooster-*` crates are WillBooster's forks of the runtime and grammars. Every grammar crate
|
|
21
|
+
# depends only on `tree-sitter-language`, never on upstream's `tree-sitter` crate, whose
|
|
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 = "=1.1.0" }
|
|
25
|
+
tree-sitter-c-sharp = { package = "willbooster-tree-sitter-c-sharp", version = "=1.0.3" }
|
|
26
|
+
tree-sitter-cpp = { package = "willbooster-tree-sitter-cpp", version = "=1.1.1" }
|
|
27
|
+
tree-sitter-go = "=0.25.0"
|
|
28
|
+
tree-sitter-java = "=0.23.5"
|
|
29
|
+
tree-sitter-javascript = { package = "willbooster-tree-sitter-javascript", version = "=1.0.3" }
|
|
30
|
+
tree-sitter-kotlin = { package = "willbooster-tree-sitter-kotlin", version = "=1.4.0" }
|
|
31
|
+
tree-sitter-python = "=0.25.0"
|
|
32
|
+
tree-sitter-ruby = "=0.23.1"
|
|
33
|
+
tree-sitter-rust = { package = "willbooster-tree-sitter-rust", version = "=1.1.1" }
|
|
34
|
+
tree-sitter-typescript = { package = "willbooster-tree-sitter-typescript", version = "=1.1.1" }
|
|
40
35
|
|
|
41
36
|
# The WebAssembly build (src/wasm.rs) exports a plain C ABI instead of N-API.
|
|
42
37
|
[target.'cfg(not(target_family = "wasm"))'.dependencies]
|
package/native/code-gauge.wasm
CHANGED
|
Binary file
|
package/native/src/dep_degree.rs
CHANGED
|
@@ -93,7 +93,7 @@ const NON_BINDING_PARAMETER_FIELDS: &[&str] = &["type", "value"];
|
|
|
93
93
|
/// One leaf of the def-use walk: its field in the parent and its function-scope chain.
|
|
94
94
|
struct DepDegreeLeaf<'t> {
|
|
95
95
|
node: Node<'t>,
|
|
96
|
-
field_name: Option<&'
|
|
96
|
+
field_name: Option<&'t str>,
|
|
97
97
|
/// Chain of nested-function scope ids below the measured function: "" for its own body,
|
|
98
98
|
/// then "/0", "/0/1", ...
|
|
99
99
|
scope: String,
|
|
@@ -221,7 +221,7 @@ fn implicit_accessor_definitions<'a>(function_node: Node<'_>, code: &Source<'a>)
|
|
|
221
221
|
#[allow(clippy::too_many_arguments)]
|
|
222
222
|
fn collect_dep_degree_leaves<'t>(
|
|
223
223
|
node: Node<'t>,
|
|
224
|
-
field_name: Option<&'
|
|
224
|
+
field_name: Option<&'t str>,
|
|
225
225
|
scope: &str,
|
|
226
226
|
next_scope_id: &mut usize,
|
|
227
227
|
function_nodes: &FxHashSet<&'static str>,
|
|
@@ -234,7 +234,7 @@ fn collect_dep_degree_leaves<'t>(
|
|
|
234
234
|
) {
|
|
235
235
|
return;
|
|
236
236
|
}
|
|
237
|
-
if node
|
|
237
|
+
if crate::util::is_token(node) {
|
|
238
238
|
leaves.push(DepDegreeLeaf {
|
|
239
239
|
node,
|
|
240
240
|
field_name,
|
|
@@ -341,7 +341,7 @@ fn is_definition_field(holder: Node<'_>, field_name: Option<&str>) -> bool {
|
|
|
341
341
|
/// fieldless child; a member pointer's `pointer_type_declarator` is the `name` of a
|
|
342
342
|
/// `qualified_identifier`; an `array_declarator` size or a nested parameter has another field and
|
|
343
343
|
/// stops the climb) to the outermost wrapper and its field in the declaration.
|
|
344
|
-
fn unwrap_declarator_wrappers<'t>(leaf: &DepDegreeLeaf<'t>) -> (Node<'t>, Option<&'
|
|
344
|
+
fn unwrap_declarator_wrappers<'t>(leaf: &DepDegreeLeaf<'t>) -> (Node<'t>, Option<&'t str>) {
|
|
345
345
|
let mut current = leaf.node;
|
|
346
346
|
let mut field_name = leaf.field_name;
|
|
347
347
|
while let Some(parent) = current
|
|
@@ -439,11 +439,11 @@ fn is_kotlin_parameter(node: Node<'_>) -> bool {
|
|
|
439
439
|
}
|
|
440
440
|
|
|
441
441
|
/// Only called with small-arity parents (declarator wrappers, definition-list holders).
|
|
442
|
-
fn field_name_in_parent(node: Node<'_>, parent: Node<'
|
|
442
|
+
fn field_name_in_parent<'t>(node: Node<'_>, parent: Node<'t>) -> Option<&'t str> {
|
|
443
443
|
for index in 0..parent.child_count() {
|
|
444
444
|
if let Some(child) = parent.child(index) {
|
|
445
445
|
if child.id() == node.id() {
|
|
446
|
-
return parent.field_name_for_child(index
|
|
446
|
+
return parent.field_name_for_child(index);
|
|
447
447
|
}
|
|
448
448
|
}
|
|
449
449
|
}
|
|
@@ -10,7 +10,7 @@ use crate::types::{
|
|
|
10
10
|
CrossFileCandidate, CrossFileToken, CrossFileTokenRange, DuplicateBlockOccurrence,
|
|
11
11
|
DuplicationMetrics,
|
|
12
12
|
};
|
|
13
|
-
use crate::util::{all_children, named_children, node_text, to_int32, Source};
|
|
13
|
+
use crate::util::{all_children, is_token, named_children, node_text, to_int32, Source};
|
|
14
14
|
|
|
15
15
|
/// Block-like nodes considered as whole-subtree duplicate candidates.
|
|
16
16
|
const DUPLICATE_BLOCK_TYPES: &[&str] = &[
|
|
@@ -103,6 +103,7 @@ const STATEMENT_CONTAINER_TYPES: &[&str] = &[
|
|
|
103
103
|
"default_case",
|
|
104
104
|
"compilation_unit",
|
|
105
105
|
"switch_section",
|
|
106
|
+
"extension_body",
|
|
106
107
|
"enum_class_body",
|
|
107
108
|
"function_body",
|
|
108
109
|
"lambda_literal",
|
|
@@ -201,6 +202,26 @@ const LITERAL_KIND_BY_TYPE: &[(&str, &str)] = &[
|
|
|
201
202
|
|
|
202
203
|
const COMMENT_TYPES: &[&str] = &["comment", "line_comment", "block_comment"];
|
|
203
204
|
|
|
205
|
+
/// Declaration kinds that only name dependencies (imports, package clauses, includes).
|
|
206
|
+
const DEPENDENCY_DECLARATION_TYPES: &[&str] = &[
|
|
207
|
+
"import_statement",
|
|
208
|
+
"import_from_statement",
|
|
209
|
+
"future_import_statement",
|
|
210
|
+
"import_alias",
|
|
211
|
+
"import_declaration",
|
|
212
|
+
"package_clause",
|
|
213
|
+
"package_declaration",
|
|
214
|
+
"package_header",
|
|
215
|
+
"use_declaration",
|
|
216
|
+
"extern_crate_declaration",
|
|
217
|
+
"using_directive",
|
|
218
|
+
"extern_alias_directive",
|
|
219
|
+
"preproc_include",
|
|
220
|
+
];
|
|
221
|
+
|
|
222
|
+
/// Ruby methods that load another file, called without a receiver.
|
|
223
|
+
const RUBY_REQUIRE_METHODS: &[&str] = &["require", "require_relative"];
|
|
224
|
+
|
|
204
225
|
/// Children of a string node that carry only literal content; anything else is interpolation.
|
|
205
226
|
const STRING_FRAGMENT_TYPES: &[&str] = &[
|
|
206
227
|
"string_fragment",
|
|
@@ -209,6 +230,8 @@ const STRING_FRAGMENT_TYPES: &[&str] = &[
|
|
|
209
230
|
"string_literal_content",
|
|
210
231
|
"character_literal_content",
|
|
211
232
|
"raw_string_content",
|
|
233
|
+
"interpreted_string_literal_content",
|
|
234
|
+
"raw_string_literal_content",
|
|
212
235
|
"raw_string_start",
|
|
213
236
|
"raw_string_end",
|
|
214
237
|
"escape_sequence",
|
|
@@ -272,6 +295,8 @@ const STRING_CONTENT_FRAGMENT_TYPES: &[&str] = &[
|
|
|
272
295
|
"string_literal_content",
|
|
273
296
|
"character_literal_content",
|
|
274
297
|
"raw_string_content",
|
|
298
|
+
"interpreted_string_literal_content",
|
|
299
|
+
"raw_string_literal_content",
|
|
275
300
|
"escape_sequence",
|
|
276
301
|
"heredoc_content",
|
|
277
302
|
];
|
|
@@ -528,20 +553,25 @@ fn collect_tokens<'a>(
|
|
|
528
553
|
block_ranges: &mut Vec<TokenRange>,
|
|
529
554
|
container_statement_ranges: &mut Vec<Vec<TokenRange>>,
|
|
530
555
|
) {
|
|
556
|
+
/// `statement_list_statements` receives the statements of a Go `statement_list`, which wraps
|
|
557
|
+
/// the statements of blocks and case clauses and whose statements belong to the run of the
|
|
558
|
+
/// enclosing container.
|
|
531
559
|
fn visit<'a>(
|
|
532
560
|
node: Node<'_>,
|
|
533
561
|
code: &Source<'a>,
|
|
534
562
|
tokens: &mut Vec<Token<'a>>,
|
|
535
563
|
block_ranges: &mut Vec<TokenRange>,
|
|
536
564
|
container_statement_ranges: &mut Vec<Vec<TokenRange>>,
|
|
565
|
+
statement_list_statements: &mut Vec<TokenRange>,
|
|
537
566
|
) -> TokenRange {
|
|
538
567
|
let start_token_index = tokens.len();
|
|
539
|
-
let
|
|
568
|
+
let is_token = is_token(node);
|
|
569
|
+
let atomic_kind = if is_token {
|
|
540
570
|
None
|
|
541
571
|
} else {
|
|
542
572
|
atomic_literal_kind(node)
|
|
543
573
|
};
|
|
544
|
-
if
|
|
574
|
+
if is_token {
|
|
545
575
|
append_leaf_token(node, code, tokens);
|
|
546
576
|
} else if let Some(atomic_kind) = atomic_kind {
|
|
547
577
|
// Interpolation-free strings collapse to their kind tag so copies differing only in
|
|
@@ -553,30 +583,39 @@ fn collect_tokens<'a>(
|
|
|
553
583
|
node.start_position().row,
|
|
554
584
|
node.end_position().row,
|
|
555
585
|
));
|
|
556
|
-
} else if !
|
|
586
|
+
} else if !is_tokenless(node, code) {
|
|
557
587
|
let mut statement_ranges: Vec<TokenRange> = Vec::new();
|
|
558
|
-
let
|
|
588
|
+
let is_statement_list = node.kind_name() == "statement_list";
|
|
589
|
+
let is_container = is_statement_list || is_statement_container(node);
|
|
559
590
|
for child in all_children(node) {
|
|
591
|
+
let mut child_statements = Vec::new();
|
|
560
592
|
let child_range = visit(
|
|
561
593
|
child,
|
|
562
594
|
code,
|
|
563
595
|
tokens,
|
|
564
596
|
block_ranges,
|
|
565
597
|
container_statement_ranges,
|
|
598
|
+
&mut child_statements,
|
|
566
599
|
);
|
|
567
600
|
// A Kotlin lambda's parameters sit beside its statements.
|
|
568
601
|
if is_container
|
|
569
602
|
&& child.is_named()
|
|
570
|
-
&& !
|
|
603
|
+
&& !is_tokenless(child, code)
|
|
571
604
|
&& child.kind_name() != "lambda_parameters"
|
|
572
605
|
{
|
|
573
|
-
|
|
606
|
+
if child.kind_name() == "statement_list" {
|
|
607
|
+
statement_ranges.append(&mut child_statements);
|
|
608
|
+
} else {
|
|
609
|
+
statement_ranges.push(child_range);
|
|
610
|
+
}
|
|
574
611
|
}
|
|
575
612
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
613
|
+
if is_statement_list {
|
|
614
|
+
*statement_list_statements = statement_ranges;
|
|
615
|
+
} else if is_container && !statement_ranges.is_empty() {
|
|
616
|
+
// Single-statement containers are recorded too: window enumeration needs two
|
|
617
|
+
// statements and yields nothing for them, but cross-file matching catalogues each
|
|
618
|
+
// container's full run.
|
|
580
619
|
container_statement_ranges.push(statement_ranges);
|
|
581
620
|
}
|
|
582
621
|
}
|
|
@@ -595,7 +634,97 @@ fn collect_tokens<'a>(
|
|
|
595
634
|
range
|
|
596
635
|
}
|
|
597
636
|
|
|
598
|
-
visit(
|
|
637
|
+
visit(
|
|
638
|
+
root,
|
|
639
|
+
code,
|
|
640
|
+
tokens,
|
|
641
|
+
block_ranges,
|
|
642
|
+
container_statement_ranges,
|
|
643
|
+
&mut Vec::new(),
|
|
644
|
+
);
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/// Comments and dependency declarations contribute no tokens, so no clone consists of them; the
|
|
648
|
+
/// tokens around them stay adjacent, so a clone may still extend across one.
|
|
649
|
+
fn is_tokenless(node: Node<'_>, code: &Source<'_>) -> bool {
|
|
650
|
+
COMMENT_TYPES.contains(&node.kind_name()) || is_dependency_declaration(node, code)
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/// Every module must spell out its own dependencies, so a clone made of them has no refactoring
|
|
654
|
+
/// that could remove it; they are left out of clone detection entirely.
|
|
655
|
+
fn is_dependency_declaration(node: Node<'_>, code: &Source<'_>) -> bool {
|
|
656
|
+
if !node.is_named() {
|
|
657
|
+
return false;
|
|
658
|
+
}
|
|
659
|
+
let kind = node.kind_name();
|
|
660
|
+
if DEPENDENCY_DECLARATION_TYPES.contains(&kind) {
|
|
661
|
+
return true;
|
|
662
|
+
}
|
|
663
|
+
match (crate::tree_index::language_name(), kind) {
|
|
664
|
+
// JavaScript's dynamic `import(...)` is a named `import` node too.
|
|
665
|
+
("kotlin", "import") => true,
|
|
666
|
+
("cpp", "using_declaration") => true,
|
|
667
|
+
// `mod name;` loads a file; a `mod name { ... }` with a body is code.
|
|
668
|
+
("rust", "mod_item") => node.child_by_field_name("body").is_none(),
|
|
669
|
+
// Re-exports (`export { a } from './a'`); other exports wrap declarations.
|
|
670
|
+
("javascript" | "jsx" | "typescript" | "tsx", "export_statement") => {
|
|
671
|
+
node.child_by_field_name("source").is_some()
|
|
672
|
+
}
|
|
673
|
+
(
|
|
674
|
+
"javascript" | "jsx" | "typescript" | "tsx",
|
|
675
|
+
"lexical_declaration" | "variable_declaration",
|
|
676
|
+
) => is_require_declaration(node, code),
|
|
677
|
+
// `require('a');` and CommonJS re-exports such as `module.exports = require('a');`.
|
|
678
|
+
("javascript" | "jsx" | "typescript" | "tsx", "expression_statement") => {
|
|
679
|
+
named_children(node).first().is_some_and(|expression| {
|
|
680
|
+
if expression.kind_name() == "assignment_expression" {
|
|
681
|
+
expression
|
|
682
|
+
.child_by_field_name("right")
|
|
683
|
+
.is_some_and(|right| is_require_value(right, code))
|
|
684
|
+
} else {
|
|
685
|
+
is_require_value(*expression, code)
|
|
686
|
+
}
|
|
687
|
+
})
|
|
688
|
+
}
|
|
689
|
+
("ruby", "call") => {
|
|
690
|
+
node.child_by_field_name("receiver").is_none()
|
|
691
|
+
&& node
|
|
692
|
+
.child_by_field_name("method")
|
|
693
|
+
.is_some_and(|method| RUBY_REQUIRE_METHODS.contains(&node_text(method, code)))
|
|
694
|
+
}
|
|
695
|
+
_ => false,
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/// `const a = require('a')` (including destructuring and `require('a').b`) in every declarator.
|
|
700
|
+
fn is_require_declaration(node: Node<'_>, code: &Source<'_>) -> bool {
|
|
701
|
+
let declarators: Vec<Node<'_>> = named_children(node)
|
|
702
|
+
.into_iter()
|
|
703
|
+
.filter(|child| child.kind_name() == "variable_declarator")
|
|
704
|
+
.collect();
|
|
705
|
+
!declarators.is_empty()
|
|
706
|
+
&& declarators.iter().all(|declarator| {
|
|
707
|
+
declarator
|
|
708
|
+
.child_by_field_name("value")
|
|
709
|
+
.is_some_and(|value| is_require_value(value, code))
|
|
710
|
+
})
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/// `require('a')` or a property read from it (`require('a').b`), but not a call on the module.
|
|
714
|
+
fn is_require_value(node: Node<'_>, code: &Source<'_>) -> bool {
|
|
715
|
+
let mut value = node;
|
|
716
|
+
while value.kind_name() == "member_expression" {
|
|
717
|
+
let Some(object) = value.child_by_field_name("object") else {
|
|
718
|
+
return false;
|
|
719
|
+
};
|
|
720
|
+
value = object;
|
|
721
|
+
}
|
|
722
|
+
value.kind_name() == "call_expression"
|
|
723
|
+
&& value
|
|
724
|
+
.child_by_field_name("function")
|
|
725
|
+
.is_some_and(|function| {
|
|
726
|
+
function.kind_name() == "identifier" && node_text(function, code) == "require"
|
|
727
|
+
})
|
|
599
728
|
}
|
|
600
729
|
|
|
601
730
|
/// The kind tag of a string-like node with no interpolation, or None to descend normally.
|
|
@@ -832,7 +961,7 @@ fn is_semantic_name_leaf(node: Node<'_>, code: &Source<'_>) -> bool {
|
|
|
832
961
|
.named_child(0)
|
|
833
962
|
.is_some_and(|first| first.id() == node.id());
|
|
834
963
|
let is_last_named = parent
|
|
835
|
-
.named_child(parent.named_child_count().saturating_sub(1))
|
|
964
|
+
.named_child(parent.named_child_count().saturating_sub(1) as u32)
|
|
836
965
|
.is_some_and(|last| last.id() == node.id());
|
|
837
966
|
if parent.kind_name() == "navigation_expression" && is_last_named && !is_first_named {
|
|
838
967
|
return true;
|
|
@@ -840,7 +969,7 @@ fn is_semantic_name_leaf(node: Node<'_>, code: &Source<'_>) -> bool {
|
|
|
840
969
|
if parent.kind_name() == "call_expression"
|
|
841
970
|
&& is_first_named
|
|
842
971
|
&& parent
|
|
843
|
-
.named_child(parent.named_child_count().saturating_sub(1))
|
|
972
|
+
.named_child(parent.named_child_count().saturating_sub(1) as u32)
|
|
844
973
|
.is_some_and(|arguments| {
|
|
845
974
|
matches!(
|
|
846
975
|
arguments.kind_name(),
|
package/native/src/functions.rs
CHANGED
|
@@ -821,13 +821,16 @@ fn find_string_literal_content(literal: Node<'_>, code: &Source<'_>) -> Option<S
|
|
|
821
821
|
{
|
|
822
822
|
return None;
|
|
823
823
|
}
|
|
824
|
-
//
|
|
825
|
-
//
|
|
824
|
+
// Escapes alone do not spell the content out, so the concatenation is trusted only when a
|
|
825
|
+
// content node takes part in it.
|
|
826
826
|
let mut content = String::new();
|
|
827
827
|
let mut has_content_node = false;
|
|
828
828
|
for child in &children {
|
|
829
829
|
match child.kind_name() {
|
|
830
|
-
"string_content"
|
|
830
|
+
"string_content"
|
|
831
|
+
| "string_fragment"
|
|
832
|
+
| "interpreted_string_literal_content"
|
|
833
|
+
| "raw_string_literal_content" => {
|
|
831
834
|
has_content_node = true;
|
|
832
835
|
content.push_str(node_text(*child, code));
|
|
833
836
|
}
|
|
@@ -838,8 +841,8 @@ fn find_string_literal_content(literal: Node<'_>, code: &Source<'_>) -> Option<S
|
|
|
838
841
|
if has_content_node && !content.is_empty() {
|
|
839
842
|
return Some(content);
|
|
840
843
|
}
|
|
841
|
-
// A
|
|
842
|
-
//
|
|
844
|
+
// A literal without a content child (Go's rune literal) keeps its delimiters in the text; an
|
|
845
|
+
// empty literal is left without a name.
|
|
843
846
|
let text = node_text(literal, code);
|
|
844
847
|
let quote = text
|
|
845
848
|
.chars()
|
|
@@ -880,7 +883,7 @@ fn find_assignment_target_name(assignment: Node<'_>, code: &Source<'_>) -> Optio
|
|
|
880
883
|
"field_expression" => target.child_by_field_name("field")?,
|
|
881
884
|
"member_access_expression" => target.child_by_field_name("name")?,
|
|
882
885
|
"field_access" => target.child_by_field_name("field")?,
|
|
883
|
-
"navigation_expression" => target.named_child(target.named_child_count() - 1)?,
|
|
886
|
+
"navigation_expression" => target.named_child(target.named_child_count() as u32 - 1)?,
|
|
884
887
|
"qualified_identifier" => return unwrap_declarator_name(Some(target), code),
|
|
885
888
|
_ => return None,
|
|
886
889
|
};
|
|
@@ -975,7 +978,7 @@ fn is_value_of_parent(node: Node<'_>, parent: Node<'_>) -> bool {
|
|
|
975
978
|
.is_some_and(|child| child.id() == node.id())
|
|
976
979
|
{
|
|
977
980
|
return matches!(
|
|
978
|
-
parent.field_name_for_child(index
|
|
981
|
+
parent.field_name_for_child(index),
|
|
979
982
|
None | Some("value")
|
|
980
983
|
);
|
|
981
984
|
}
|