rubydex 0.3.0 → 0.4.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.
- checksums.yaml +4 -4
- data/README.md +29 -7
- data/THIRD_PARTY_LICENSES.html +238 -2
- data/exe/rdx +2 -149
- data/ext/rubydex/config.c +140 -0
- data/ext/rubydex/config.h +16 -0
- data/ext/rubydex/definition.c +34 -0
- data/ext/rubydex/definition.h +3 -0
- data/ext/rubydex/diagnostic.c +28 -1
- data/ext/rubydex/diagnostic.h +2 -0
- data/ext/rubydex/extconf.rb +4 -2
- data/ext/rubydex/graph.c +71 -55
- data/ext/rubydex/graph.h +6 -0
- data/ext/rubydex/query.c +398 -16
- data/ext/rubydex/rubydex.c +2 -0
- data/ext/rubydex/utils.c +11 -4
- data/lib/ruby_lsp/rubydex/addon.rb +211 -0
- data/lib/rubydex/cli/command/console.rb +55 -0
- data/lib/rubydex/cli/command/lint/explain.rb +76 -0
- data/lib/rubydex/cli/command/lint.rb +208 -0
- data/lib/rubydex/cli/command/mcp.rb +30 -0
- data/lib/rubydex/cli/command/query.rb +70 -0
- data/lib/rubydex/cli/command/skill.rb +69 -0
- data/lib/rubydex/cli/command.rb +168 -0
- data/lib/rubydex/cli.rb +93 -0
- data/lib/rubydex/config.rb +59 -0
- data/lib/rubydex/diagnostic.rb +12 -3
- data/lib/rubydex/errors.rb +42 -1
- data/lib/rubydex/graph.rb +10 -3
- data/lib/rubydex/linter/custom_rule.rb +97 -0
- data/lib/rubydex/linter/helpers/path_helpers.rb +78 -0
- data/lib/rubydex/linter/helpers/source_access_helpers.rb +31 -0
- data/lib/rubydex/linter/rule_loader.rb +36 -0
- data/lib/rubydex/linter/rule_test_case.rb +343 -0
- data/lib/rubydex/linter/runner.rb +56 -0
- data/lib/rubydex/linter.rb +19 -0
- data/lib/rubydex/location.rb +3 -0
- data/lib/rubydex/mcp_server.rb +1 -2
- data/lib/rubydex/related_information.rb +17 -0
- data/lib/rubydex/rule.rb +33 -0
- data/lib/rubydex/rules/dynamic_ancestor.rb +29 -0
- data/lib/rubydex/rules/dynamic_constant_reference.rb +25 -0
- data/lib/rubydex/rules/dynamic_singleton_definition.rb +30 -0
- data/lib/rubydex/rules/invalid_constant_visibility.rb +28 -0
- data/lib/rubydex/rules/invalid_method_visibility.rb +28 -0
- data/lib/rubydex/rules/parse_error.rb +25 -0
- data/lib/rubydex/rules/parse_warning.rb +28 -0
- data/lib/rubydex/rules/top_level_mixin_self.rb +27 -0
- data/lib/rubydex/rules/undefined_constant_visibility_target.rb +27 -0
- data/lib/rubydex/rules/undefined_method_visibility_target.rb +27 -0
- data/lib/rubydex/severity.rb +70 -0
- data/lib/rubydex/skill.rb +89 -0
- data/lib/rubydex/skill_registry.rb +62 -0
- data/lib/rubydex/version.rb +1 -1
- data/lib/rubydex.rb +8 -0
- data/lib/rubydex_linter/rules/rule_structure.rb +140 -0
- data/rbi/rubydex.rbi +464 -21
- data/rust/Cargo.lock +2 -2
- data/rust/rubydex/Cargo.toml +5 -1
- data/rust/rubydex/benches/graph_memory.rs +3 -5
- data/rust/rubydex/src/bin/generate_ruby_rules.rs +94 -0
- data/rust/rubydex/src/config.rs +538 -157
- data/rust/rubydex/src/diagnostic.rs +162 -45
- data/rust/rubydex/src/errors.rs +0 -1
- data/rust/rubydex/src/indexing/local_graph.rs +6 -5
- data/rust/rubydex/src/indexing/rbs_indexer.rs +270 -6
- data/rust/rubydex/src/indexing/ruby_indexer.rs +10 -12
- data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +134 -81
- data/rust/rubydex/src/lib.rs +1 -0
- data/rust/rubydex/src/listing.rs +26 -1
- data/rust/rubydex/src/main.rs +7 -4
- data/rust/rubydex/src/model/declaration.rs +302 -219
- data/rust/rubydex/src/model/graph.rs +27 -40
- data/rust/rubydex/src/model/name.rs +58 -17
- data/rust/rubydex/src/operation/ruby_builder.rs +30 -45
- data/rust/rubydex/src/path_helpers.rs +77 -0
- data/rust/rubydex/src/query/cypher/schema.rs +63 -0
- data/rust/rubydex/src/query/cypher/tests.rs +26 -1
- data/rust/rubydex/src/query/cypher.rs +8 -11
- data/rust/rubydex/src/query.rs +314 -44
- data/rust/rubydex/src/resolution.rs +139 -187
- data/rust/rubydex/src/resolution_tests.rs +247 -19
- data/rust/rubydex/src/test_utils/context.rs +2 -1
- data/rust/rubydex/src/test_utils/graph_test.rs +26 -12
- data/rust/rubydex/src/test_utils/local_graph_test.rs +19 -0
- data/rust/rubydex/tests/cli.rs +4 -4
- data/rust/rubydex-sys/src/config_api.rs +205 -0
- data/rust/rubydex-sys/src/cypher_api.rs +791 -0
- data/rust/rubydex-sys/src/definition_api.rs +68 -1
- data/rust/rubydex-sys/src/diagnostic_api.rs +42 -8
- data/rust/rubydex-sys/src/graph_api.rs +125 -205
- data/rust/rubydex-sys/src/lib.rs +2 -0
- data/rust/rubydex-sys/src/name_api.rs +49 -17
- data/rust/rubydex-sys/src/utils.rs +37 -0
- data/skills/send-private-method/SKILL.md +133 -0
- metadata +42 -2
data/rust/rubydex/src/query.rs
CHANGED
|
@@ -5,7 +5,7 @@ use std::thread;
|
|
|
5
5
|
|
|
6
6
|
use url::Url;
|
|
7
7
|
|
|
8
|
-
use crate::model::built_in::OBJECT_ID;
|
|
8
|
+
use crate::model::built_in::{BUILT_IN_URI_ID, OBJECT_ID};
|
|
9
9
|
use crate::model::declaration::{Ancestor, Declaration, Namespace};
|
|
10
10
|
use crate::model::definitions::{Definition, Parameter};
|
|
11
11
|
use crate::model::graph::Graph;
|
|
@@ -833,6 +833,63 @@ pub fn follow_method_alias(graph: &Graph, alias_id: DefinitionId) -> Result<Decl
|
|
|
833
833
|
}
|
|
834
834
|
}
|
|
835
835
|
|
|
836
|
+
/// Returns `true` when any definition of `declaration` was seeded by rubydex rather than read from indexed source.
|
|
837
|
+
///
|
|
838
|
+
/// Dead code candidates are reported at declaration granularity, so a seeded definition disqualifies the declaration
|
|
839
|
+
/// even when user code also reopens it.
|
|
840
|
+
fn is_built_in(graph: &Graph, declaration: &Declaration) -> bool {
|
|
841
|
+
declaration.definitions().iter().any(|definition_id| {
|
|
842
|
+
graph
|
|
843
|
+
.definitions()
|
|
844
|
+
.get(definition_id)
|
|
845
|
+
.is_some_and(|definition| definition.uri_id() == &*BUILT_IN_URI_ID)
|
|
846
|
+
})
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/// Returns a list of declarations that may be unused in the codebase, which means
|
|
850
|
+
/// that the analysis could not find any references to them. This misses
|
|
851
|
+
/// meta-programming or untyped code, which is why the term candidates is used.
|
|
852
|
+
///
|
|
853
|
+
/// Currently, only supports constants.
|
|
854
|
+
///
|
|
855
|
+
/// # Panics
|
|
856
|
+
///
|
|
857
|
+
/// Will panic if any of the threads panic
|
|
858
|
+
pub fn dead_code_candidates(graph: &Graph) -> Vec<DeclarationId> {
|
|
859
|
+
let num_threads = thread::available_parallelism().map_or(4, std::num::NonZero::get);
|
|
860
|
+
let declarations = graph.declarations();
|
|
861
|
+
|
|
862
|
+
let ids: Vec<DeclarationId> = declarations.keys().copied().collect();
|
|
863
|
+
let chunk_size = ids.len().div_ceil(num_threads);
|
|
864
|
+
|
|
865
|
+
if chunk_size == 0 {
|
|
866
|
+
return Vec::new();
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
thread::scope(|s| {
|
|
870
|
+
let handles: Vec<_> = ids
|
|
871
|
+
.chunks(chunk_size)
|
|
872
|
+
.map(|chunk| {
|
|
873
|
+
s.spawn(|| {
|
|
874
|
+
chunk
|
|
875
|
+
.iter()
|
|
876
|
+
.filter(|id| {
|
|
877
|
+
let decl = declarations.get(id).unwrap();
|
|
878
|
+
decl.constant_references().is_some_and(HashSet::is_empty)
|
|
879
|
+
&& !matches!(decl, Declaration::Namespace(Namespace::SingletonClass(_)))
|
|
880
|
+
&& !decl.has_no_definitions()
|
|
881
|
+
&& !is_built_in(graph, decl)
|
|
882
|
+
})
|
|
883
|
+
.copied()
|
|
884
|
+
.collect::<Vec<_>>()
|
|
885
|
+
})
|
|
886
|
+
})
|
|
887
|
+
.collect();
|
|
888
|
+
|
|
889
|
+
handles.into_iter().flat_map(|h| h.join().unwrap()).collect()
|
|
890
|
+
})
|
|
891
|
+
}
|
|
892
|
+
|
|
836
893
|
#[cfg(test)]
|
|
837
894
|
mod tests {
|
|
838
895
|
use std::str::FromStr;
|
|
@@ -920,6 +977,21 @@ mod tests {
|
|
|
920
977
|
};
|
|
921
978
|
}
|
|
922
979
|
|
|
980
|
+
macro_rules! assert_dead_code_candidates {
|
|
981
|
+
($context:expr, [$($expected:expr),* $(,)?]) => {
|
|
982
|
+
let mut actual: Vec<String> = dead_code_candidates($context.graph())
|
|
983
|
+
.iter()
|
|
984
|
+
.map(|id| $context.graph().declarations().get(id).unwrap().name().to_string())
|
|
985
|
+
.collect();
|
|
986
|
+
actual.sort();
|
|
987
|
+
|
|
988
|
+
let mut expected: Vec<String> = vec![$(String::from($expected)),*];
|
|
989
|
+
expected.sort();
|
|
990
|
+
|
|
991
|
+
assert_eq!(expected, actual);
|
|
992
|
+
};
|
|
993
|
+
}
|
|
994
|
+
|
|
923
995
|
#[test]
|
|
924
996
|
fn fuzzy_search_returns_partial_matches() {
|
|
925
997
|
let mut context = GraphTest::new();
|
|
@@ -1173,7 +1245,13 @@ mod tests {
|
|
|
1173
1245
|
);
|
|
1174
1246
|
context.resolve();
|
|
1175
1247
|
|
|
1176
|
-
let name_id = Name::new(
|
|
1248
|
+
let name_id = Name::new(
|
|
1249
|
+
context.graph().names(),
|
|
1250
|
+
StringId::from("Child"),
|
|
1251
|
+
ParentScope::None,
|
|
1252
|
+
None,
|
|
1253
|
+
)
|
|
1254
|
+
.id();
|
|
1177
1255
|
assert_declaration_completion_eq!(
|
|
1178
1256
|
context,
|
|
1179
1257
|
CompletionReceiver::Expression {
|
|
@@ -1222,7 +1300,13 @@ mod tests {
|
|
|
1222
1300
|
);
|
|
1223
1301
|
context.resolve();
|
|
1224
1302
|
|
|
1225
|
-
let name_id = Name::new(
|
|
1303
|
+
let name_id = Name::new(
|
|
1304
|
+
context.graph().names(),
|
|
1305
|
+
StringId::from("Child"),
|
|
1306
|
+
ParentScope::None,
|
|
1307
|
+
None,
|
|
1308
|
+
)
|
|
1309
|
+
.id();
|
|
1226
1310
|
assert_declaration_completion_eq!(
|
|
1227
1311
|
context,
|
|
1228
1312
|
CompletionReceiver::Expression {
|
|
@@ -1271,7 +1355,7 @@ mod tests {
|
|
|
1271
1355
|
);
|
|
1272
1356
|
context.resolve();
|
|
1273
1357
|
|
|
1274
|
-
let name_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
1358
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Foo"), ParentScope::None, None).id();
|
|
1275
1359
|
assert_declaration_completion_eq!(
|
|
1276
1360
|
context,
|
|
1277
1361
|
CompletionReceiver::Expression {
|
|
@@ -1320,8 +1404,14 @@ mod tests {
|
|
|
1320
1404
|
);
|
|
1321
1405
|
context.resolve();
|
|
1322
1406
|
|
|
1323
|
-
let foo_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
1324
|
-
let name_id = Name::new(
|
|
1407
|
+
let foo_id = Name::new(context.graph().names(), StringId::from("Foo"), ParentScope::None, None).id();
|
|
1408
|
+
let name_id = Name::new(
|
|
1409
|
+
context.graph().names(),
|
|
1410
|
+
StringId::from("<Foo>"),
|
|
1411
|
+
ParentScope::Attached(foo_id),
|
|
1412
|
+
Some(foo_id),
|
|
1413
|
+
)
|
|
1414
|
+
.id();
|
|
1325
1415
|
assert_declaration_completion_eq!(
|
|
1326
1416
|
context,
|
|
1327
1417
|
CompletionReceiver::Expression {
|
|
@@ -1341,7 +1431,7 @@ mod tests {
|
|
|
1341
1431
|
]
|
|
1342
1432
|
);
|
|
1343
1433
|
|
|
1344
|
-
let name_id = Name::new(StringId::from("Bar"), ParentScope::None, None).id();
|
|
1434
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Bar"), ParentScope::None, None).id();
|
|
1345
1435
|
assert_declaration_completion_eq!(
|
|
1346
1436
|
context,
|
|
1347
1437
|
CompletionReceiver::Expression {
|
|
@@ -1383,8 +1473,14 @@ mod tests {
|
|
|
1383
1473
|
);
|
|
1384
1474
|
context.resolve();
|
|
1385
1475
|
|
|
1386
|
-
let foo_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
1387
|
-
let name_id = Name::new(
|
|
1476
|
+
let foo_id = Name::new(context.graph().names(), StringId::from("Foo"), ParentScope::None, None).id();
|
|
1477
|
+
let name_id = Name::new(
|
|
1478
|
+
context.graph().names(),
|
|
1479
|
+
StringId::from("<Foo>"),
|
|
1480
|
+
ParentScope::Attached(foo_id),
|
|
1481
|
+
Some(foo_id),
|
|
1482
|
+
)
|
|
1483
|
+
.id();
|
|
1388
1484
|
|
|
1389
1485
|
assert_declaration_completion_eq!(
|
|
1390
1486
|
context,
|
|
@@ -1431,9 +1527,10 @@ mod tests {
|
|
|
1431
1527
|
context.resolve();
|
|
1432
1528
|
|
|
1433
1529
|
let name_id = Name::new(
|
|
1530
|
+
context.graph().names(),
|
|
1434
1531
|
StringId::from("Bar"),
|
|
1435
1532
|
ParentScope::TopLevel,
|
|
1436
|
-
Some(Name::new(StringId::from("Foo"), ParentScope::None, None).id()),
|
|
1533
|
+
Some(Name::new(context.graph().names(), StringId::from("Foo"), ParentScope::None, None).id()),
|
|
1437
1534
|
)
|
|
1438
1535
|
.id();
|
|
1439
1536
|
|
|
@@ -1457,7 +1554,7 @@ mod tests {
|
|
|
1457
1554
|
]
|
|
1458
1555
|
);
|
|
1459
1556
|
|
|
1460
|
-
let name_id = Name::new(StringId::from("Bar"), ParentScope::None, None).id();
|
|
1557
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Bar"), ParentScope::None, None).id();
|
|
1461
1558
|
assert_declaration_completion_eq!(
|
|
1462
1559
|
context,
|
|
1463
1560
|
CompletionReceiver::Expression {
|
|
@@ -1498,8 +1595,14 @@ mod tests {
|
|
|
1498
1595
|
);
|
|
1499
1596
|
context.resolve();
|
|
1500
1597
|
|
|
1501
|
-
let foo_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
1502
|
-
let name_id = Name::new(
|
|
1598
|
+
let foo_id = Name::new(context.graph().names(), StringId::from("Foo"), ParentScope::None, None).id();
|
|
1599
|
+
let name_id = Name::new(
|
|
1600
|
+
context.graph().names(),
|
|
1601
|
+
StringId::from("Bar"),
|
|
1602
|
+
ParentScope::None,
|
|
1603
|
+
Some(foo_id),
|
|
1604
|
+
)
|
|
1605
|
+
.id();
|
|
1503
1606
|
// Foo::CONST is reachable from Foo::Bar through lexical scoping, so it must appear as a completion candidate
|
|
1504
1607
|
// when the user types the unqualified name CONST
|
|
1505
1608
|
assert_declaration_completion_eq!(
|
|
@@ -1544,9 +1647,10 @@ mod tests {
|
|
|
1544
1647
|
context.resolve();
|
|
1545
1648
|
|
|
1546
1649
|
let name_id = Name::new(
|
|
1650
|
+
context.graph().names(),
|
|
1547
1651
|
StringId::from("Bar"),
|
|
1548
1652
|
ParentScope::None,
|
|
1549
|
-
Some(Name::new(StringId::from("Foo"), ParentScope::None, None).id()),
|
|
1653
|
+
Some(Name::new(context.graph().names(), StringId::from("Foo"), ParentScope::None, None).id()),
|
|
1550
1654
|
)
|
|
1551
1655
|
.id();
|
|
1552
1656
|
assert_declaration_completion_eq!(
|
|
@@ -2078,7 +2182,7 @@ mod tests {
|
|
|
2078
2182
|
);
|
|
2079
2183
|
context.resolve();
|
|
2080
2184
|
|
|
2081
|
-
let name_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
2185
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Foo"), ParentScope::None, None).id();
|
|
2082
2186
|
assert_declaration_completion_eq!(
|
|
2083
2187
|
context,
|
|
2084
2188
|
CompletionReceiver::MethodArgument {
|
|
@@ -2119,7 +2223,7 @@ mod tests {
|
|
|
2119
2223
|
);
|
|
2120
2224
|
context.resolve();
|
|
2121
2225
|
|
|
2122
|
-
let name_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
2226
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Foo"), ParentScope::None, None).id();
|
|
2123
2227
|
assert_declaration_completion_eq!(
|
|
2124
2228
|
context,
|
|
2125
2229
|
CompletionReceiver::MethodArgument {
|
|
@@ -2156,7 +2260,7 @@ mod tests {
|
|
|
2156
2260
|
);
|
|
2157
2261
|
context.resolve();
|
|
2158
2262
|
|
|
2159
|
-
let name_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
2263
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Foo"), ParentScope::None, None).id();
|
|
2160
2264
|
assert_declaration_completion_eq!(
|
|
2161
2265
|
context,
|
|
2162
2266
|
CompletionReceiver::MethodArgument {
|
|
@@ -2182,7 +2286,7 @@ mod tests {
|
|
|
2182
2286
|
);
|
|
2183
2287
|
context.resolve();
|
|
2184
2288
|
|
|
2185
|
-
let name_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
2289
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Foo"), ParentScope::None, None).id();
|
|
2186
2290
|
assert_declaration_completion_eq!(
|
|
2187
2291
|
context,
|
|
2188
2292
|
CompletionReceiver::MethodArgument {
|
|
@@ -2226,7 +2330,7 @@ mod tests {
|
|
|
2226
2330
|
);
|
|
2227
2331
|
context.resolve();
|
|
2228
2332
|
|
|
2229
|
-
let name_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
2333
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Foo"), ParentScope::None, None).id();
|
|
2230
2334
|
assert_declaration_completion_eq!(
|
|
2231
2335
|
context,
|
|
2232
2336
|
CompletionReceiver::MethodArgument {
|
|
@@ -2254,7 +2358,7 @@ mod tests {
|
|
|
2254
2358
|
context.index_uri("file:///foo.rb", "class Foo; end");
|
|
2255
2359
|
context.resolve();
|
|
2256
2360
|
|
|
2257
|
-
let name_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
2361
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Foo"), ParentScope::None, None).id();
|
|
2258
2362
|
assert_completion_eq!(
|
|
2259
2363
|
context,
|
|
2260
2364
|
CompletionReceiver::Expression {
|
|
@@ -2319,7 +2423,7 @@ mod tests {
|
|
|
2319
2423
|
context.index_uri("file:///foo.rb", "class Foo; def bar(name:); end; end");
|
|
2320
2424
|
context.resolve();
|
|
2321
2425
|
|
|
2322
|
-
let name_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
2426
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Foo"), ParentScope::None, None).id();
|
|
2323
2427
|
assert_completion_eq!(
|
|
2324
2428
|
context,
|
|
2325
2429
|
CompletionReceiver::MethodArgument {
|
|
@@ -2440,7 +2544,13 @@ mod tests {
|
|
|
2440
2544
|
);
|
|
2441
2545
|
context.resolve();
|
|
2442
2546
|
|
|
2443
|
-
let outer_name_id = Name::new(
|
|
2547
|
+
let outer_name_id = Name::new(
|
|
2548
|
+
context.graph().names(),
|
|
2549
|
+
StringId::from("Outer"),
|
|
2550
|
+
ParentScope::None,
|
|
2551
|
+
None,
|
|
2552
|
+
)
|
|
2553
|
+
.id();
|
|
2444
2554
|
assert_declaration_completion_eq!(
|
|
2445
2555
|
context,
|
|
2446
2556
|
CompletionReceiver::Expression {
|
|
@@ -2479,7 +2589,13 @@ mod tests {
|
|
|
2479
2589
|
);
|
|
2480
2590
|
context.resolve();
|
|
2481
2591
|
|
|
2482
|
-
let name_id = Name::new(
|
|
2592
|
+
let name_id = Name::new(
|
|
2593
|
+
context.graph().names(),
|
|
2594
|
+
StringId::from("Outer"),
|
|
2595
|
+
ParentScope::None,
|
|
2596
|
+
None,
|
|
2597
|
+
)
|
|
2598
|
+
.id();
|
|
2483
2599
|
// `self_decl_id` points to the alias `Outer::MyAlias`, which is a `ConstantAlias` rather than a `Namespace`.
|
|
2484
2600
|
// The completion should still collect members from the aliased namespace (`Outer::Original`) instead of
|
|
2485
2601
|
// returning an error, so callers do not have to unwrap aliases themselves.
|
|
@@ -2525,7 +2641,7 @@ mod tests {
|
|
|
2525
2641
|
);
|
|
2526
2642
|
context.resolve();
|
|
2527
2643
|
|
|
2528
|
-
let name_id = Name::new(StringId::from("Bar"), ParentScope::None, None).id();
|
|
2644
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Bar"), ParentScope::None, None).id();
|
|
2529
2645
|
assert_declaration_completion_eq!(
|
|
2530
2646
|
context,
|
|
2531
2647
|
CompletionReceiver::Expression {
|
|
@@ -2568,7 +2684,7 @@ mod tests {
|
|
|
2568
2684
|
);
|
|
2569
2685
|
context.resolve();
|
|
2570
2686
|
|
|
2571
|
-
let name_id = Name::new(StringId::from("Bar"), ParentScope::None, None).id();
|
|
2687
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Bar"), ParentScope::None, None).id();
|
|
2572
2688
|
assert_declaration_completion_eq!(
|
|
2573
2689
|
context,
|
|
2574
2690
|
CompletionReceiver::Expression {
|
|
@@ -2613,7 +2729,7 @@ mod tests {
|
|
|
2613
2729
|
);
|
|
2614
2730
|
context.resolve();
|
|
2615
2731
|
|
|
2616
|
-
let name_id = Name::new(StringId::from("Bar"), ParentScope::None, None).id();
|
|
2732
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Bar"), ParentScope::None, None).id();
|
|
2617
2733
|
assert_declaration_completion_eq!(
|
|
2618
2734
|
context,
|
|
2619
2735
|
CompletionReceiver::Expression {
|
|
@@ -2650,7 +2766,13 @@ mod tests {
|
|
|
2650
2766
|
);
|
|
2651
2767
|
context.resolve();
|
|
2652
2768
|
|
|
2653
|
-
let name_id = Name::new(
|
|
2769
|
+
let name_id = Name::new(
|
|
2770
|
+
context.graph().names(),
|
|
2771
|
+
StringId::from("Object"),
|
|
2772
|
+
ParentScope::None,
|
|
2773
|
+
None,
|
|
2774
|
+
)
|
|
2775
|
+
.id();
|
|
2654
2776
|
assert_declaration_completion_eq!(
|
|
2655
2777
|
context,
|
|
2656
2778
|
CompletionReceiver::Expression {
|
|
@@ -2677,7 +2799,7 @@ mod tests {
|
|
|
2677
2799
|
);
|
|
2678
2800
|
context.resolve();
|
|
2679
2801
|
|
|
2680
|
-
let name_id = Name::new(StringId::from("Mod"), ParentScope::None, None).id();
|
|
2802
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Mod"), ParentScope::None, None).id();
|
|
2681
2803
|
assert_declaration_completion_eq!(
|
|
2682
2804
|
context,
|
|
2683
2805
|
CompletionReceiver::Expression {
|
|
@@ -2715,7 +2837,7 @@ mod tests {
|
|
|
2715
2837
|
);
|
|
2716
2838
|
context.resolve();
|
|
2717
2839
|
|
|
2718
|
-
let name_id = Name::new(StringId::from("Mod"), ParentScope::None, None).id();
|
|
2840
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Mod"), ParentScope::None, None).id();
|
|
2719
2841
|
assert_declaration_completion_eq!(
|
|
2720
2842
|
context,
|
|
2721
2843
|
CompletionReceiver::Expression {
|
|
@@ -2750,7 +2872,13 @@ mod tests {
|
|
|
2750
2872
|
);
|
|
2751
2873
|
context.resolve();
|
|
2752
2874
|
|
|
2753
|
-
let name_id = Name::new(
|
|
2875
|
+
let name_id = Name::new(
|
|
2876
|
+
context.graph().names(),
|
|
2877
|
+
StringId::from("Object"),
|
|
2878
|
+
ParentScope::None,
|
|
2879
|
+
None,
|
|
2880
|
+
)
|
|
2881
|
+
.id();
|
|
2754
2882
|
assert_declaration_completion_eq!(
|
|
2755
2883
|
context,
|
|
2756
2884
|
CompletionReceiver::Expression {
|
|
@@ -2785,7 +2913,7 @@ mod tests {
|
|
|
2785
2913
|
);
|
|
2786
2914
|
context.resolve();
|
|
2787
2915
|
|
|
2788
|
-
let name_id = Name::new(StringId::from("Bar"), ParentScope::None, None).id();
|
|
2916
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Bar"), ParentScope::None, None).id();
|
|
2789
2917
|
assert_declaration_completion_eq!(
|
|
2790
2918
|
context,
|
|
2791
2919
|
CompletionReceiver::Expression {
|
|
@@ -2818,9 +2946,21 @@ mod tests {
|
|
|
2818
2946
|
);
|
|
2819
2947
|
context.resolve();
|
|
2820
2948
|
|
|
2821
|
-
let bar_id = Name::new(StringId::from("Bar"), ParentScope::None, None).id();
|
|
2822
|
-
let foo_ref_id = Name::new(
|
|
2823
|
-
|
|
2949
|
+
let bar_id = Name::new(context.graph().names(), StringId::from("Bar"), ParentScope::None, None).id();
|
|
2950
|
+
let foo_ref_id = Name::new(
|
|
2951
|
+
context.graph().names(),
|
|
2952
|
+
StringId::from("Foo"),
|
|
2953
|
+
ParentScope::None,
|
|
2954
|
+
Some(bar_id),
|
|
2955
|
+
)
|
|
2956
|
+
.id();
|
|
2957
|
+
let nesting_name_id = Name::new(
|
|
2958
|
+
context.graph().names(),
|
|
2959
|
+
StringId::from("<Foo>"),
|
|
2960
|
+
ParentScope::Attached(foo_ref_id),
|
|
2961
|
+
Some(bar_id),
|
|
2962
|
+
)
|
|
2963
|
+
.id();
|
|
2824
2964
|
|
|
2825
2965
|
assert_declaration_completion_eq!(
|
|
2826
2966
|
context,
|
|
@@ -2859,7 +2999,7 @@ mod tests {
|
|
|
2859
2999
|
);
|
|
2860
3000
|
context.resolve();
|
|
2861
3001
|
|
|
2862
|
-
let name_id = Name::new(StringId::from("Mod"), ParentScope::None, None).id();
|
|
3002
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Mod"), ParentScope::None, None).id();
|
|
2863
3003
|
assert_declaration_completion_eq!(
|
|
2864
3004
|
context,
|
|
2865
3005
|
CompletionReceiver::Expression {
|
|
@@ -2897,8 +3037,14 @@ mod tests {
|
|
|
2897
3037
|
);
|
|
2898
3038
|
context.resolve();
|
|
2899
3039
|
|
|
2900
|
-
let foo_ref_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
2901
|
-
let nesting_name_id = Name::new(
|
|
3040
|
+
let foo_ref_id = Name::new(context.graph().names(), StringId::from("Foo"), ParentScope::None, None).id();
|
|
3041
|
+
let nesting_name_id = Name::new(
|
|
3042
|
+
context.graph().names(),
|
|
3043
|
+
StringId::from("<Foo>"),
|
|
3044
|
+
ParentScope::Attached(foo_ref_id),
|
|
3045
|
+
None,
|
|
3046
|
+
)
|
|
3047
|
+
.id();
|
|
2902
3048
|
|
|
2903
3049
|
assert_declaration_completion_eq!(
|
|
2904
3050
|
context,
|
|
@@ -2923,7 +3069,7 @@ mod tests {
|
|
|
2923
3069
|
);
|
|
2924
3070
|
context.resolve();
|
|
2925
3071
|
|
|
2926
|
-
let name_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
3072
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Foo"), ParentScope::None, None).id();
|
|
2927
3073
|
let result = completion_candidates(
|
|
2928
3074
|
context.graph(),
|
|
2929
3075
|
CompletionContext::new(CompletionReceiver::Expression {
|
|
@@ -2949,7 +3095,7 @@ mod tests {
|
|
|
2949
3095
|
);
|
|
2950
3096
|
context.resolve();
|
|
2951
3097
|
|
|
2952
|
-
let name_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
3098
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Foo"), ParentScope::None, None).id();
|
|
2953
3099
|
let result = completion_candidates(
|
|
2954
3100
|
context.graph(),
|
|
2955
3101
|
CompletionContext::new(CompletionReceiver::Expression {
|
|
@@ -2981,7 +3127,13 @@ mod tests {
|
|
|
2981
3127
|
);
|
|
2982
3128
|
context.resolve();
|
|
2983
3129
|
|
|
2984
|
-
let name_id = Name::new(
|
|
3130
|
+
let name_id = Name::new(
|
|
3131
|
+
context.graph().names(),
|
|
3132
|
+
StringId::from("Outer"),
|
|
3133
|
+
ParentScope::None,
|
|
3134
|
+
None,
|
|
3135
|
+
)
|
|
3136
|
+
.id();
|
|
2985
3137
|
assert_declaration_completion_eq!(
|
|
2986
3138
|
context,
|
|
2987
3139
|
CompletionReceiver::Expression {
|
|
@@ -3024,7 +3176,7 @@ mod tests {
|
|
|
3024
3176
|
);
|
|
3025
3177
|
context.resolve();
|
|
3026
3178
|
|
|
3027
|
-
let name_id = Name::new(StringId::from("Bar"), ParentScope::None, None).id();
|
|
3179
|
+
let name_id = Name::new(context.graph().names(), StringId::from("Bar"), ParentScope::None, None).id();
|
|
3028
3180
|
assert_declaration_completion_eq!(
|
|
3029
3181
|
context,
|
|
3030
3182
|
CompletionReceiver::Expression {
|
|
@@ -3097,7 +3249,7 @@ mod tests {
|
|
|
3097
3249
|
);
|
|
3098
3250
|
context.resolve();
|
|
3099
3251
|
|
|
3100
|
-
let foo_name_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
3252
|
+
let foo_name_id = Name::new(context.graph().names(), StringId::from("Foo"), ParentScope::None, None).id();
|
|
3101
3253
|
assert_declaration_completion_eq!(
|
|
3102
3254
|
context,
|
|
3103
3255
|
CompletionReceiver::Expression {
|
|
@@ -3117,7 +3269,7 @@ mod tests {
|
|
|
3117
3269
|
]
|
|
3118
3270
|
);
|
|
3119
3271
|
|
|
3120
|
-
let bar_name_id = Name::new(StringId::from("Bar"), ParentScope::None, None).id();
|
|
3272
|
+
let bar_name_id = Name::new(context.graph().names(), StringId::from("Bar"), ParentScope::None, None).id();
|
|
3121
3273
|
assert_declaration_completion_eq!(
|
|
3122
3274
|
context,
|
|
3123
3275
|
CompletionReceiver::Expression {
|
|
@@ -3433,7 +3585,7 @@ mod tests {
|
|
|
3433
3585
|
);
|
|
3434
3586
|
context.resolve();
|
|
3435
3587
|
|
|
3436
|
-
let foo_name_id = Name::new(StringId::from("Foo"), ParentScope::None, None).id();
|
|
3588
|
+
let foo_name_id = Name::new(context.graph().names(), StringId::from("Foo"), ParentScope::None, None).id();
|
|
3437
3589
|
assert_declaration_completion_eq!(
|
|
3438
3590
|
context,
|
|
3439
3591
|
CompletionReceiver::Expression {
|
|
@@ -3902,4 +4054,122 @@ mod tests {
|
|
|
3902
4054
|
Err(FindMemberError::DeclarationNotFound),
|
|
3903
4055
|
);
|
|
3904
4056
|
}
|
|
4057
|
+
|
|
4058
|
+
#[test]
|
|
4059
|
+
fn dead_code_candidates_returns_unreferenced_constants() {
|
|
4060
|
+
let mut context = GraphTest::new();
|
|
4061
|
+
context.index_uri(
|
|
4062
|
+
"file:///foo.rb",
|
|
4063
|
+
r"
|
|
4064
|
+
class UnusedClass; end
|
|
4065
|
+
module UnusedModule; end
|
|
4066
|
+
UNUSED_CONSTANT = 1
|
|
4067
|
+
",
|
|
4068
|
+
);
|
|
4069
|
+
context.resolve();
|
|
4070
|
+
|
|
4071
|
+
assert_dead_code_candidates!(context, ["UnusedClass", "UnusedModule", "UNUSED_CONSTANT"]);
|
|
4072
|
+
}
|
|
4073
|
+
|
|
4074
|
+
#[test]
|
|
4075
|
+
fn dead_code_candidates_excludes_referenced_constants() {
|
|
4076
|
+
let mut context = GraphTest::new();
|
|
4077
|
+
context.index_uri(
|
|
4078
|
+
"file:///foo.rb",
|
|
4079
|
+
r"
|
|
4080
|
+
class UsedClass; end
|
|
4081
|
+
USED_CONSTANT = 1
|
|
4082
|
+
",
|
|
4083
|
+
);
|
|
4084
|
+
context.index_uri(
|
|
4085
|
+
"file:///bar.rb",
|
|
4086
|
+
r"
|
|
4087
|
+
UsedClass
|
|
4088
|
+
USED_CONSTANT
|
|
4089
|
+
",
|
|
4090
|
+
);
|
|
4091
|
+
context.resolve();
|
|
4092
|
+
|
|
4093
|
+
assert_dead_code_candidates!(context, []);
|
|
4094
|
+
}
|
|
4095
|
+
|
|
4096
|
+
#[test]
|
|
4097
|
+
fn dead_code_candidates_excludes_methods_and_variables() {
|
|
4098
|
+
let mut context = GraphTest::new();
|
|
4099
|
+
context.index_uri(
|
|
4100
|
+
"file:///foo.rb",
|
|
4101
|
+
r"
|
|
4102
|
+
$global_var = 1
|
|
4103
|
+
|
|
4104
|
+
class Holder
|
|
4105
|
+
@@class_var = 1
|
|
4106
|
+
|
|
4107
|
+
def initialize
|
|
4108
|
+
@instance_var = 1
|
|
4109
|
+
end
|
|
4110
|
+
|
|
4111
|
+
def never_called; end
|
|
4112
|
+
end
|
|
4113
|
+
",
|
|
4114
|
+
);
|
|
4115
|
+
context.resolve();
|
|
4116
|
+
|
|
4117
|
+
assert_dead_code_candidates!(context, ["Holder"]);
|
|
4118
|
+
}
|
|
4119
|
+
|
|
4120
|
+
#[test]
|
|
4121
|
+
fn dead_code_candidates_counts_references_rather_than_reachability() {
|
|
4122
|
+
let mut context = GraphTest::new();
|
|
4123
|
+
context.index_uri(
|
|
4124
|
+
"file:///foo.rb",
|
|
4125
|
+
r"
|
|
4126
|
+
class Target; end
|
|
4127
|
+
AliasName = Target
|
|
4128
|
+
",
|
|
4129
|
+
);
|
|
4130
|
+
context.resolve();
|
|
4131
|
+
|
|
4132
|
+
// `AliasName` is itself dead, but it still references `Target`, giving `Target` a non-zero count.
|
|
4133
|
+
// A single pass only peels the outermost layer of a dead subgraph.
|
|
4134
|
+
assert_dead_code_candidates!(context, ["AliasName"]);
|
|
4135
|
+
}
|
|
4136
|
+
|
|
4137
|
+
#[test]
|
|
4138
|
+
fn dead_code_candidates_excludes_built_ins_and_definitionless_declarations() {
|
|
4139
|
+
let mut context = GraphTest::new();
|
|
4140
|
+
context.index_uri(
|
|
4141
|
+
"file:///foo.rb",
|
|
4142
|
+
r"
|
|
4143
|
+
class Unused
|
|
4144
|
+
def self.class_method; end
|
|
4145
|
+
end
|
|
4146
|
+
|
|
4147
|
+
class Class
|
|
4148
|
+
end
|
|
4149
|
+
",
|
|
4150
|
+
);
|
|
4151
|
+
context.resolve();
|
|
4152
|
+
|
|
4153
|
+
// A reopened built-in remains excluded regardless of definition order.
|
|
4154
|
+
assert_dead_code_candidates!(context, ["Unused"]);
|
|
4155
|
+
}
|
|
4156
|
+
|
|
4157
|
+
#[test]
|
|
4158
|
+
fn dead_code_candidates_excludes_singleton_classes() {
|
|
4159
|
+
let mut context = GraphTest::new();
|
|
4160
|
+
context.index_uri(
|
|
4161
|
+
"file:///foo.rb",
|
|
4162
|
+
r"
|
|
4163
|
+
class Unused; end
|
|
4164
|
+
class Attached; end
|
|
4165
|
+
|
|
4166
|
+
class << Attached
|
|
4167
|
+
def never_called; end
|
|
4168
|
+
end
|
|
4169
|
+
",
|
|
4170
|
+
);
|
|
4171
|
+
context.resolve();
|
|
4172
|
+
|
|
4173
|
+
assert_dead_code_candidates!(context, ["Unused"]);
|
|
4174
|
+
}
|
|
3905
4175
|
}
|