rubydex 0.3.0 → 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 +45 -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/diagnostic.c +75 -1
- data/ext/rubydex/diagnostic.h +2 -0
- data/ext/rubydex/graph.c +13 -45
- 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 +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 +558 -17
- data/rust/Cargo.lock +2 -2
- data/rust/rubydex/Cargo.toml +1 -1
- data/rust/rubydex/benches/graph_memory.rs +3 -5
- 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 +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 +123 -43
- 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/diagnostic_api.rs +77 -8
- data/rust/rubydex-sys/src/graph_api.rs +13 -194
- data/rust/rubydex-sys/src/lib.rs +2 -0
- data/rust/rubydex-sys/src/name_api.rs +2 -6
- data/rust/rubydex-sys/src/utils.rs +37 -0
- data/skills/send-private-method/SKILL.md +133 -0
- metadata +31 -2
|
@@ -1,7 +1,4 @@
|
|
|
1
|
-
use std::{
|
|
2
|
-
collections::{HashSet, VecDeque, hash_map::Entry},
|
|
3
|
-
hash::BuildHasher,
|
|
4
|
-
};
|
|
1
|
+
use std::collections::{HashSet, VecDeque, hash_map::Entry};
|
|
5
2
|
|
|
6
3
|
use crate::diagnostic::{Diagnostic, Rule};
|
|
7
4
|
use crate::model::{
|
|
@@ -51,11 +48,32 @@ enum SingletonAncestors {
|
|
|
51
48
|
Deferred,
|
|
52
49
|
}
|
|
53
50
|
|
|
51
|
+
#[derive(Default)]
|
|
52
|
+
struct ChainState {
|
|
53
|
+
cyclic: bool,
|
|
54
|
+
partial: bool,
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
impl ChainState {
|
|
58
|
+
/// Fold a nested linearization's result into this frame's state, returning its ancestor list
|
|
59
|
+
fn fold(&mut self, ancestors: Ancestors) -> Vec<Ancestor> {
|
|
60
|
+
match ancestors {
|
|
61
|
+
Ancestors::Complete(ids) => ids,
|
|
62
|
+
Ancestors::Cyclic(ids) => {
|
|
63
|
+
self.cyclic = true;
|
|
64
|
+
ids
|
|
65
|
+
}
|
|
66
|
+
Ancestors::Partial(ids) => {
|
|
67
|
+
self.partial = true;
|
|
68
|
+
ids
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
54
74
|
struct LinearizationContext {
|
|
55
75
|
descendants: IdentityHashSet<DeclarationId>,
|
|
56
76
|
seen_ids: IdentityHashSet<DeclarationId>,
|
|
57
|
-
cyclic: bool,
|
|
58
|
-
partial: bool,
|
|
59
77
|
}
|
|
60
78
|
|
|
61
79
|
impl LinearizationContext {
|
|
@@ -63,8 +81,6 @@ impl LinearizationContext {
|
|
|
63
81
|
Self {
|
|
64
82
|
descendants: IdentityHashSet::default(),
|
|
65
83
|
seen_ids: IdentityHashSet::default(),
|
|
66
|
-
cyclic: false,
|
|
67
|
-
partial: false,
|
|
68
84
|
}
|
|
69
85
|
}
|
|
70
86
|
|
|
@@ -83,6 +99,8 @@ pub struct Resolver<'a> {
|
|
|
83
99
|
unit_queue: VecDeque<Unit>,
|
|
84
100
|
/// Whether we made any progress in the last pass of the resolution loop
|
|
85
101
|
made_progress: bool,
|
|
102
|
+
/// The linearization context carried across recursion
|
|
103
|
+
context: LinearizationContext,
|
|
86
104
|
}
|
|
87
105
|
|
|
88
106
|
impl<'a> Resolver<'a> {
|
|
@@ -91,6 +109,7 @@ impl<'a> Resolver<'a> {
|
|
|
91
109
|
graph,
|
|
92
110
|
unit_queue: VecDeque::new(),
|
|
93
111
|
made_progress: false,
|
|
112
|
+
context: LinearizationContext::new(),
|
|
94
113
|
}
|
|
95
114
|
}
|
|
96
115
|
|
|
@@ -229,12 +248,38 @@ impl<'a> Resolver<'a> {
|
|
|
229
248
|
Outcome::Resolved(id) => {
|
|
230
249
|
if needs_linearization {
|
|
231
250
|
self.unit_queue.push_back(Unit::Ancestors(id));
|
|
251
|
+
self.relinearize_singleton_hierarchy(id);
|
|
232
252
|
}
|
|
233
253
|
self.made_progress = true;
|
|
234
254
|
}
|
|
235
255
|
}
|
|
236
256
|
}
|
|
237
257
|
|
|
258
|
+
/// Clears and re-enqueues the ancestors of a declaration's singleton class and all of its higher-order singleton
|
|
259
|
+
/// classes. A later class or module definition can change their parent hierarchy.
|
|
260
|
+
fn relinearize_singleton_hierarchy(&mut self, declaration_id: DeclarationId) {
|
|
261
|
+
let Some(singleton_id) = self
|
|
262
|
+
.graph
|
|
263
|
+
.declarations()
|
|
264
|
+
.get(&declaration_id)
|
|
265
|
+
.and_then(|declaration| declaration.as_namespace())
|
|
266
|
+
.and_then(|namespace| namespace.singleton_class())
|
|
267
|
+
.copied()
|
|
268
|
+
else {
|
|
269
|
+
return;
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
self.graph
|
|
273
|
+
.declarations_mut()
|
|
274
|
+
.get_mut(&singleton_id)
|
|
275
|
+
.unwrap()
|
|
276
|
+
.as_namespace_mut()
|
|
277
|
+
.unwrap()
|
|
278
|
+
.clear_ancestors();
|
|
279
|
+
self.unit_queue.push_back(Unit::Ancestors(singleton_id));
|
|
280
|
+
self.relinearize_singleton_hierarchy(singleton_id);
|
|
281
|
+
}
|
|
282
|
+
|
|
238
283
|
/// Handles a unit of work for resolving a constant reference
|
|
239
284
|
fn handle_reference_unit(&mut self, unit_id: Unit, id: ConstantReferenceId) {
|
|
240
285
|
let constant_ref = self.graph.constant_references().get(&id).unwrap();
|
|
@@ -257,7 +302,7 @@ impl<'a> Resolver<'a> {
|
|
|
257
302
|
|
|
258
303
|
/// Handles a unit of work for linearizing ancestors of a declaration
|
|
259
304
|
fn handle_ancestor_unit(&mut self, id: DeclarationId) {
|
|
260
|
-
match self.
|
|
305
|
+
match self.linearize_ancestors(id) {
|
|
261
306
|
Ancestors::Complete(_) | Ancestors::Cyclic(_) => {
|
|
262
307
|
// We succeeded in some capacity this time
|
|
263
308
|
self.made_progress = true;
|
|
@@ -928,7 +973,7 @@ impl<'a> Resolver<'a> {
|
|
|
928
973
|
fn schedule_singleton_ancestors(&mut self, id: DeclarationId, mode: SingletonAncestors) {
|
|
929
974
|
match mode {
|
|
930
975
|
SingletonAncestors::Eager => {
|
|
931
|
-
let _ = self.
|
|
976
|
+
let _ = self.linearize_ancestors(id);
|
|
932
977
|
}
|
|
933
978
|
SingletonAncestors::Enqueue => {
|
|
934
979
|
self.unit_queue.push_back(Unit::Ancestors(id));
|
|
@@ -945,35 +990,24 @@ impl<'a> Resolver<'a> {
|
|
|
945
990
|
///
|
|
946
991
|
/// Can panic if there's inconsistent data in the graph
|
|
947
992
|
#[must_use]
|
|
948
|
-
fn
|
|
949
|
-
let mut context = LinearizationContext::new();
|
|
950
|
-
self.linearize_ancestors(declaration_id, &mut context)
|
|
951
|
-
}
|
|
952
|
-
|
|
953
|
-
/// Linearizes the ancestors of a declaration, returning the list of ancestor declaration IDs
|
|
954
|
-
///
|
|
955
|
-
/// # Panics
|
|
956
|
-
///
|
|
957
|
-
/// Can panic if there's inconsistent data in the graph
|
|
958
|
-
#[must_use]
|
|
959
|
-
fn linearize_ancestors(&mut self, declaration_id: DeclarationId, context: &mut LinearizationContext) -> Ancestors {
|
|
993
|
+
fn linearize_ancestors(&mut self, declaration_id: DeclarationId) -> Ancestors {
|
|
960
994
|
{
|
|
961
995
|
let declaration = self.graph.declarations_mut().get_mut(&declaration_id).unwrap();
|
|
962
996
|
|
|
963
997
|
// Add this declaration to the descendants so that we capture transitive descendant relationships
|
|
964
|
-
context.descendants.insert(declaration_id);
|
|
998
|
+
self.context.descendants.insert(declaration_id);
|
|
965
999
|
|
|
966
1000
|
// Return the cached ancestors if we already computed them. If they are partial ancestors, ignore the cache to try
|
|
967
1001
|
// again
|
|
968
1002
|
if declaration.as_namespace().unwrap().has_complete_ancestors() {
|
|
969
1003
|
let cached = declaration.as_namespace().unwrap().clone_ancestors();
|
|
970
|
-
self.propagate_descendants(&
|
|
1004
|
+
self.propagate_descendants(&cached);
|
|
971
1005
|
|
|
972
|
-
context.finalize(declaration_id);
|
|
1006
|
+
self.context.finalize(declaration_id);
|
|
973
1007
|
return cached;
|
|
974
1008
|
}
|
|
975
1009
|
|
|
976
|
-
if !context.seen_ids.insert(declaration_id) {
|
|
1010
|
+
if !self.context.seen_ids.insert(declaration_id) {
|
|
977
1011
|
// If we find a cycle when linearizing ancestors, it's an error that the programmer must fix. However, we try to
|
|
978
1012
|
// still approximate features by assuming that it must inherit from `Object` at some point (which is what most
|
|
979
1013
|
// classes/modules inherit from). This is not 100% correct, but it allows us to provide a bit better IDE support
|
|
@@ -988,24 +1022,20 @@ impl<'a> Resolver<'a> {
|
|
|
988
1022
|
.unwrap()
|
|
989
1023
|
.set_ancestors(estimated_ancestors.clone());
|
|
990
1024
|
|
|
991
|
-
context.finalize(declaration_id);
|
|
1025
|
+
self.context.finalize(declaration_id);
|
|
992
1026
|
return estimated_ancestors;
|
|
993
1027
|
}
|
|
994
1028
|
|
|
995
1029
|
// Automatically track descendants as we recurse. This has to happen before checking the cache since we may have
|
|
996
1030
|
// already linearized the parent's ancestors, but it's the first time we're discovering the descendant
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
.get_mut(&declaration_id)
|
|
1001
|
-
.unwrap()
|
|
1002
|
-
.as_namespace_mut()
|
|
1003
|
-
.unwrap()
|
|
1004
|
-
.add_descendant(*descendant);
|
|
1031
|
+
let namespace = declaration.as_namespace_mut().unwrap();
|
|
1032
|
+
for descendant in &self.context.descendants {
|
|
1033
|
+
namespace.add_descendant(*descendant);
|
|
1005
1034
|
}
|
|
1006
1035
|
}
|
|
1007
1036
|
|
|
1008
|
-
let
|
|
1037
|
+
let mut state = ChainState::default();
|
|
1038
|
+
let parent_ancestors = self.linearize_parent_ancestors(declaration_id, &mut state);
|
|
1009
1039
|
let declaration = self.graph.declarations().get(&declaration_id).unwrap();
|
|
1010
1040
|
let mut mixins = Vec::new();
|
|
1011
1041
|
|
|
@@ -1019,9 +1049,9 @@ impl<'a> Resolver<'a> {
|
|
|
1019
1049
|
attached_decl
|
|
1020
1050
|
.definitions()
|
|
1021
1051
|
.iter()
|
|
1022
|
-
.
|
|
1023
|
-
.
|
|
1024
|
-
.
|
|
1052
|
+
.flat_map(|definition_id| self.mixins_of(*definition_id))
|
|
1053
|
+
.filter(|mixin| matches!(mixin, Mixin::Extend(_)))
|
|
1054
|
+
.cloned(),
|
|
1025
1055
|
);
|
|
1026
1056
|
}
|
|
1027
1057
|
|
|
@@ -1029,12 +1059,10 @@ impl<'a> Resolver<'a> {
|
|
|
1029
1059
|
let mut has_extends = false;
|
|
1030
1060
|
|
|
1031
1061
|
for definition_id in declaration.definitions() {
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
Mixin::Extend(_) => has_extends = true,
|
|
1037
|
-
}
|
|
1062
|
+
for mixin in self.mixins_of(*definition_id) {
|
|
1063
|
+
match mixin {
|
|
1064
|
+
Mixin::Prepend(_) | Mixin::Include(_) => mixins.push(mixin.clone()),
|
|
1065
|
+
Mixin::Extend(_) => has_extends = true,
|
|
1038
1066
|
}
|
|
1039
1067
|
}
|
|
1040
1068
|
}
|
|
@@ -1045,7 +1073,7 @@ impl<'a> Resolver<'a> {
|
|
|
1045
1073
|
}
|
|
1046
1074
|
|
|
1047
1075
|
let (linearized_prepends, linearized_includes) =
|
|
1048
|
-
self.linearize_mixins(
|
|
1076
|
+
self.linearize_mixins(mixins, parent_ancestors.as_ref(), &mut state);
|
|
1049
1077
|
|
|
1050
1078
|
// Build the final list
|
|
1051
1079
|
let mut ancestors = Vec::new();
|
|
@@ -1056,9 +1084,9 @@ impl<'a> Resolver<'a> {
|
|
|
1056
1084
|
ancestors.extend(parents);
|
|
1057
1085
|
}
|
|
1058
1086
|
|
|
1059
|
-
let result = if
|
|
1087
|
+
let result = if state.cyclic {
|
|
1060
1088
|
Ancestors::Cyclic(ancestors)
|
|
1061
|
-
} else if
|
|
1089
|
+
} else if state.partial {
|
|
1062
1090
|
Ancestors::Partial(ancestors)
|
|
1063
1091
|
} else {
|
|
1064
1092
|
Ancestors::Complete(ancestors)
|
|
@@ -1072,50 +1100,32 @@ impl<'a> Resolver<'a> {
|
|
|
1072
1100
|
.unwrap()
|
|
1073
1101
|
.set_ancestors(result.clone());
|
|
1074
1102
|
|
|
1075
|
-
context.finalize(declaration_id);
|
|
1103
|
+
self.context.finalize(declaration_id);
|
|
1076
1104
|
result
|
|
1077
1105
|
}
|
|
1078
1106
|
|
|
1079
1107
|
fn linearize_parent_ancestors(
|
|
1080
1108
|
&mut self,
|
|
1081
1109
|
declaration_id: DeclarationId,
|
|
1082
|
-
|
|
1110
|
+
state: &mut ChainState,
|
|
1083
1111
|
) -> Option<Vec<Ancestor>> {
|
|
1084
1112
|
let declaration = self.graph.declarations().get(&declaration_id).unwrap();
|
|
1085
1113
|
|
|
1086
1114
|
match declaration {
|
|
1087
1115
|
Declaration::Namespace(Namespace::Class(_)) => {
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
Ancestors::Cyclic(ids) => {
|
|
1091
|
-
context.cyclic = true;
|
|
1092
|
-
ids
|
|
1093
|
-
}
|
|
1094
|
-
Ancestors::Partial(ids) => {
|
|
1095
|
-
context.partial = true;
|
|
1096
|
-
ids
|
|
1097
|
-
}
|
|
1098
|
-
})
|
|
1116
|
+
let superclass = self.linearize_superclass(declaration_id, state)?;
|
|
1117
|
+
Some(state.fold(superclass))
|
|
1099
1118
|
}
|
|
1100
1119
|
Declaration::Namespace(Namespace::SingletonClass(_)) => {
|
|
1101
1120
|
let owner_id = *declaration.owner_id();
|
|
1102
1121
|
|
|
1103
1122
|
let (singleton_parent_id, partial_singleton) = self.singleton_parent_id(owner_id);
|
|
1104
1123
|
if partial_singleton {
|
|
1105
|
-
|
|
1124
|
+
state.partial = true;
|
|
1106
1125
|
}
|
|
1107
1126
|
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
Ancestors::Cyclic(ids) => {
|
|
1111
|
-
context.cyclic = true;
|
|
1112
|
-
ids
|
|
1113
|
-
}
|
|
1114
|
-
Ancestors::Partial(ids) => {
|
|
1115
|
-
context.partial = true;
|
|
1116
|
-
ids
|
|
1117
|
-
}
|
|
1118
|
-
})
|
|
1127
|
+
let parent = self.linearize_ancestors(singleton_parent_id);
|
|
1128
|
+
Some(state.fold(parent))
|
|
1119
1129
|
}
|
|
1120
1130
|
_ => None,
|
|
1121
1131
|
}
|
|
@@ -1125,9 +1135,9 @@ impl<'a> Resolver<'a> {
|
|
|
1125
1135
|
/// modules are deduplicated against them
|
|
1126
1136
|
fn linearize_mixins(
|
|
1127
1137
|
&mut self,
|
|
1128
|
-
context: &mut LinearizationContext,
|
|
1129
1138
|
mixins: Vec<Mixin>,
|
|
1130
1139
|
parent_ancestors: Option<&Vec<Ancestor>>,
|
|
1140
|
+
state: &mut ChainState,
|
|
1131
1141
|
) -> (VecDeque<Ancestor>, VecDeque<Ancestor>) {
|
|
1132
1142
|
let mut linearized_prepends = VecDeque::new();
|
|
1133
1143
|
let mut linearized_includes = VecDeque::new();
|
|
@@ -1150,17 +1160,8 @@ impl<'a> Resolver<'a> {
|
|
|
1150
1160
|
continue;
|
|
1151
1161
|
};
|
|
1152
1162
|
|
|
1153
|
-
let
|
|
1154
|
-
|
|
1155
|
-
Ancestors::Cyclic(ids) => {
|
|
1156
|
-
context.cyclic = true;
|
|
1157
|
-
ids
|
|
1158
|
-
}
|
|
1159
|
-
Ancestors::Partial(ids) => {
|
|
1160
|
-
context.partial = true;
|
|
1161
|
-
ids
|
|
1162
|
-
}
|
|
1163
|
-
};
|
|
1163
|
+
let module_ancestors = self.linearize_ancestors(module_id);
|
|
1164
|
+
let ids = state.fold(module_ancestors);
|
|
1164
1165
|
|
|
1165
1166
|
// Only reorder if there are new modules to add. If all modules being
|
|
1166
1167
|
// prepended are already in the chain (e.g., `prepend A` when A is already
|
|
@@ -1177,7 +1178,7 @@ impl<'a> Resolver<'a> {
|
|
|
1177
1178
|
NameRef::Unresolved(_) => {
|
|
1178
1179
|
// We haven't been able to resolve this name yet, so we push it as a partial linearization to finish
|
|
1179
1180
|
// later
|
|
1180
|
-
|
|
1181
|
+
state.partial = true;
|
|
1181
1182
|
linearized_prepends.push_front(Ancestor::Partial(*constant_reference.name_id()));
|
|
1182
1183
|
}
|
|
1183
1184
|
}
|
|
@@ -1189,17 +1190,8 @@ impl<'a> Resolver<'a> {
|
|
|
1189
1190
|
continue;
|
|
1190
1191
|
};
|
|
1191
1192
|
|
|
1192
|
-
let
|
|
1193
|
-
|
|
1194
|
-
Ancestors::Cyclic(ids) => {
|
|
1195
|
-
context.cyclic = true;
|
|
1196
|
-
ids
|
|
1197
|
-
}
|
|
1198
|
-
Ancestors::Partial(ids) => {
|
|
1199
|
-
context.partial = true;
|
|
1200
|
-
ids
|
|
1201
|
-
}
|
|
1202
|
-
};
|
|
1193
|
+
let module_ancestors = self.linearize_ancestors(module_id);
|
|
1194
|
+
let mut ids = state.fold(module_ancestors);
|
|
1203
1195
|
|
|
1204
1196
|
// Prepended module are deduped based only on other prepended modules
|
|
1205
1197
|
ids.retain(|id| {
|
|
@@ -1217,7 +1209,7 @@ impl<'a> Resolver<'a> {
|
|
|
1217
1209
|
NameRef::Unresolved(_) => {
|
|
1218
1210
|
// We haven't been able to resolve this name yet, so we push it as a partial linearization to finish
|
|
1219
1211
|
// later
|
|
1220
|
-
|
|
1212
|
+
state.partial = true;
|
|
1221
1213
|
linearized_includes.push_front(Ancestor::Partial(*constant_reference.name_id()));
|
|
1222
1214
|
}
|
|
1223
1215
|
}
|
|
@@ -1229,11 +1221,9 @@ impl<'a> Resolver<'a> {
|
|
|
1229
1221
|
}
|
|
1230
1222
|
|
|
1231
1223
|
/// Propagate descendants to all cached ancestors
|
|
1232
|
-
fn propagate_descendants
|
|
1233
|
-
&
|
|
1234
|
-
|
|
1235
|
-
cached: &Ancestors,
|
|
1236
|
-
) {
|
|
1224
|
+
fn propagate_descendants(&mut self, cached: &Ancestors) {
|
|
1225
|
+
let descendants = &self.context.descendants;
|
|
1226
|
+
|
|
1237
1227
|
if !descendants.is_empty() {
|
|
1238
1228
|
for ancestor in cached {
|
|
1239
1229
|
if let Ancestor::Complete(ancestor_id) = ancestor {
|
|
@@ -1245,7 +1235,7 @@ impl<'a> Resolver<'a> {
|
|
|
1245
1235
|
.as_namespace_mut()
|
|
1246
1236
|
.unwrap();
|
|
1247
1237
|
|
|
1248
|
-
for descendant in descendants
|
|
1238
|
+
for descendant in descendants {
|
|
1249
1239
|
namespace.add_descendant(*descendant);
|
|
1250
1240
|
}
|
|
1251
1241
|
}
|
|
@@ -1443,6 +1433,9 @@ impl<'a> Resolver<'a> {
|
|
|
1443
1433
|
parent_owner_id,
|
|
1444
1434
|
)))));
|
|
1445
1435
|
self.graph.add_member(&parent_owner_id, declaration_id, parent_str_id);
|
|
1436
|
+
// A namespace is the first entry of its own ancestor chain, and member lookups only walk that chain. Without
|
|
1437
|
+
// enqueueing this, the Todo keeps an empty `Partial` chain and none of its members can ever be found
|
|
1438
|
+
self.unit_queue.push_back(Unit::Ancestors(declaration_id));
|
|
1446
1439
|
}
|
|
1447
1440
|
|
|
1448
1441
|
declaration_id
|
|
@@ -1735,8 +1728,13 @@ impl<'a> Resolver<'a> {
|
|
|
1735
1728
|
if is_module || chain_incomplete {
|
|
1736
1729
|
let object_outcome = self.search_ancestors(*OBJECT_ID, str_id);
|
|
1737
1730
|
|
|
1738
|
-
|
|
1739
|
-
|
|
1731
|
+
match object_outcome {
|
|
1732
|
+
// Found the constant through Object's ancestors.
|
|
1733
|
+
Outcome::Resolved(decl_id) => return Outcome::Resolved(decl_id),
|
|
1734
|
+
// Object's ancestor chain is still partial, so we need to retry later.
|
|
1735
|
+
Outcome::Retry { .. } => return object_outcome,
|
|
1736
|
+
// Object's chain is complete, so we indeed can't resolve this constant.
|
|
1737
|
+
Outcome::Unresolved => {}
|
|
1740
1738
|
}
|
|
1741
1739
|
}
|
|
1742
1740
|
|
|
@@ -1751,7 +1749,7 @@ impl<'a> Resolver<'a> {
|
|
|
1751
1749
|
|
|
1752
1750
|
/// Search for a member in a declaration's ancestor chain.
|
|
1753
1751
|
fn search_ancestors(&mut self, declaration_id: DeclarationId, str_id: StringId) -> Outcome {
|
|
1754
|
-
match self.
|
|
1752
|
+
match self.linearize_ancestors(declaration_id) {
|
|
1755
1753
|
Ancestors::Complete(ids) | Ancestors::Cyclic(ids) => ids
|
|
1756
1754
|
.iter()
|
|
1757
1755
|
.find_map(|ancestor_id| {
|
|
@@ -1829,9 +1827,15 @@ impl<'a> Resolver<'a> {
|
|
|
1829
1827
|
Outcome::Unresolved
|
|
1830
1828
|
}
|
|
1831
1829
|
|
|
1832
|
-
///
|
|
1833
|
-
///
|
|
1834
|
-
///
|
|
1830
|
+
/// Drains `pending_work` and classifies items into the resolution queue.
|
|
1831
|
+
///
|
|
1832
|
+
/// Namespace definitions and constant references are sorted by their name's [depth](Name::depth) so that simpler
|
|
1833
|
+
/// names are processed first, with the URI rank and offset breaking ties to keep the order deterministic.
|
|
1834
|
+
/// Non-namespace definitions (methods, attrs, variables) are returned separately for
|
|
1835
|
+
/// `handle_remaining_definitions`.
|
|
1836
|
+
///
|
|
1837
|
+
/// Ordering by depth matters because a name's parts may need to be resolved before the name itself can be. Simple
|
|
1838
|
+
/// names are always straightforward no matter how deeply they nest:
|
|
1835
1839
|
///
|
|
1836
1840
|
/// ```ruby
|
|
1837
1841
|
/// module Foo
|
|
@@ -1841,9 +1845,8 @@ impl<'a> Resolver<'a> {
|
|
|
1841
1845
|
/// end
|
|
1842
1846
|
/// ```
|
|
1843
1847
|
///
|
|
1844
|
-
///
|
|
1845
|
-
///
|
|
1846
|
-
/// be resolved to determine what they refer to and so they must be sorted last.
|
|
1848
|
+
/// Here `Foo`, `Bar` and `Baz` only have to be ordered by nesting level. Names with parent scopes, however, require
|
|
1849
|
+
/// their parts to be resolved first, so they must sort last:
|
|
1847
1850
|
///
|
|
1848
1851
|
/// ```ruby
|
|
1849
1852
|
/// module Foo
|
|
@@ -1853,51 +1856,9 @@ impl<'a> Resolver<'a> {
|
|
|
1853
1856
|
/// end
|
|
1854
1857
|
/// ```
|
|
1855
1858
|
///
|
|
1856
|
-
/// In this case
|
|
1857
|
-
///
|
|
1858
|
-
///
|
|
1859
|
-
/// parent scopes in the entire nesting, not just for the name itself
|
|
1860
|
-
///
|
|
1861
|
-
/// Compute the depth of a name in the graph by recursively summing the depths of its
|
|
1862
|
-
/// `parent_scope` and `nesting` chains. Results are memoized in `cache` (`NameId` → depth)
|
|
1863
|
-
/// so each name is computed at most once across all calls.
|
|
1864
|
-
///
|
|
1865
|
-
/// Depth represents the total complexity of a name's position in the namespace hierarchy.
|
|
1866
|
-
/// For example, in `module Foo; module Bar; class Baz; end; end; end`, Foo has depth 1
|
|
1867
|
-
/// (top-level), Bar has depth 2, and Baz has depth 3.
|
|
1868
|
-
///
|
|
1869
|
-
/// # Panics
|
|
1870
|
-
///
|
|
1871
|
-
/// Will panic if there is inconsistent data in the graph
|
|
1872
|
-
fn name_depth(
|
|
1873
|
-
name_id: NameId,
|
|
1874
|
-
names: &IdentityHashMap<NameId, NameRef>,
|
|
1875
|
-
cache: &mut IdentityHashMap<NameId, u32>,
|
|
1876
|
-
) -> u32 {
|
|
1877
|
-
if let Some(&depth) = cache.get(&name_id) {
|
|
1878
|
-
return depth;
|
|
1879
|
-
}
|
|
1880
|
-
|
|
1881
|
-
let name = names.get(&name_id).unwrap();
|
|
1882
|
-
|
|
1883
|
-
let depth = if name.parent_scope().is_top_level() {
|
|
1884
|
-
1
|
|
1885
|
-
} else {
|
|
1886
|
-
let parent_depth = name.parent_scope().map_or(0, |id| Self::name_depth(*id, names, cache));
|
|
1887
|
-
|
|
1888
|
-
let nesting_depth = name.nesting().map_or(0, |id| Self::name_depth(id, names, cache));
|
|
1889
|
-
|
|
1890
|
-
parent_depth + nesting_depth + 1
|
|
1891
|
-
};
|
|
1892
|
-
|
|
1893
|
-
cache.insert(name_id, depth);
|
|
1894
|
-
depth
|
|
1895
|
-
}
|
|
1896
|
-
|
|
1897
|
-
/// Drains `pending_work` and classifies items into the resolution queue.
|
|
1898
|
-
/// Namespace definitions and constant references are sorted by name depth for deterministic
|
|
1899
|
-
/// resolution order. Non-namespace definitions (methods, attrs, variables) are returned
|
|
1900
|
-
/// separately for `handle_remaining_definitions`.
|
|
1859
|
+
/// In this case `Bar` must already be processed so the `Bar` reference inside `Foo` resolves, which then unblocks
|
|
1860
|
+
/// `Baz` and finally `Qux`. Notice how `Qux` is a simple name nested under a complex one, so it too sorts late —
|
|
1861
|
+
/// which is why depth accounts for the parent scopes across the entire nesting, not just for the name itself.
|
|
1901
1862
|
fn prepare_units(&mut self) -> Vec<DefinitionId> {
|
|
1902
1863
|
let work = self.graph.take_pending_work();
|
|
1903
1864
|
let estimated = work.len() / 2;
|
|
@@ -1907,16 +1868,13 @@ impl<'a> Resolver<'a> {
|
|
|
1907
1868
|
let mut const_refs = Vec::new();
|
|
1908
1869
|
let mut ancestors = vec![*BASIC_OBJECT_ID, *KERNEL_ID, *OBJECT_ID, *MODULE_ID, *CLASS_ID];
|
|
1909
1870
|
let names = self.graph.names();
|
|
1910
|
-
//
|
|
1911
|
-
//
|
|
1912
|
-
|
|
1913
|
-
// reasonable capacity that avoids rehashing as depths accumulate.
|
|
1914
|
-
let mut depths: IdentityHashMap<NameId, u32> =
|
|
1915
|
-
IdentityHashMap::with_capacity_and_hasher(estimated.min(names.len()), IdentityHashBuilder);
|
|
1871
|
+
// Every name captured its depth during indexing (see `Name::compute_depth`), so ordering the units below is a
|
|
1872
|
+
// direct lookup rather than a recursive walk of the nesting chain.
|
|
1873
|
+
let depth_of = |name_id: &NameId| names.get(name_id).unwrap().depth();
|
|
1916
1874
|
|
|
1917
1875
|
// Precompute the lexicographic rank of every document URI. Definitions and references are sorted by
|
|
1918
|
-
// (name depth, URI, offset) below; storing precomputed integer
|
|
1919
|
-
//
|
|
1876
|
+
// (name depth, URI, offset) below; storing a precomputed integer rank instead of comparing URI strings on
|
|
1877
|
+
// every comparison makes sorting substantially cheaper on large graphs.
|
|
1920
1878
|
let mut uris: Vec<(&str, UriId)> = self
|
|
1921
1879
|
.graph
|
|
1922
1880
|
.documents()
|
|
@@ -1950,23 +1908,23 @@ impl<'a> Resolver<'a> {
|
|
|
1950
1908
|
|
|
1951
1909
|
match definition {
|
|
1952
1910
|
Definition::Class(def) => {
|
|
1953
|
-
let depth =
|
|
1911
|
+
let depth = depth_of(def.name_id());
|
|
1954
1912
|
definitions.push((Unit::Definition(id), (depth, uri_rank, definition.offset())));
|
|
1955
1913
|
}
|
|
1956
1914
|
Definition::Module(def) => {
|
|
1957
|
-
let depth =
|
|
1915
|
+
let depth = depth_of(def.name_id());
|
|
1958
1916
|
definitions.push((Unit::Definition(id), (depth, uri_rank, definition.offset())));
|
|
1959
1917
|
}
|
|
1960
1918
|
Definition::Constant(def) => {
|
|
1961
|
-
let depth =
|
|
1919
|
+
let depth = depth_of(def.name_id());
|
|
1962
1920
|
definitions.push((Unit::Definition(id), (depth, uri_rank, definition.offset())));
|
|
1963
1921
|
}
|
|
1964
1922
|
Definition::ConstantAlias(def) => {
|
|
1965
|
-
let depth =
|
|
1923
|
+
let depth = depth_of(def.name_id());
|
|
1966
1924
|
definitions.push((Unit::Definition(id), (depth, uri_rank, definition.offset())));
|
|
1967
1925
|
}
|
|
1968
1926
|
Definition::SingletonClass(def) => {
|
|
1969
|
-
let depth =
|
|
1927
|
+
let depth = depth_of(def.name_id());
|
|
1970
1928
|
definitions.push((Unit::Definition(id), (depth, uri_rank, definition.offset())));
|
|
1971
1929
|
}
|
|
1972
1930
|
// SelfReceiver methods create singleton classes, which need
|
|
@@ -1989,7 +1947,7 @@ impl<'a> Resolver<'a> {
|
|
|
1989
1947
|
continue;
|
|
1990
1948
|
};
|
|
1991
1949
|
let uri_rank = *uri_ranks.get(&constant_ref.uri_id()).unwrap();
|
|
1992
|
-
let depth =
|
|
1950
|
+
let depth = depth_of(constant_ref.name_id());
|
|
1993
1951
|
const_refs.push((Unit::ConstantRef(id), (depth, uri_rank, constant_ref.offset())));
|
|
1994
1952
|
}
|
|
1995
1953
|
Unit::Ancestors(id) => {
|
|
@@ -2106,16 +2064,12 @@ impl<'a> Resolver<'a> {
|
|
|
2106
2064
|
))
|
|
2107
2065
|
}
|
|
2108
2066
|
|
|
2109
|
-
fn linearize_superclass(
|
|
2110
|
-
&mut self,
|
|
2111
|
-
declaration_id: DeclarationId,
|
|
2112
|
-
context: &mut LinearizationContext,
|
|
2113
|
-
) -> Option<Ancestors> {
|
|
2067
|
+
fn linearize_superclass(&mut self, declaration_id: DeclarationId, state: &mut ChainState) -> Option<Ancestors> {
|
|
2114
2068
|
let (superclass_id, unresolved_superclass) = self.get_superclass(declaration_id)?;
|
|
2115
|
-
let mut result = self.linearize_ancestors(superclass_id
|
|
2069
|
+
let mut result = self.linearize_ancestors(superclass_id);
|
|
2116
2070
|
|
|
2117
2071
|
if let Some(name_id) = unresolved_superclass {
|
|
2118
|
-
|
|
2072
|
+
state.partial = true;
|
|
2119
2073
|
|
|
2120
2074
|
// Insert the unresolved superclass as a Partial ancestor at the front of the chain, so it
|
|
2121
2075
|
// appears before the default Object ancestors
|
|
@@ -2130,14 +2084,12 @@ impl<'a> Resolver<'a> {
|
|
|
2130
2084
|
Some(result)
|
|
2131
2085
|
}
|
|
2132
2086
|
|
|
2133
|
-
fn mixins_of(&self, definition_id: DefinitionId) ->
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
Definition::
|
|
2138
|
-
|
|
2139
|
-
Definition::Module(module) => Some(module.mixins().to_vec()),
|
|
2140
|
-
_ => None,
|
|
2087
|
+
fn mixins_of(&self, definition_id: DefinitionId) -> &[Mixin] {
|
|
2088
|
+
match self.graph.definitions().get(&definition_id).unwrap() {
|
|
2089
|
+
Definition::Class(class) => class.mixins(),
|
|
2090
|
+
Definition::SingletonClass(class) => class.mixins(),
|
|
2091
|
+
Definition::Module(module) => module.mixins(),
|
|
2092
|
+
_ => &[],
|
|
2141
2093
|
}
|
|
2142
2094
|
}
|
|
2143
2095
|
}
|