rubydex 0.2.9 → 0.4.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.
- checksums.yaml +4 -4
- data/README.md +110 -6
- data/THIRD_PARTY_LICENSES.html +271 -2
- data/exe/rdx +2 -73
- data/ext/rubydex/config.c +140 -0
- data/ext/rubydex/config.h +16 -0
- data/ext/rubydex/declaration.c +1 -1
- data/ext/rubydex/definition.c +32 -4
- data/ext/rubydex/diagnostic.c +75 -1
- data/ext/rubydex/diagnostic.h +2 -0
- data/ext/rubydex/graph.c +27 -48
- data/ext/rubydex/graph.h +6 -0
- data/ext/rubydex/query.c +487 -0
- data/ext/rubydex/query.h +8 -0
- data/ext/rubydex/reference.c +60 -0
- data/ext/rubydex/rubydex.c +4 -0
- data/ext/rubydex/utils.c +23 -4
- data/ext/rubydex/utils.h +5 -0
- 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 +74 -0
- data/lib/rubydex/cli/command/lint.rb +202 -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/severity.rb +70 -0
- data/lib/rubydex/skill.rb +88 -0
- data/lib/rubydex/skill_registry.rb +62 -0
- data/lib/rubydex/version.rb +1 -1
- data/lib/rubydex.rb +6 -0
- data/lib/rubydex_linter/rules/rule_structure.rb +125 -0
- data/rbi/rubydex.rbi +578 -15
- data/rust/Cargo.lock +7 -0
- data/rust/rubydex/Cargo.toml +1 -0
- data/rust/rubydex/benches/graph_memory.rs +20 -4
- data/rust/rubydex/src/compile_assertions.rs +15 -0
- data/rust/rubydex/src/config.rs +538 -157
- data/rust/rubydex/src/diagnostic.rs +66 -40
- 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 +284 -8
- data/rust/rubydex/src/indexing/ruby_indexer.rs +59 -70
- data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +195 -86
- data/rust/rubydex/src/lib.rs +1 -0
- data/rust/rubydex/src/listing.rs +26 -1
- data/rust/rubydex/src/main.rs +9 -128
- data/rust/rubydex/src/model/declaration.rs +301 -229
- data/rust/rubydex/src/model/definitions.rs +27 -26
- data/rust/rubydex/src/model/document.rs +43 -7
- data/rust/rubydex/src/model/graph.rs +67 -68
- data/rust/rubydex/src/model/id.rs +55 -0
- data/rust/rubydex/src/model/ids.rs +21 -9
- data/rust/rubydex/src/model/name.rs +88 -19
- data/rust/rubydex/src/model/references.rs +16 -13
- data/rust/rubydex/src/operation/ruby_builder.rs +78 -104
- data/rust/rubydex/src/path_helpers.rs +77 -0
- data/rust/rubydex/src/query/cypher/schema.rs +853 -0
- data/rust/rubydex/src/query/cypher/schema_info.rs +161 -0
- data/rust/rubydex/src/query/cypher/tests.rs +253 -0
- data/rust/rubydex/src/query/cypher.rs +54 -0
- data/rust/rubydex/src/query.rs +125 -43
- data/rust/rubydex/src/resolution.rs +368 -395
- data/rust/rubydex/src/resolution_tests.rs +504 -78
- 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/declaration_api.rs +6 -3
- data/rust/rubydex-sys/src/definition_api.rs +27 -7
- data/rust/rubydex-sys/src/diagnostic_api.rs +77 -8
- data/rust/rubydex-sys/src/graph_api.rs +31 -68
- data/rust/rubydex-sys/src/lib.rs +2 -0
- data/rust/rubydex-sys/src/name_api.rs +2 -6
- data/rust/rubydex-sys/src/reference_api.rs +58 -12
- data/rust/rubydex-sys/src/utils.rs +37 -0
- data/skills/send-private-method/SKILL.md +133 -0
- metadata +37 -2
|
@@ -8,9 +8,8 @@ use crate::{
|
|
|
8
8
|
assert_declaration_exists, assert_declaration_kind_eq, assert_declaration_references_count_eq, assert_descendants,
|
|
9
9
|
assert_diagnostics_eq, assert_instance_variables_eq, assert_members_eq, assert_no_constant_alias_target,
|
|
10
10
|
assert_no_diagnostics, assert_no_members, assert_owner_eq, assert_singleton_class_eq,
|
|
11
|
-
diagnostic::Rule,
|
|
11
|
+
diagnostic::{Rule, Severity},
|
|
12
12
|
model::{declaration::Ancestors, ids::DeclarationId, name::NameRef},
|
|
13
|
-
resolution::Resolver,
|
|
14
13
|
test_utils::GraphTest,
|
|
15
14
|
};
|
|
16
15
|
|
|
@@ -1495,6 +1494,43 @@ mod include_tests {
|
|
|
1495
1494
|
assert_descendants!(context, "Bar", ["Baz"]);
|
|
1496
1495
|
assert_descendants!(context, "Foo", ["Bar", "Baz"]);
|
|
1497
1496
|
}
|
|
1497
|
+
|
|
1498
|
+
#[test]
|
|
1499
|
+
fn unresolved_include_followed_by_resolved_include_stays_partial() {
|
|
1500
|
+
let mut context = graph_test();
|
|
1501
|
+
context.index_uri("file:///foo.rb", {
|
|
1502
|
+
r"
|
|
1503
|
+
module M
|
|
1504
|
+
end
|
|
1505
|
+
|
|
1506
|
+
class C
|
|
1507
|
+
include Missing
|
|
1508
|
+
include M
|
|
1509
|
+
end
|
|
1510
|
+
"
|
|
1511
|
+
});
|
|
1512
|
+
context.resolve();
|
|
1513
|
+
|
|
1514
|
+
assert_ancestors_eq!(
|
|
1515
|
+
context,
|
|
1516
|
+
"C",
|
|
1517
|
+
["C", "M", Partial("Missing"), "Object", "Kernel", "BasicObject"]
|
|
1518
|
+
);
|
|
1519
|
+
assert!(
|
|
1520
|
+
matches!(
|
|
1521
|
+
context
|
|
1522
|
+
.graph()
|
|
1523
|
+
.declarations()
|
|
1524
|
+
.get(&DeclarationId::from("C"))
|
|
1525
|
+
.unwrap()
|
|
1526
|
+
.as_namespace()
|
|
1527
|
+
.unwrap()
|
|
1528
|
+
.ancestors(),
|
|
1529
|
+
Ancestors::Partial(_)
|
|
1530
|
+
),
|
|
1531
|
+
"C should have a partial chain: the `Missing` include never resolves"
|
|
1532
|
+
);
|
|
1533
|
+
}
|
|
1498
1534
|
}
|
|
1499
1535
|
|
|
1500
1536
|
mod prepend_tests {
|
|
@@ -2133,6 +2169,30 @@ mod object_ancestors_tests {
|
|
|
2133
2169
|
assert_constant_reference_to!(context, "Kernel::FOUND_ME", "file:///foo.rb:11:3-11:11");
|
|
2134
2170
|
assert_constant_reference_unresolved!(context, "FOUND_ME", "file:///foo.rb:14:6-14:14");
|
|
2135
2171
|
}
|
|
2172
|
+
|
|
2173
|
+
#[test]
|
|
2174
|
+
fn object_inherited_constant_inside_module_reordered() {
|
|
2175
|
+
let mut context = graph_test();
|
|
2176
|
+
context.index_uri("file:///foo.rb", {
|
|
2177
|
+
r"
|
|
2178
|
+
module Foo
|
|
2179
|
+
# Accessible via Object inheritance
|
|
2180
|
+
FOUND_ME
|
|
2181
|
+
end
|
|
2182
|
+
|
|
2183
|
+
module Kernel
|
|
2184
|
+
FOUND_ME = true
|
|
2185
|
+
end
|
|
2186
|
+
|
|
2187
|
+
class Object
|
|
2188
|
+
include Kernel
|
|
2189
|
+
end
|
|
2190
|
+
"
|
|
2191
|
+
});
|
|
2192
|
+
context.resolve();
|
|
2193
|
+
|
|
2194
|
+
assert_constant_reference_to!(context, "Kernel::FOUND_ME", "file:///foo.rb:3:3-3:11");
|
|
2195
|
+
}
|
|
2136
2196
|
}
|
|
2137
2197
|
|
|
2138
2198
|
mod singleton_ancestors_tests {
|
|
@@ -3034,7 +3094,7 @@ mod variable_tests {
|
|
|
3034
3094
|
|
|
3035
3095
|
assert_diagnostics_eq!(
|
|
3036
3096
|
&context,
|
|
3037
|
-
["
|
|
3097
|
+
["DynamicSingletonDefinition: Dynamic receiver for singleton method definition (2:3-4:6)",]
|
|
3038
3098
|
);
|
|
3039
3099
|
|
|
3040
3100
|
// Instance variable in method with unresolved receiver should not create a declaration
|
|
@@ -3097,13 +3157,14 @@ mod declaration_creation_tests {
|
|
|
3097
3157
|
#[test]
|
|
3098
3158
|
fn resolution_for_ambiguous_namespace_definitions() {
|
|
3099
3159
|
// Like many examples of Ruby code that is ambiguous to static analysis, this example is ambiguous due to
|
|
3100
|
-
// require order. If `
|
|
3101
|
-
// error or warning for a non existing constant.
|
|
3160
|
+
// require order. If `a_qualified.rb` is loaded first, then `Bar` doesn't exist, Ruby crashes and we should
|
|
3161
|
+
// emit an error or warning for a non existing constant.
|
|
3102
3162
|
//
|
|
3103
|
-
// If `
|
|
3104
|
-
// escaping the `Foo` nesting.
|
|
3163
|
+
// If `z_parent.rb` is loaded first, then `Bar` resolves to top level `Bar` and `Bar::Baz` is defined,
|
|
3164
|
+
// completely escaping the `Foo` nesting. The URIs intentionally sort in the opposite order: name complexity
|
|
3165
|
+
// must take precedence over URI so the top level `Bar` is resolved before the qualified `Bar::Baz` definition.
|
|
3105
3166
|
let mut context = graph_test();
|
|
3106
|
-
context.index_uri("file:///
|
|
3167
|
+
context.index_uri("file:///a_qualified.rb", {
|
|
3107
3168
|
r"
|
|
3108
3169
|
module Foo
|
|
3109
3170
|
class Bar::Baz
|
|
@@ -3111,7 +3172,7 @@ mod declaration_creation_tests {
|
|
|
3111
3172
|
end
|
|
3112
3173
|
"
|
|
3113
3174
|
});
|
|
3114
|
-
context.index_uri("file:///
|
|
3175
|
+
context.index_uri("file:///z_parent.rb", {
|
|
3115
3176
|
r"
|
|
3116
3177
|
module Bar
|
|
3117
3178
|
end
|
|
@@ -3123,62 +3184,11 @@ mod declaration_creation_tests {
|
|
|
3123
3184
|
|
|
3124
3185
|
assert_no_members!(context, "Foo");
|
|
3125
3186
|
assert_owner_eq!(context, "Foo", "Object");
|
|
3187
|
+
assert_declaration_does_not_exist!(context, "Foo::Bar");
|
|
3126
3188
|
|
|
3127
3189
|
assert_members_eq!(context, "Bar", ["Baz"]);
|
|
3128
3190
|
assert_owner_eq!(context, "Bar", "Object");
|
|
3129
3191
|
}
|
|
3130
|
-
|
|
3131
|
-
#[test]
|
|
3132
|
-
fn expected_name_depth_order() {
|
|
3133
|
-
let mut context = graph_test();
|
|
3134
|
-
context.index_uri("file:///foo.rb", {
|
|
3135
|
-
r"
|
|
3136
|
-
module Foo
|
|
3137
|
-
module Bar
|
|
3138
|
-
module Baz
|
|
3139
|
-
end
|
|
3140
|
-
|
|
3141
|
-
module ::Top
|
|
3142
|
-
class AfterTop
|
|
3143
|
-
end
|
|
3144
|
-
end
|
|
3145
|
-
end
|
|
3146
|
-
|
|
3147
|
-
module Qux::Zip
|
|
3148
|
-
module Zap
|
|
3149
|
-
class Zop::Boop
|
|
3150
|
-
end
|
|
3151
|
-
end
|
|
3152
|
-
end
|
|
3153
|
-
end
|
|
3154
|
-
"
|
|
3155
|
-
});
|
|
3156
|
-
|
|
3157
|
-
let depths = Resolver::compute_name_depths(context.graph().names());
|
|
3158
|
-
let mut names = context
|
|
3159
|
-
.graph()
|
|
3160
|
-
.names()
|
|
3161
|
-
.iter()
|
|
3162
|
-
.filter(|(_, n)| {
|
|
3163
|
-
!["Kernel", "BasicObject", "Object", "Module", "Class"]
|
|
3164
|
-
.contains(&context.graph().strings().get(n.str()).unwrap().as_str())
|
|
3165
|
-
})
|
|
3166
|
-
.collect::<Vec<_>>();
|
|
3167
|
-
assert_eq!(10, names.len());
|
|
3168
|
-
|
|
3169
|
-
names.sort_by_key(|(id, _)| depths.get(id).unwrap());
|
|
3170
|
-
|
|
3171
|
-
assert_eq!(
|
|
3172
|
-
[
|
|
3173
|
-
"Top", "Foo", "Bar", "Qux", "AfterTop", "Baz", "Zip", "Zap", "Zop", "Boop"
|
|
3174
|
-
],
|
|
3175
|
-
names
|
|
3176
|
-
.iter()
|
|
3177
|
-
.map(|(_, n)| context.graph().strings().get(n.str()).unwrap().as_str())
|
|
3178
|
-
.collect::<Vec<_>>()
|
|
3179
|
-
.as_slice()
|
|
3180
|
-
);
|
|
3181
|
-
}
|
|
3182
3192
|
}
|
|
3183
3193
|
|
|
3184
3194
|
mod singleton_class_tests {
|
|
@@ -3973,6 +3983,41 @@ mod todo_tests {
|
|
|
3973
3983
|
assert_members_eq!(context, "A::B::D", vec!["d_method()"]);
|
|
3974
3984
|
}
|
|
3975
3985
|
|
|
3986
|
+
#[test]
|
|
3987
|
+
fn have_linearized_ancestors() {
|
|
3988
|
+
// Todos are namespaces, so they must be the first entry of their own ancestor chain. Member lookups walk that
|
|
3989
|
+
// chain, so an empty one makes every member of the Todo unreachable
|
|
3990
|
+
let mut context = graph_test();
|
|
3991
|
+
context.index_uri("file:///a.rb", {
|
|
3992
|
+
r"
|
|
3993
|
+
class A::B::C
|
|
3994
|
+
def foo; end
|
|
3995
|
+
end
|
|
3996
|
+
"
|
|
3997
|
+
});
|
|
3998
|
+
context.index_uri("file:///d.rb", "class A::B::D < A::B::C; end");
|
|
3999
|
+
context.resolve();
|
|
4000
|
+
|
|
4001
|
+
assert_declaration_kind_eq!(context, "A", "<TODO>");
|
|
4002
|
+
assert_declaration_kind_eq!(context, "A::B", "<TODO>");
|
|
4003
|
+
assert_ancestors_eq!(context, "A", ["A"]);
|
|
4004
|
+
assert_ancestors_eq!(context, "A::B", ["A::B"]);
|
|
4005
|
+
|
|
4006
|
+
// Every member of a Todo is only reachable because the Todo lists itself in its own chain
|
|
4007
|
+
assert_members_eq!(context, "A", ["B"]);
|
|
4008
|
+
assert_members_eq!(context, "A::B", ["C", "D"]);
|
|
4009
|
+
|
|
4010
|
+
// Classes nested in a Todo still inherit from each other. Both the superclass path `A::B::C` and `D`'s own
|
|
4011
|
+
// owner chain go through the Todos
|
|
4012
|
+
assert_ancestors_eq!(
|
|
4013
|
+
context,
|
|
4014
|
+
"A::B::D",
|
|
4015
|
+
["A::B::D", "A::B::C", "Object", "Kernel", "BasicObject"]
|
|
4016
|
+
);
|
|
4017
|
+
assert_owner_eq!(context, "A::B::D", "A::B");
|
|
4018
|
+
assert_descendants!(context, "A::B::C", ["A::B::D"]);
|
|
4019
|
+
}
|
|
4020
|
+
|
|
3976
4021
|
#[test]
|
|
3977
4022
|
fn promoted_incrementally() {
|
|
3978
4023
|
// Index class A::B::C first (creates Todos), then provide real definitions.
|
|
@@ -4535,6 +4580,7 @@ mod promotability_tests {
|
|
|
4535
4580
|
|
|
4536
4581
|
context.resolve();
|
|
4537
4582
|
assert_no_diagnostics!(&context);
|
|
4583
|
+
assert_declaration_kind_eq!(context, "Qux", "<TODO>");
|
|
4538
4584
|
assert_declaration_exists!(context, "Qux::<Qux>");
|
|
4539
4585
|
}
|
|
4540
4586
|
|
|
@@ -4570,9 +4616,8 @@ mod promotability_tests {
|
|
|
4570
4616
|
|
|
4571
4617
|
#[test]
|
|
4572
4618
|
fn promoted_constant_has_correct_ancestors() {
|
|
4573
|
-
// When a promotable constant is auto-promoted via singleton class access,
|
|
4574
|
-
//
|
|
4575
|
-
// Modules don't inherit from Object.
|
|
4619
|
+
// When a promotable constant is auto-promoted via singleton class access, its kind remains unknown.
|
|
4620
|
+
// Todos do not inherit from Object.
|
|
4576
4621
|
let mut context = graph_test();
|
|
4577
4622
|
context.index_uri("file:///foo.rb", {
|
|
4578
4623
|
r"
|
|
@@ -4583,6 +4628,7 @@ mod promotability_tests {
|
|
|
4583
4628
|
|
|
4584
4629
|
context.resolve();
|
|
4585
4630
|
assert_no_diagnostics!(&context);
|
|
4631
|
+
assert_declaration_kind_eq!(context, "Foo", "<TODO>");
|
|
4586
4632
|
assert_ancestors_eq!(context, "Foo", ["Foo"]);
|
|
4587
4633
|
}
|
|
4588
4634
|
|
|
@@ -4633,8 +4679,8 @@ mod promotability_tests {
|
|
|
4633
4679
|
});
|
|
4634
4680
|
|
|
4635
4681
|
context.resolve();
|
|
4636
|
-
assert_declaration_kind_eq!(context, "Foo", "
|
|
4637
|
-
assert_declaration_kind_eq!(context, "Foo::Bar", "
|
|
4682
|
+
assert_declaration_kind_eq!(context, "Foo", "<TODO>");
|
|
4683
|
+
assert_declaration_kind_eq!(context, "Foo::Bar", "<TODO>");
|
|
4638
4684
|
assert_declaration_kind_eq!(context, "Foo::Bar::Baz", "Constant");
|
|
4639
4685
|
}
|
|
4640
4686
|
|
|
@@ -4652,7 +4698,7 @@ mod promotability_tests {
|
|
|
4652
4698
|
});
|
|
4653
4699
|
|
|
4654
4700
|
context.resolve();
|
|
4655
|
-
assert_declaration_kind_eq!(context, "Foo", "
|
|
4701
|
+
assert_declaration_kind_eq!(context, "Foo", "<TODO>");
|
|
4656
4702
|
assert_declaration_exists!(context, "Foo::<Foo>#bar()");
|
|
4657
4703
|
}
|
|
4658
4704
|
|
|
@@ -4675,6 +4721,131 @@ mod promotability_tests {
|
|
|
4675
4721
|
assert_declaration_does_not_exist!(context, "Foo::<Foo>#bar()");
|
|
4676
4722
|
}
|
|
4677
4723
|
|
|
4724
|
+
#[test]
|
|
4725
|
+
fn rbs_constant_is_promotable_for_singleton_call() {
|
|
4726
|
+
let mut context = graph_test();
|
|
4727
|
+
context.index_rbs_uri("file:///env.rbs", "ENV: untyped");
|
|
4728
|
+
context.index_uri("file:///env.rb", {
|
|
4729
|
+
r#"
|
|
4730
|
+
ENV.fetch("FEATURE")
|
|
4731
|
+
"#
|
|
4732
|
+
});
|
|
4733
|
+
context.resolve();
|
|
4734
|
+
|
|
4735
|
+
assert_no_diagnostics!(&context);
|
|
4736
|
+
assert_declaration_exists!(context, "ENV::<ENV>");
|
|
4737
|
+
|
|
4738
|
+
let fetch = context
|
|
4739
|
+
.graph()
|
|
4740
|
+
.method_references()
|
|
4741
|
+
.values()
|
|
4742
|
+
.find(|method| *method.str() == "fetch".into())
|
|
4743
|
+
.expect("ENV.fetch should be indexed");
|
|
4744
|
+
let receiver_name_id = fetch.receiver().expect("ENV.fetch should have a receiver");
|
|
4745
|
+
let NameRef::Resolved(receiver) = context.graph().names().get(&receiver_name_id).unwrap() else {
|
|
4746
|
+
panic!("ENV.fetch receiver should resolve");
|
|
4747
|
+
};
|
|
4748
|
+
assert_eq!(*receiver.declaration_id(), DeclarationId::from("ENV::<ENV>"));
|
|
4749
|
+
}
|
|
4750
|
+
|
|
4751
|
+
#[test]
|
|
4752
|
+
fn singleton_on_todo_is_preserved_and_relinearized_when_promoted_to_class() {
|
|
4753
|
+
let mut context = graph_test();
|
|
4754
|
+
context.index_uri("file:///placeholder.rb", "class Foo::Placeholder; end");
|
|
4755
|
+
context.resolve();
|
|
4756
|
+
|
|
4757
|
+
assert_declaration_kind_eq!(context, "Foo", "<TODO>");
|
|
4758
|
+
|
|
4759
|
+
context.index_uri("file:///value.rb", "Foo.call");
|
|
4760
|
+
context.resolve();
|
|
4761
|
+
|
|
4762
|
+
assert_singleton_class_eq!(context, "Foo", "Foo::<Foo>");
|
|
4763
|
+
|
|
4764
|
+
context.index_uri("file:///class.rb", "class Foo; end");
|
|
4765
|
+
context.resolve();
|
|
4766
|
+
|
|
4767
|
+
assert_declaration_kind_eq!(context, "Foo", "Class");
|
|
4768
|
+
assert_singleton_class_eq!(context, "Foo", "Foo::<Foo>");
|
|
4769
|
+
assert_ancestors_eq!(
|
|
4770
|
+
context,
|
|
4771
|
+
"Foo::<Foo>",
|
|
4772
|
+
[
|
|
4773
|
+
"Foo::<Foo>",
|
|
4774
|
+
"Object::<Object>",
|
|
4775
|
+
"BasicObject::<BasicObject>",
|
|
4776
|
+
"Class",
|
|
4777
|
+
"Module",
|
|
4778
|
+
"Object",
|
|
4779
|
+
"Kernel",
|
|
4780
|
+
"BasicObject"
|
|
4781
|
+
]
|
|
4782
|
+
);
|
|
4783
|
+
}
|
|
4784
|
+
|
|
4785
|
+
#[test]
|
|
4786
|
+
fn singleton_on_todo_is_preserved_when_promoted_to_module() {
|
|
4787
|
+
let mut context = graph_test();
|
|
4788
|
+
context.index_uri("file:///placeholder.rb", "class Bar::Placeholder; end");
|
|
4789
|
+
context.resolve();
|
|
4790
|
+
|
|
4791
|
+
assert_declaration_kind_eq!(context, "Bar", "<TODO>");
|
|
4792
|
+
|
|
4793
|
+
context.index_uri("file:///value.rb", "Bar.call");
|
|
4794
|
+
context.resolve();
|
|
4795
|
+
|
|
4796
|
+
assert_singleton_class_eq!(context, "Bar", "Bar::<Bar>");
|
|
4797
|
+
|
|
4798
|
+
context.index_uri("file:///module.rb", "module Bar; end");
|
|
4799
|
+
context.resolve();
|
|
4800
|
+
|
|
4801
|
+
assert_declaration_kind_eq!(context, "Bar", "Module");
|
|
4802
|
+
assert_singleton_class_eq!(context, "Bar", "Bar::<Bar>");
|
|
4803
|
+
assert_ancestors_eq!(
|
|
4804
|
+
context,
|
|
4805
|
+
"Bar::<Bar>",
|
|
4806
|
+
["Bar::<Bar>", "Module", "Object", "Kernel", "BasicObject"]
|
|
4807
|
+
);
|
|
4808
|
+
}
|
|
4809
|
+
|
|
4810
|
+
#[test]
|
|
4811
|
+
fn higher_order_singleton_on_todo_is_relinearized_when_promoted_to_class() {
|
|
4812
|
+
let mut context = graph_test();
|
|
4813
|
+
context.index_uri("file:///placeholder.rb", "class Foo::Placeholder; end");
|
|
4814
|
+
context.resolve();
|
|
4815
|
+
|
|
4816
|
+
context.index_uri("file:///value.rb", {
|
|
4817
|
+
r"
|
|
4818
|
+
class << Foo
|
|
4819
|
+
class << self
|
|
4820
|
+
end
|
|
4821
|
+
end
|
|
4822
|
+
"
|
|
4823
|
+
});
|
|
4824
|
+
context.resolve();
|
|
4825
|
+
|
|
4826
|
+
context.index_uri("file:///class.rb", "class Foo; end");
|
|
4827
|
+
context.resolve();
|
|
4828
|
+
|
|
4829
|
+
assert_ancestors_eq!(
|
|
4830
|
+
context,
|
|
4831
|
+
"Foo::<Foo>::<<Foo>>",
|
|
4832
|
+
[
|
|
4833
|
+
"Foo::<Foo>::<<Foo>>",
|
|
4834
|
+
"Object::<Object>::<<Object>>",
|
|
4835
|
+
"BasicObject::<BasicObject>::<<BasicObject>>",
|
|
4836
|
+
"Class::<Class>",
|
|
4837
|
+
"Module::<Module>",
|
|
4838
|
+
"Object::<Object>",
|
|
4839
|
+
"BasicObject::<BasicObject>",
|
|
4840
|
+
"Class",
|
|
4841
|
+
"Module",
|
|
4842
|
+
"Object",
|
|
4843
|
+
"Kernel",
|
|
4844
|
+
"BasicObject"
|
|
4845
|
+
]
|
|
4846
|
+
);
|
|
4847
|
+
}
|
|
4848
|
+
|
|
4678
4849
|
#[test]
|
|
4679
4850
|
fn ivar_defined_inside_of_undefined_alias_namespace() {
|
|
4680
4851
|
let mut context = graph_test();
|
|
@@ -4882,6 +5053,67 @@ mod rbs_tests {
|
|
|
4882
5053
|
assert_declaration_exists!(context, "Foo::Bar::Baz");
|
|
4883
5054
|
}
|
|
4884
5055
|
|
|
5056
|
+
#[test]
|
|
5057
|
+
fn rbs_qualified_mixin_resolves_through_parent_scope() {
|
|
5058
|
+
let mut context = graph_test();
|
|
5059
|
+
context.index_rbs_uri("file:///minitest.rbs", {
|
|
5060
|
+
r"
|
|
5061
|
+
module Minitest
|
|
5062
|
+
module Guard
|
|
5063
|
+
end
|
|
5064
|
+
|
|
5065
|
+
module Assertions
|
|
5066
|
+
end
|
|
5067
|
+
|
|
5068
|
+
class Runnable
|
|
5069
|
+
end
|
|
5070
|
+
end
|
|
5071
|
+
"
|
|
5072
|
+
});
|
|
5073
|
+
context.index_rbs_uri("file:///minitest/test.rbs", {
|
|
5074
|
+
r"
|
|
5075
|
+
class Minitest::Test < ::Minitest::Runnable
|
|
5076
|
+
include Minitest::Assertions
|
|
5077
|
+
include Minitest::Guard
|
|
5078
|
+
extend Minitest::Guard
|
|
5079
|
+
end
|
|
5080
|
+
"
|
|
5081
|
+
});
|
|
5082
|
+
context.resolve();
|
|
5083
|
+
|
|
5084
|
+
assert_no_diagnostics!(&context);
|
|
5085
|
+
|
|
5086
|
+
assert_ancestors_eq!(
|
|
5087
|
+
context,
|
|
5088
|
+
"Minitest::Test",
|
|
5089
|
+
[
|
|
5090
|
+
"Minitest::Test",
|
|
5091
|
+
"Minitest::Guard",
|
|
5092
|
+
"Minitest::Assertions",
|
|
5093
|
+
"Minitest::Runnable",
|
|
5094
|
+
"Object",
|
|
5095
|
+
"Kernel",
|
|
5096
|
+
"BasicObject"
|
|
5097
|
+
]
|
|
5098
|
+
);
|
|
5099
|
+
assert_ancestors_eq!(
|
|
5100
|
+
context,
|
|
5101
|
+
"Minitest::Test::<Test>",
|
|
5102
|
+
[
|
|
5103
|
+
"Minitest::Test::<Test>",
|
|
5104
|
+
"Minitest::Guard",
|
|
5105
|
+
"Minitest::Runnable::<Runnable>",
|
|
5106
|
+
"Object::<Object>",
|
|
5107
|
+
"BasicObject::<BasicObject>",
|
|
5108
|
+
"Class",
|
|
5109
|
+
"Module",
|
|
5110
|
+
"Object",
|
|
5111
|
+
"Kernel",
|
|
5112
|
+
"BasicObject"
|
|
5113
|
+
]
|
|
5114
|
+
);
|
|
5115
|
+
}
|
|
5116
|
+
|
|
4885
5117
|
#[test]
|
|
4886
5118
|
fn rbs_qualified_name_inside_nested_module() {
|
|
4887
5119
|
let mut context = graph_test();
|
|
@@ -4986,6 +5218,38 @@ mod rbs_tests {
|
|
|
4986
5218
|
);
|
|
4987
5219
|
}
|
|
4988
5220
|
|
|
5221
|
+
#[test]
|
|
5222
|
+
fn rbs_attributes_create_method_declarations_without_instance_variable_declarations() {
|
|
5223
|
+
let mut context = graph_test();
|
|
5224
|
+
context.index_rbs_uri("file:///attributes.rbs", {
|
|
5225
|
+
r"
|
|
5226
|
+
class Foo
|
|
5227
|
+
attr_reader reader: String
|
|
5228
|
+
attr_writer writer (@writer): Integer
|
|
5229
|
+
attr_accessor accessor(): bool
|
|
5230
|
+
private attr_accessor self.class_value (@class_value): Symbol
|
|
5231
|
+
end
|
|
5232
|
+
"
|
|
5233
|
+
});
|
|
5234
|
+
context.resolve();
|
|
5235
|
+
|
|
5236
|
+
assert_no_diagnostics!(&context);
|
|
5237
|
+
for method in [
|
|
5238
|
+
"Foo#reader()",
|
|
5239
|
+
"Foo#writer=()",
|
|
5240
|
+
"Foo#accessor()",
|
|
5241
|
+
"Foo#accessor=()",
|
|
5242
|
+
"Foo::<Foo>#class_value()",
|
|
5243
|
+
"Foo::<Foo>#class_value=()",
|
|
5244
|
+
] {
|
|
5245
|
+
assert_declaration_exists!(context, method);
|
|
5246
|
+
assert_declaration_kind_eq!(context, method, "Method");
|
|
5247
|
+
}
|
|
5248
|
+
for instance_variable in ["Foo#@reader", "Foo#@writer", "Foo::<Foo>#@class_value"] {
|
|
5249
|
+
assert_declaration_does_not_exist!(context, instance_variable);
|
|
5250
|
+
}
|
|
5251
|
+
}
|
|
5252
|
+
|
|
4989
5253
|
#[test]
|
|
4990
5254
|
fn rbs_mixin_resolution() {
|
|
4991
5255
|
let mut context = graph_test();
|
|
@@ -5225,8 +5489,9 @@ mod visibility_resolution_tests {
|
|
|
5225
5489
|
assert_diagnostics_eq!(
|
|
5226
5490
|
context,
|
|
5227
5491
|
&[
|
|
5228
|
-
"
|
|
5229
|
-
]
|
|
5492
|
+
"UndefinedMethodVisibilityTarget: undefined method `Foo#nonexistent()` for visibility change (2:12-2:23)"
|
|
5493
|
+
],
|
|
5494
|
+
severity: Severity::Warning
|
|
5230
5495
|
);
|
|
5231
5496
|
}
|
|
5232
5497
|
|
|
@@ -5360,9 +5625,10 @@ mod visibility_resolution_tests {
|
|
|
5360
5625
|
assert_diagnostics_eq!(
|
|
5361
5626
|
context,
|
|
5362
5627
|
&[
|
|
5363
|
-
"
|
|
5364
|
-
"
|
|
5365
|
-
]
|
|
5628
|
+
"UndefinedConstantVisibilityTarget: undefined constant `NOPE_ONE` for visibility change in `Foo` (2:21-2:29)",
|
|
5629
|
+
"UndefinedConstantVisibilityTarget: undefined constant `NOPE_TWO` for visibility change in `Foo` (2:32-2:40)",
|
|
5630
|
+
],
|
|
5631
|
+
severity: Severity::Warning
|
|
5366
5632
|
);
|
|
5367
5633
|
}
|
|
5368
5634
|
|
|
@@ -5386,7 +5652,7 @@ mod visibility_resolution_tests {
|
|
|
5386
5652
|
assert_diagnostics_eq!(
|
|
5387
5653
|
context,
|
|
5388
5654
|
&[
|
|
5389
|
-
"
|
|
5655
|
+
"UndefinedConstantVisibilityTarget: undefined constant `CONST` for visibility change in `Child` (6:21-6:26)"
|
|
5390
5656
|
]
|
|
5391
5657
|
);
|
|
5392
5658
|
assert_visibility_eq!(context, "Parent::CONST", Visibility::Public);
|
|
@@ -5528,7 +5794,7 @@ mod visibility_resolution_tests {
|
|
|
5528
5794
|
assert_diagnostics_eq!(
|
|
5529
5795
|
context,
|
|
5530
5796
|
&[
|
|
5531
|
-
"
|
|
5797
|
+
"UndefinedMethodVisibilityTarget: undefined method `Foo::<Foo>#nonexistent()` for visibility change (2:25-2:36)"
|
|
5532
5798
|
]
|
|
5533
5799
|
);
|
|
5534
5800
|
}
|
|
@@ -5556,7 +5822,7 @@ mod visibility_resolution_tests {
|
|
|
5556
5822
|
assert_diagnostics_eq!(
|
|
5557
5823
|
context,
|
|
5558
5824
|
&[
|
|
5559
|
-
"
|
|
5825
|
+
"UndefinedMethodVisibilityTarget: undefined method `Foo::<Foo>#missing()` for visibility change (2:25-2:32)"
|
|
5560
5826
|
]
|
|
5561
5827
|
);
|
|
5562
5828
|
|
|
@@ -5582,7 +5848,7 @@ mod visibility_resolution_tests {
|
|
|
5582
5848
|
assert_diagnostics_eq!(
|
|
5583
5849
|
context,
|
|
5584
5850
|
&[
|
|
5585
|
-
"
|
|
5851
|
+
"UndefinedMethodVisibilityTarget: undefined method `Foo::<Foo>#missing()` for visibility change (2:25-2:32)"
|
|
5586
5852
|
]
|
|
5587
5853
|
);
|
|
5588
5854
|
|
|
@@ -5603,7 +5869,7 @@ mod visibility_resolution_tests {
|
|
|
5603
5869
|
|
|
5604
5870
|
#[test]
|
|
5605
5871
|
fn retroactive_singleton_method_visibility_inline_def() {
|
|
5606
|
-
let mut context =
|
|
5872
|
+
let mut context = graph_test();
|
|
5607
5873
|
context.index_uri(
|
|
5608
5874
|
"file:///foo.rb",
|
|
5609
5875
|
r"
|
|
@@ -5620,7 +5886,7 @@ mod visibility_resolution_tests {
|
|
|
5620
5886
|
|
|
5621
5887
|
#[test]
|
|
5622
5888
|
fn retroactive_singleton_method_visibility_array_form() {
|
|
5623
|
-
let mut context =
|
|
5889
|
+
let mut context = graph_test();
|
|
5624
5890
|
context.index_uri(
|
|
5625
5891
|
"file:///foo.rb",
|
|
5626
5892
|
r#"
|
|
@@ -5641,4 +5907,164 @@ mod visibility_resolution_tests {
|
|
|
5641
5907
|
assert_visibility_eq!(context, "Foo::<Foo>#b()", Visibility::Private);
|
|
5642
5908
|
assert_visibility_eq!(context, "Foo::<Foo>#c()", Visibility::Public);
|
|
5643
5909
|
}
|
|
5910
|
+
|
|
5911
|
+
#[test]
|
|
5912
|
+
fn inline_module_function() {
|
|
5913
|
+
let mut context = graph_test();
|
|
5914
|
+
context.index_uri(
|
|
5915
|
+
"file:///foo.rb",
|
|
5916
|
+
r"
|
|
5917
|
+
module Foo
|
|
5918
|
+
module_function def bar; end
|
|
5919
|
+
end
|
|
5920
|
+
",
|
|
5921
|
+
);
|
|
5922
|
+
context.resolve();
|
|
5923
|
+
|
|
5924
|
+
assert_no_diagnostics!(&context);
|
|
5925
|
+
assert_visibility_eq!(context, "Foo#bar()", Visibility::Private);
|
|
5926
|
+
assert_visibility_eq!(context, "Foo::<Foo>#bar()", Visibility::Public);
|
|
5927
|
+
}
|
|
5928
|
+
|
|
5929
|
+
#[test]
|
|
5930
|
+
fn inline_module_function_singleton_companion_visibility_can_be_changed() {
|
|
5931
|
+
let mut context = graph_test();
|
|
5932
|
+
context.index_uri(
|
|
5933
|
+
"file:///foo.rb",
|
|
5934
|
+
r"
|
|
5935
|
+
module Foo
|
|
5936
|
+
module_function def bar; end
|
|
5937
|
+
|
|
5938
|
+
class << self
|
|
5939
|
+
private :bar
|
|
5940
|
+
end
|
|
5941
|
+
end
|
|
5942
|
+
",
|
|
5943
|
+
);
|
|
5944
|
+
context.resolve();
|
|
5945
|
+
|
|
5946
|
+
assert_no_diagnostics!(&context);
|
|
5947
|
+
assert_visibility_eq!(context, "Foo#bar()", Visibility::Private);
|
|
5948
|
+
assert_visibility_eq!(context, "Foo::<Foo>#bar()", Visibility::Private);
|
|
5949
|
+
}
|
|
5950
|
+
|
|
5951
|
+
#[test]
|
|
5952
|
+
fn retroactive_module_function() {
|
|
5953
|
+
let mut context = graph_test();
|
|
5954
|
+
context.index_uri(
|
|
5955
|
+
"file:///foo.rb",
|
|
5956
|
+
r"
|
|
5957
|
+
module Foo
|
|
5958
|
+
def bar; end
|
|
5959
|
+
module_function :bar
|
|
5960
|
+
end
|
|
5961
|
+
",
|
|
5962
|
+
);
|
|
5963
|
+
context.resolve();
|
|
5964
|
+
|
|
5965
|
+
assert_no_diagnostics!(&context);
|
|
5966
|
+
assert_visibility_eq!(context, "Foo#bar()", Visibility::Private);
|
|
5967
|
+
assert_visibility_eq!(context, "Foo::<Foo>#bar()", Visibility::Public);
|
|
5968
|
+
|
|
5969
|
+
// Instance: Method def + MethodVisibility def
|
|
5970
|
+
assert_declaration_definitions_count_eq!(context, "Foo#bar()", 2);
|
|
5971
|
+
// Singleton companion: only the MethodVisibility def is attached
|
|
5972
|
+
assert_declaration_definitions_count_eq!(context, "Foo::<Foo>#bar()", 1);
|
|
5973
|
+
}
|
|
5974
|
+
|
|
5975
|
+
#[test]
|
|
5976
|
+
fn retroactive_module_function_undefined_target_emits_single_diagnostic() {
|
|
5977
|
+
let mut context = graph_test();
|
|
5978
|
+
context.index_uri(
|
|
5979
|
+
"file:///foo.rb",
|
|
5980
|
+
r"
|
|
5981
|
+
module Foo
|
|
5982
|
+
module_function :missing
|
|
5983
|
+
end
|
|
5984
|
+
",
|
|
5985
|
+
);
|
|
5986
|
+
context.resolve();
|
|
5987
|
+
|
|
5988
|
+
// The singleton-side def stays silent; only the instance-side def emits.
|
|
5989
|
+
assert_diagnostics_eq!(
|
|
5990
|
+
context,
|
|
5991
|
+
&["UndefinedMethodVisibilityTarget: undefined method `Foo#missing()` for visibility change (2:20-2:27)"]
|
|
5992
|
+
);
|
|
5993
|
+
assert_declaration_does_not_exist!(context, "Foo::<Foo>#missing()");
|
|
5994
|
+
assert_declaration_does_not_exist!(context, "Foo::<Foo>");
|
|
5995
|
+
}
|
|
5996
|
+
|
|
5997
|
+
#[test]
|
|
5998
|
+
fn retroactive_module_function_singleton_companion_cleared_when_removed_multi_file() {
|
|
5999
|
+
let mut context = graph_test();
|
|
6000
|
+
context.index_uri(
|
|
6001
|
+
"file:///a.rb",
|
|
6002
|
+
r"
|
|
6003
|
+
module Foo
|
|
6004
|
+
def bar; end
|
|
6005
|
+
module_function :bar
|
|
6006
|
+
end
|
|
6007
|
+
",
|
|
6008
|
+
);
|
|
6009
|
+
context.index_uri(
|
|
6010
|
+
"file:///b.rb",
|
|
6011
|
+
r"
|
|
6012
|
+
module Foo
|
|
6013
|
+
def baz; end
|
|
6014
|
+
end
|
|
6015
|
+
",
|
|
6016
|
+
);
|
|
6017
|
+
context.resolve();
|
|
6018
|
+
|
|
6019
|
+
assert_visibility_eq!(context, "Foo::<Foo>#bar()", Visibility::Public);
|
|
6020
|
+
|
|
6021
|
+
// Re-index a.rb without `module_function :bar`. Foo still has b.rb's Module def, so the
|
|
6022
|
+
// namespace stays alive and no cascade through Foo's singleton class runs. The singleton
|
|
6023
|
+
// companion must still be cleaned up via reverse-lookup invalidation of its own def.
|
|
6024
|
+
context.index_uri(
|
|
6025
|
+
"file:///a.rb",
|
|
6026
|
+
r"
|
|
6027
|
+
module Foo
|
|
6028
|
+
def bar; end
|
|
6029
|
+
end
|
|
6030
|
+
",
|
|
6031
|
+
);
|
|
6032
|
+
context.resolve();
|
|
6033
|
+
|
|
6034
|
+
assert_no_diagnostics!(&context);
|
|
6035
|
+
assert_visibility_eq!(context, "Foo#bar()", Visibility::Public);
|
|
6036
|
+
assert_declaration_does_not_exist!(context, "Foo::<Foo>#bar()");
|
|
6037
|
+
}
|
|
6038
|
+
|
|
6039
|
+
#[test]
|
|
6040
|
+
fn retroactive_module_function_singleton_companion_cleared_when_removed() {
|
|
6041
|
+
let mut context = graph_test();
|
|
6042
|
+
context.index_uri(
|
|
6043
|
+
"file:///foo.rb",
|
|
6044
|
+
r"
|
|
6045
|
+
module Foo
|
|
6046
|
+
def bar; end
|
|
6047
|
+
module_function :bar
|
|
6048
|
+
end
|
|
6049
|
+
",
|
|
6050
|
+
);
|
|
6051
|
+
context.resolve();
|
|
6052
|
+
|
|
6053
|
+
assert_visibility_eq!(context, "Foo::<Foo>#bar()", Visibility::Public);
|
|
6054
|
+
|
|
6055
|
+
context.index_uri(
|
|
6056
|
+
"file:///foo.rb",
|
|
6057
|
+
r"
|
|
6058
|
+
module Foo
|
|
6059
|
+
def bar; end
|
|
6060
|
+
end
|
|
6061
|
+
",
|
|
6062
|
+
);
|
|
6063
|
+
context.resolve();
|
|
6064
|
+
|
|
6065
|
+
assert_no_diagnostics!(&context);
|
|
6066
|
+
assert_visibility_eq!(context, "Foo#bar()", Visibility::Public);
|
|
6067
|
+
assert_declaration_does_not_exist!(context, "Foo::<Foo>#bar()");
|
|
6068
|
+
assert_declaration_does_not_exist!(context, "Foo::<Foo>");
|
|
6069
|
+
}
|
|
5644
6070
|
}
|