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
|
@@ -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::{
|
|
@@ -14,8 +11,9 @@ use crate::model::{
|
|
|
14
11
|
definitions::{Definition, Mixin, Receiver},
|
|
15
12
|
graph::{Graph, Unit},
|
|
16
13
|
identity_maps::{IdentityHashBuilder, IdentityHashMap, IdentityHashSet},
|
|
17
|
-
ids::{ConstantReferenceId, DeclarationId, DefinitionId, NameId, StringId},
|
|
14
|
+
ids::{ConstantReferenceId, DeclarationId, DefinitionId, NameId, StringId, UriId},
|
|
18
15
|
name::{Name, NameRef, ParentScope},
|
|
16
|
+
visibility::Visibility,
|
|
19
17
|
};
|
|
20
18
|
|
|
21
19
|
enum Outcome {
|
|
@@ -50,11 +48,32 @@ enum SingletonAncestors {
|
|
|
50
48
|
Deferred,
|
|
51
49
|
}
|
|
52
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
|
+
|
|
53
74
|
struct LinearizationContext {
|
|
54
75
|
descendants: IdentityHashSet<DeclarationId>,
|
|
55
76
|
seen_ids: IdentityHashSet<DeclarationId>,
|
|
56
|
-
cyclic: bool,
|
|
57
|
-
partial: bool,
|
|
58
77
|
}
|
|
59
78
|
|
|
60
79
|
impl LinearizationContext {
|
|
@@ -62,8 +81,6 @@ impl LinearizationContext {
|
|
|
62
81
|
Self {
|
|
63
82
|
descendants: IdentityHashSet::default(),
|
|
64
83
|
seen_ids: IdentityHashSet::default(),
|
|
65
|
-
cyclic: false,
|
|
66
|
-
partial: false,
|
|
67
84
|
}
|
|
68
85
|
}
|
|
69
86
|
|
|
@@ -82,6 +99,8 @@ pub struct Resolver<'a> {
|
|
|
82
99
|
unit_queue: VecDeque<Unit>,
|
|
83
100
|
/// Whether we made any progress in the last pass of the resolution loop
|
|
84
101
|
made_progress: bool,
|
|
102
|
+
/// The linearization context carried across recursion
|
|
103
|
+
context: LinearizationContext,
|
|
85
104
|
}
|
|
86
105
|
|
|
87
106
|
impl<'a> Resolver<'a> {
|
|
@@ -90,6 +109,7 @@ impl<'a> Resolver<'a> {
|
|
|
90
109
|
graph,
|
|
91
110
|
unit_queue: VecDeque::new(),
|
|
92
111
|
made_progress: false,
|
|
112
|
+
context: LinearizationContext::new(),
|
|
93
113
|
}
|
|
94
114
|
}
|
|
95
115
|
|
|
@@ -228,12 +248,38 @@ impl<'a> Resolver<'a> {
|
|
|
228
248
|
Outcome::Resolved(id) => {
|
|
229
249
|
if needs_linearization {
|
|
230
250
|
self.unit_queue.push_back(Unit::Ancestors(id));
|
|
251
|
+
self.relinearize_singleton_hierarchy(id);
|
|
231
252
|
}
|
|
232
253
|
self.made_progress = true;
|
|
233
254
|
}
|
|
234
255
|
}
|
|
235
256
|
}
|
|
236
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
|
+
|
|
237
283
|
/// Handles a unit of work for resolving a constant reference
|
|
238
284
|
fn handle_reference_unit(&mut self, unit_id: Unit, id: ConstantReferenceId) {
|
|
239
285
|
let constant_ref = self.graph.constant_references().get(&id).unwrap();
|
|
@@ -256,7 +302,7 @@ impl<'a> Resolver<'a> {
|
|
|
256
302
|
|
|
257
303
|
/// Handles a unit of work for linearizing ancestors of a declaration
|
|
258
304
|
fn handle_ancestor_unit(&mut self, id: DeclarationId) {
|
|
259
|
-
match self.
|
|
305
|
+
match self.linearize_ancestors(id) {
|
|
260
306
|
Ancestors::Complete(_) | Ancestors::Cyclic(_) => {
|
|
261
307
|
// We succeeded in some capacity this time
|
|
262
308
|
self.made_progress = true;
|
|
@@ -635,7 +681,7 @@ impl<'a> Resolver<'a> {
|
|
|
635
681
|
}
|
|
636
682
|
|
|
637
683
|
/// Resolves retroactive method visibility changes (`private :foo`, `protected :foo`, `public :foo`,
|
|
638
|
-
/// `private_class_method :foo`, `public_class_method :foo`).
|
|
684
|
+
/// `private_class_method :foo`, `public_class_method :foo`, `module_function :foo`).
|
|
639
685
|
///
|
|
640
686
|
/// Runs as a second pass after all methods/attrs are declared, so `private :bar` works
|
|
641
687
|
/// regardless of whether `def bar` appeared before or after it in source.
|
|
@@ -652,12 +698,14 @@ impl<'a> Resolver<'a> {
|
|
|
652
698
|
let offset = method_visibility.offset().clone();
|
|
653
699
|
let lexical_nesting_id = *method_visibility.lexical_nesting_id();
|
|
654
700
|
let is_singleton = method_visibility.flags().is_singleton_method_visibility();
|
|
701
|
+
let is_module_function = *method_visibility.visibility() == Visibility::ModuleFunction;
|
|
702
|
+
let is_module_function_singleton = is_singleton && is_module_function;
|
|
655
703
|
|
|
656
704
|
let Some(lexical_owner_id) = self.resolve_lexical_owner(lexical_nesting_id, id) else {
|
|
657
705
|
continue;
|
|
658
706
|
};
|
|
659
707
|
|
|
660
|
-
let
|
|
708
|
+
let namespace_id = if is_singleton && !is_module_function {
|
|
661
709
|
let Some(singleton_id) =
|
|
662
710
|
self.get_or_create_singleton_class(lexical_owner_id, SingletonAncestors::Eager)
|
|
663
711
|
else {
|
|
@@ -668,12 +716,12 @@ impl<'a> Resolver<'a> {
|
|
|
668
716
|
lexical_owner_id
|
|
669
717
|
};
|
|
670
718
|
|
|
671
|
-
let Some(Declaration::Namespace(namespace)) = self.graph.declarations().get(&
|
|
719
|
+
let Some(Declaration::Namespace(namespace)) = self.graph.declarations().get(&namespace_id) else {
|
|
672
720
|
continue;
|
|
673
721
|
};
|
|
674
722
|
|
|
675
|
-
let mut
|
|
676
|
-
let mut
|
|
723
|
+
let mut visibility_definition_attached = false;
|
|
724
|
+
let mut ancestor_chain_is_incomplete = false;
|
|
677
725
|
|
|
678
726
|
for ancestor in namespace.ancestors() {
|
|
679
727
|
match ancestor {
|
|
@@ -687,40 +735,51 @@ impl<'a> Resolver<'a> {
|
|
|
687
735
|
.is_some();
|
|
688
736
|
|
|
689
737
|
if has_member {
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
738
|
+
if is_module_function_singleton {
|
|
739
|
+
let Some(singleton_id) =
|
|
740
|
+
self.get_or_create_singleton_class(lexical_owner_id, SingletonAncestors::Eager)
|
|
741
|
+
else {
|
|
742
|
+
break;
|
|
743
|
+
};
|
|
744
|
+
|
|
745
|
+
self.create_declaration(str_id, id, singleton_id, |name| {
|
|
746
|
+
Declaration::Method(Box::new(MethodDeclaration::new(name, singleton_id)))
|
|
747
|
+
});
|
|
748
|
+
} else {
|
|
749
|
+
self.create_declaration(str_id, id, namespace_id, |name| {
|
|
750
|
+
Declaration::Method(Box::new(MethodDeclaration::new(name, namespace_id)))
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
visibility_definition_attached = true;
|
|
697
754
|
break;
|
|
698
755
|
}
|
|
699
756
|
}
|
|
700
|
-
Ancestor::Partial(_) =>
|
|
757
|
+
Ancestor::Partial(_) => ancestor_chain_is_incomplete = true,
|
|
701
758
|
}
|
|
702
759
|
}
|
|
703
760
|
|
|
704
|
-
if
|
|
761
|
+
if visibility_definition_attached {
|
|
705
762
|
continue;
|
|
706
763
|
}
|
|
707
764
|
|
|
708
|
-
if
|
|
709
|
-
// Method might exist on an unresolved ancestor — requeue for retry.
|
|
765
|
+
if ancestor_chain_is_incomplete {
|
|
710
766
|
pending_work.push(Unit::Definition(id));
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
Rule::UndefinedMethodVisibilityTarget,
|
|
717
|
-
uri_id,
|
|
718
|
-
offset,
|
|
719
|
-
format!("undefined method `{owner_name}#{method_name}` for visibility change"),
|
|
720
|
-
);
|
|
721
|
-
|
|
722
|
-
self.graph.add_document_diagnostic(uri_id, diagnostic);
|
|
767
|
+
continue;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
if is_module_function_singleton {
|
|
771
|
+
continue;
|
|
723
772
|
}
|
|
773
|
+
|
|
774
|
+
let method_name = self.graph.strings().get(&str_id).unwrap().as_str().to_string();
|
|
775
|
+
let owner_name = self.graph.declarations().get(&namespace_id).unwrap().name().to_string();
|
|
776
|
+
let diagnostic = Diagnostic::new(
|
|
777
|
+
Rule::UndefinedMethodVisibilityTarget,
|
|
778
|
+
uri_id,
|
|
779
|
+
offset,
|
|
780
|
+
format!("undefined method `{owner_name}#{method_name}` for visibility change"),
|
|
781
|
+
);
|
|
782
|
+
self.graph.add_document_diagnostic(uri_id, diagnostic);
|
|
724
783
|
}
|
|
725
784
|
|
|
726
785
|
// Must extend work here so incremental resolution can resolve previously unresolved visibility operations
|
|
@@ -850,7 +909,7 @@ impl<'a> Resolver<'a> {
|
|
|
850
909
|
/// Gets or creates a singleton class declaration for a given class/module declaration. For class `Foo`, this
|
|
851
910
|
/// returns the declaration for `Foo::<Foo>`.
|
|
852
911
|
///
|
|
853
|
-
/// If the declaration is a `Constant` with all-promotable definitions, it is automatically promoted to
|
|
912
|
+
/// If the declaration is a `Constant` with all-promotable definitions, it is automatically promoted to an unknown
|
|
854
913
|
/// namespace before creating the singleton. Returns `None` if the declaration is not a namespace and cannot be
|
|
855
914
|
/// promoted (e.g., `FOO = 42`).
|
|
856
915
|
/// `ancestors` controls how the singleton's ancestor chain is scheduled for linearization —
|
|
@@ -863,6 +922,10 @@ impl<'a> Resolver<'a> {
|
|
|
863
922
|
) -> Option<DeclarationId> {
|
|
864
923
|
let attached_decl = self.graph.declarations().get(&attached_id).unwrap();
|
|
865
924
|
|
|
925
|
+
if let Some(singleton_id) = attached_decl.as_namespace().and_then(|d| d.singleton_class()) {
|
|
926
|
+
return Some(*singleton_id);
|
|
927
|
+
}
|
|
928
|
+
|
|
866
929
|
// If the attached object is a constant alias, follow the alias chain to find the actual namespace
|
|
867
930
|
if matches!(attached_decl, Declaration::ConstantAlias(_)) {
|
|
868
931
|
return match self.resolve_to_namespace(attached_id) {
|
|
@@ -874,7 +937,7 @@ impl<'a> Resolver<'a> {
|
|
|
874
937
|
if matches!(attached_decl, Declaration::Constant(_)) {
|
|
875
938
|
if self.graph.all_definitions_promotable(attached_decl) {
|
|
876
939
|
self.graph.promote_constant_to_namespace(attached_id, |name, owner_id| {
|
|
877
|
-
Declaration::Namespace(Namespace::
|
|
940
|
+
Declaration::Namespace(Namespace::Todo(Box::new(TodoDeclaration::new(name, owner_id))))
|
|
878
941
|
});
|
|
879
942
|
|
|
880
943
|
self.schedule_singleton_ancestors(attached_id, mode);
|
|
@@ -890,10 +953,6 @@ impl<'a> Resolver<'a> {
|
|
|
890
953
|
.as_namespace_mut()
|
|
891
954
|
.expect("constants are handled above; all other callers pass namespace declarations");
|
|
892
955
|
|
|
893
|
-
if let Some(singleton_id) = namespace_decl.singleton_class() {
|
|
894
|
-
return Some(*singleton_id);
|
|
895
|
-
}
|
|
896
|
-
|
|
897
956
|
let decl_id = DeclarationId::from(&fully_qualified_name);
|
|
898
957
|
namespace_decl.set_singleton_class_id(decl_id);
|
|
899
958
|
|
|
@@ -914,7 +973,7 @@ impl<'a> Resolver<'a> {
|
|
|
914
973
|
fn schedule_singleton_ancestors(&mut self, id: DeclarationId, mode: SingletonAncestors) {
|
|
915
974
|
match mode {
|
|
916
975
|
SingletonAncestors::Eager => {
|
|
917
|
-
let _ = self.
|
|
976
|
+
let _ = self.linearize_ancestors(id);
|
|
918
977
|
}
|
|
919
978
|
SingletonAncestors::Enqueue => {
|
|
920
979
|
self.unit_queue.push_back(Unit::Ancestors(id));
|
|
@@ -931,35 +990,24 @@ impl<'a> Resolver<'a> {
|
|
|
931
990
|
///
|
|
932
991
|
/// Can panic if there's inconsistent data in the graph
|
|
933
992
|
#[must_use]
|
|
934
|
-
fn
|
|
935
|
-
let mut context = LinearizationContext::new();
|
|
936
|
-
self.linearize_ancestors(declaration_id, &mut context)
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
/// Linearizes the ancestors of a declaration, returning the list of ancestor declaration IDs
|
|
940
|
-
///
|
|
941
|
-
/// # Panics
|
|
942
|
-
///
|
|
943
|
-
/// Can panic if there's inconsistent data in the graph
|
|
944
|
-
#[must_use]
|
|
945
|
-
fn linearize_ancestors(&mut self, declaration_id: DeclarationId, context: &mut LinearizationContext) -> Ancestors {
|
|
993
|
+
fn linearize_ancestors(&mut self, declaration_id: DeclarationId) -> Ancestors {
|
|
946
994
|
{
|
|
947
995
|
let declaration = self.graph.declarations_mut().get_mut(&declaration_id).unwrap();
|
|
948
996
|
|
|
949
997
|
// Add this declaration to the descendants so that we capture transitive descendant relationships
|
|
950
|
-
context.descendants.insert(declaration_id);
|
|
998
|
+
self.context.descendants.insert(declaration_id);
|
|
951
999
|
|
|
952
1000
|
// Return the cached ancestors if we already computed them. If they are partial ancestors, ignore the cache to try
|
|
953
1001
|
// again
|
|
954
1002
|
if declaration.as_namespace().unwrap().has_complete_ancestors() {
|
|
955
1003
|
let cached = declaration.as_namespace().unwrap().clone_ancestors();
|
|
956
|
-
self.propagate_descendants(&
|
|
1004
|
+
self.propagate_descendants(&cached);
|
|
957
1005
|
|
|
958
|
-
context.finalize(declaration_id);
|
|
1006
|
+
self.context.finalize(declaration_id);
|
|
959
1007
|
return cached;
|
|
960
1008
|
}
|
|
961
1009
|
|
|
962
|
-
if !context.seen_ids.insert(declaration_id) {
|
|
1010
|
+
if !self.context.seen_ids.insert(declaration_id) {
|
|
963
1011
|
// If we find a cycle when linearizing ancestors, it's an error that the programmer must fix. However, we try to
|
|
964
1012
|
// still approximate features by assuming that it must inherit from `Object` at some point (which is what most
|
|
965
1013
|
// classes/modules inherit from). This is not 100% correct, but it allows us to provide a bit better IDE support
|
|
@@ -974,24 +1022,20 @@ impl<'a> Resolver<'a> {
|
|
|
974
1022
|
.unwrap()
|
|
975
1023
|
.set_ancestors(estimated_ancestors.clone());
|
|
976
1024
|
|
|
977
|
-
context.finalize(declaration_id);
|
|
1025
|
+
self.context.finalize(declaration_id);
|
|
978
1026
|
return estimated_ancestors;
|
|
979
1027
|
}
|
|
980
1028
|
|
|
981
1029
|
// Automatically track descendants as we recurse. This has to happen before checking the cache since we may have
|
|
982
1030
|
// already linearized the parent's ancestors, but it's the first time we're discovering the descendant
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
.get_mut(&declaration_id)
|
|
987
|
-
.unwrap()
|
|
988
|
-
.as_namespace_mut()
|
|
989
|
-
.unwrap()
|
|
990
|
-
.add_descendant(*descendant);
|
|
1031
|
+
let namespace = declaration.as_namespace_mut().unwrap();
|
|
1032
|
+
for descendant in &self.context.descendants {
|
|
1033
|
+
namespace.add_descendant(*descendant);
|
|
991
1034
|
}
|
|
992
1035
|
}
|
|
993
1036
|
|
|
994
|
-
let
|
|
1037
|
+
let mut state = ChainState::default();
|
|
1038
|
+
let parent_ancestors = self.linearize_parent_ancestors(declaration_id, &mut state);
|
|
995
1039
|
let declaration = self.graph.declarations().get(&declaration_id).unwrap();
|
|
996
1040
|
let mut mixins = Vec::new();
|
|
997
1041
|
|
|
@@ -1005,9 +1049,9 @@ impl<'a> Resolver<'a> {
|
|
|
1005
1049
|
attached_decl
|
|
1006
1050
|
.definitions()
|
|
1007
1051
|
.iter()
|
|
1008
|
-
.
|
|
1009
|
-
.
|
|
1010
|
-
.
|
|
1052
|
+
.flat_map(|definition_id| self.mixins_of(*definition_id))
|
|
1053
|
+
.filter(|mixin| matches!(mixin, Mixin::Extend(_)))
|
|
1054
|
+
.cloned(),
|
|
1011
1055
|
);
|
|
1012
1056
|
}
|
|
1013
1057
|
|
|
@@ -1015,12 +1059,10 @@ impl<'a> Resolver<'a> {
|
|
|
1015
1059
|
let mut has_extends = false;
|
|
1016
1060
|
|
|
1017
1061
|
for definition_id in declaration.definitions() {
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
Mixin::Extend(_) => has_extends = true,
|
|
1023
|
-
}
|
|
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,
|
|
1024
1066
|
}
|
|
1025
1067
|
}
|
|
1026
1068
|
}
|
|
@@ -1031,7 +1073,7 @@ impl<'a> Resolver<'a> {
|
|
|
1031
1073
|
}
|
|
1032
1074
|
|
|
1033
1075
|
let (linearized_prepends, linearized_includes) =
|
|
1034
|
-
self.linearize_mixins(
|
|
1076
|
+
self.linearize_mixins(mixins, parent_ancestors.as_ref(), &mut state);
|
|
1035
1077
|
|
|
1036
1078
|
// Build the final list
|
|
1037
1079
|
let mut ancestors = Vec::new();
|
|
@@ -1042,9 +1084,9 @@ impl<'a> Resolver<'a> {
|
|
|
1042
1084
|
ancestors.extend(parents);
|
|
1043
1085
|
}
|
|
1044
1086
|
|
|
1045
|
-
let result = if
|
|
1087
|
+
let result = if state.cyclic {
|
|
1046
1088
|
Ancestors::Cyclic(ancestors)
|
|
1047
|
-
} else if
|
|
1089
|
+
} else if state.partial {
|
|
1048
1090
|
Ancestors::Partial(ancestors)
|
|
1049
1091
|
} else {
|
|
1050
1092
|
Ancestors::Complete(ancestors)
|
|
@@ -1058,56 +1100,32 @@ impl<'a> Resolver<'a> {
|
|
|
1058
1100
|
.unwrap()
|
|
1059
1101
|
.set_ancestors(result.clone());
|
|
1060
1102
|
|
|
1061
|
-
context.finalize(declaration_id);
|
|
1103
|
+
self.context.finalize(declaration_id);
|
|
1062
1104
|
result
|
|
1063
1105
|
}
|
|
1064
1106
|
|
|
1065
1107
|
fn linearize_parent_ancestors(
|
|
1066
1108
|
&mut self,
|
|
1067
1109
|
declaration_id: DeclarationId,
|
|
1068
|
-
|
|
1110
|
+
state: &mut ChainState,
|
|
1069
1111
|
) -> Option<Vec<Ancestor>> {
|
|
1070
|
-
if declaration_id == *BASIC_OBJECT_ID {
|
|
1071
|
-
return None;
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
1112
|
let declaration = self.graph.declarations().get(&declaration_id).unwrap();
|
|
1075
1113
|
|
|
1076
1114
|
match declaration {
|
|
1077
1115
|
Declaration::Namespace(Namespace::Class(_)) => {
|
|
1078
|
-
let
|
|
1079
|
-
|
|
1080
|
-
Some(match self.linearize_parent_class(&definition_ids, context) {
|
|
1081
|
-
Ancestors::Complete(ids) => ids,
|
|
1082
|
-
Ancestors::Cyclic(ids) => {
|
|
1083
|
-
context.cyclic = true;
|
|
1084
|
-
ids
|
|
1085
|
-
}
|
|
1086
|
-
Ancestors::Partial(ids) => {
|
|
1087
|
-
context.partial = true;
|
|
1088
|
-
ids
|
|
1089
|
-
}
|
|
1090
|
-
})
|
|
1116
|
+
let superclass = self.linearize_superclass(declaration_id, state)?;
|
|
1117
|
+
Some(state.fold(superclass))
|
|
1091
1118
|
}
|
|
1092
1119
|
Declaration::Namespace(Namespace::SingletonClass(_)) => {
|
|
1093
1120
|
let owner_id = *declaration.owner_id();
|
|
1094
1121
|
|
|
1095
1122
|
let (singleton_parent_id, partial_singleton) = self.singleton_parent_id(owner_id);
|
|
1096
1123
|
if partial_singleton {
|
|
1097
|
-
|
|
1124
|
+
state.partial = true;
|
|
1098
1125
|
}
|
|
1099
1126
|
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
Ancestors::Cyclic(ids) => {
|
|
1103
|
-
context.cyclic = true;
|
|
1104
|
-
ids
|
|
1105
|
-
}
|
|
1106
|
-
Ancestors::Partial(ids) => {
|
|
1107
|
-
context.partial = true;
|
|
1108
|
-
ids
|
|
1109
|
-
}
|
|
1110
|
-
})
|
|
1127
|
+
let parent = self.linearize_ancestors(singleton_parent_id);
|
|
1128
|
+
Some(state.fold(parent))
|
|
1111
1129
|
}
|
|
1112
1130
|
_ => None,
|
|
1113
1131
|
}
|
|
@@ -1117,9 +1135,9 @@ impl<'a> Resolver<'a> {
|
|
|
1117
1135
|
/// modules are deduplicated against them
|
|
1118
1136
|
fn linearize_mixins(
|
|
1119
1137
|
&mut self,
|
|
1120
|
-
context: &mut LinearizationContext,
|
|
1121
1138
|
mixins: Vec<Mixin>,
|
|
1122
1139
|
parent_ancestors: Option<&Vec<Ancestor>>,
|
|
1140
|
+
state: &mut ChainState,
|
|
1123
1141
|
) -> (VecDeque<Ancestor>, VecDeque<Ancestor>) {
|
|
1124
1142
|
let mut linearized_prepends = VecDeque::new();
|
|
1125
1143
|
let mut linearized_includes = VecDeque::new();
|
|
@@ -1142,17 +1160,8 @@ impl<'a> Resolver<'a> {
|
|
|
1142
1160
|
continue;
|
|
1143
1161
|
};
|
|
1144
1162
|
|
|
1145
|
-
let
|
|
1146
|
-
|
|
1147
|
-
Ancestors::Cyclic(ids) => {
|
|
1148
|
-
context.cyclic = true;
|
|
1149
|
-
ids
|
|
1150
|
-
}
|
|
1151
|
-
Ancestors::Partial(ids) => {
|
|
1152
|
-
context.partial = true;
|
|
1153
|
-
ids
|
|
1154
|
-
}
|
|
1155
|
-
};
|
|
1163
|
+
let module_ancestors = self.linearize_ancestors(module_id);
|
|
1164
|
+
let ids = state.fold(module_ancestors);
|
|
1156
1165
|
|
|
1157
1166
|
// Only reorder if there are new modules to add. If all modules being
|
|
1158
1167
|
// prepended are already in the chain (e.g., `prepend A` when A is already
|
|
@@ -1169,7 +1178,7 @@ impl<'a> Resolver<'a> {
|
|
|
1169
1178
|
NameRef::Unresolved(_) => {
|
|
1170
1179
|
// We haven't been able to resolve this name yet, so we push it as a partial linearization to finish
|
|
1171
1180
|
// later
|
|
1172
|
-
|
|
1181
|
+
state.partial = true;
|
|
1173
1182
|
linearized_prepends.push_front(Ancestor::Partial(*constant_reference.name_id()));
|
|
1174
1183
|
}
|
|
1175
1184
|
}
|
|
@@ -1181,17 +1190,8 @@ impl<'a> Resolver<'a> {
|
|
|
1181
1190
|
continue;
|
|
1182
1191
|
};
|
|
1183
1192
|
|
|
1184
|
-
let
|
|
1185
|
-
|
|
1186
|
-
Ancestors::Cyclic(ids) => {
|
|
1187
|
-
context.cyclic = true;
|
|
1188
|
-
ids
|
|
1189
|
-
}
|
|
1190
|
-
Ancestors::Partial(ids) => {
|
|
1191
|
-
context.partial = true;
|
|
1192
|
-
ids
|
|
1193
|
-
}
|
|
1194
|
-
};
|
|
1193
|
+
let module_ancestors = self.linearize_ancestors(module_id);
|
|
1194
|
+
let mut ids = state.fold(module_ancestors);
|
|
1195
1195
|
|
|
1196
1196
|
// Prepended module are deduped based only on other prepended modules
|
|
1197
1197
|
ids.retain(|id| {
|
|
@@ -1209,7 +1209,7 @@ impl<'a> Resolver<'a> {
|
|
|
1209
1209
|
NameRef::Unresolved(_) => {
|
|
1210
1210
|
// We haven't been able to resolve this name yet, so we push it as a partial linearization to finish
|
|
1211
1211
|
// later
|
|
1212
|
-
|
|
1212
|
+
state.partial = true;
|
|
1213
1213
|
linearized_includes.push_front(Ancestor::Partial(*constant_reference.name_id()));
|
|
1214
1214
|
}
|
|
1215
1215
|
}
|
|
@@ -1221,22 +1221,22 @@ impl<'a> Resolver<'a> {
|
|
|
1221
1221
|
}
|
|
1222
1222
|
|
|
1223
1223
|
/// Propagate descendants to all cached ancestors
|
|
1224
|
-
fn propagate_descendants
|
|
1225
|
-
&
|
|
1226
|
-
|
|
1227
|
-
cached: &Ancestors,
|
|
1228
|
-
) {
|
|
1224
|
+
fn propagate_descendants(&mut self, cached: &Ancestors) {
|
|
1225
|
+
let descendants = &self.context.descendants;
|
|
1226
|
+
|
|
1229
1227
|
if !descendants.is_empty() {
|
|
1230
1228
|
for ancestor in cached {
|
|
1231
1229
|
if let Ancestor::Complete(ancestor_id) = ancestor {
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1230
|
+
let namespace = self
|
|
1231
|
+
.graph
|
|
1232
|
+
.declarations_mut()
|
|
1233
|
+
.get_mut(ancestor_id)
|
|
1234
|
+
.unwrap()
|
|
1235
|
+
.as_namespace_mut()
|
|
1236
|
+
.unwrap();
|
|
1237
|
+
|
|
1238
|
+
for descendant in descendants {
|
|
1239
|
+
namespace.add_descendant(*descendant);
|
|
1240
1240
|
}
|
|
1241
1241
|
}
|
|
1242
1242
|
}
|
|
@@ -1280,8 +1280,8 @@ impl<'a> Resolver<'a> {
|
|
|
1280
1280
|
Outcome::Resolved(owner_id) => {
|
|
1281
1281
|
let mut fully_qualified_name = self.graph.strings().get(&str_id).unwrap().to_string();
|
|
1282
1282
|
|
|
1283
|
-
// If the owner is a promotable constant and something is being defined inside it, promote it to
|
|
1284
|
-
// module
|
|
1283
|
+
// If the owner is a promotable constant and something is being defined inside it, promote it to an
|
|
1284
|
+
// unknown namespace. A later explicit class or module definition selects the concrete kind.
|
|
1285
1285
|
{
|
|
1286
1286
|
let owner = self.graph.declarations().get(&owner_id).unwrap();
|
|
1287
1287
|
let is_promotable_constant =
|
|
@@ -1289,7 +1289,7 @@ impl<'a> Resolver<'a> {
|
|
|
1289
1289
|
|
|
1290
1290
|
if is_promotable_constant {
|
|
1291
1291
|
self.graph.promote_constant_to_namespace(owner_id, |name, owner_id| {
|
|
1292
|
-
Declaration::Namespace(Namespace::
|
|
1292
|
+
Declaration::Namespace(Namespace::Todo(Box::new(TodoDeclaration::new(name, owner_id))))
|
|
1293
1293
|
});
|
|
1294
1294
|
self.unit_queue.push_back(Unit::Ancestors(owner_id));
|
|
1295
1295
|
}
|
|
@@ -1433,6 +1433,9 @@ impl<'a> Resolver<'a> {
|
|
|
1433
1433
|
parent_owner_id,
|
|
1434
1434
|
)))));
|
|
1435
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));
|
|
1436
1439
|
}
|
|
1437
1440
|
|
|
1438
1441
|
declaration_id
|
|
@@ -1468,139 +1471,139 @@ impl<'a> Resolver<'a> {
|
|
|
1468
1471
|
/// resolved
|
|
1469
1472
|
#[allow(clippy::too_many_lines)]
|
|
1470
1473
|
fn resolve_constant_internal(&mut self, name_id: NameId) -> Outcome {
|
|
1471
|
-
let
|
|
1474
|
+
let name = match self.graph.names().get(&name_id).unwrap() {
|
|
1475
|
+
NameRef::Resolved(resolved) => return Outcome::Resolved(*resolved.declaration_id()),
|
|
1476
|
+
NameRef::Unresolved(name) => name.as_ref().clone(),
|
|
1477
|
+
};
|
|
1472
1478
|
|
|
1473
|
-
match
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
ParentScope::TopLevel => {
|
|
1477
|
-
let result = self.search_ancestors(*OBJECT_ID, *name.str());
|
|
1479
|
+
match name.parent_scope() {
|
|
1480
|
+
ParentScope::TopLevel => {
|
|
1481
|
+
let result = self.search_ancestors(*OBJECT_ID, *name.str());
|
|
1478
1482
|
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1483
|
+
if let Outcome::Resolved(declaration_id) = result {
|
|
1484
|
+
self.graph.record_resolved_name(name_id, declaration_id);
|
|
1485
|
+
}
|
|
1482
1486
|
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1487
|
+
result
|
|
1488
|
+
}
|
|
1489
|
+
ParentScope::Attached(parent_scope_id) => {
|
|
1490
|
+
let NameRef::Resolved(parent_scope) = self.graph.names().get(parent_scope_id).unwrap() else {
|
|
1491
|
+
return Outcome::Retry {
|
|
1492
|
+
partial_ancestors: false,
|
|
1493
|
+
};
|
|
1494
|
+
};
|
|
1495
|
+
|
|
1496
|
+
let mut target_decl_id = *parent_scope.declaration_id();
|
|
1497
|
+
let target_decl = self.graph.declarations().get(&target_decl_id).unwrap();
|
|
1498
|
+
|
|
1499
|
+
// If the attached object is a constant alias, resolve it to the target namespace
|
|
1500
|
+
// (e.g., ALIAS.bar where ALIAS = Foo should create the singleton class on Foo, not ALIAS)
|
|
1501
|
+
if matches!(target_decl, Declaration::ConstantAlias(_)) {
|
|
1502
|
+
let resolved_ids = self.resolve_alias_chains(target_decl_id);
|
|
1503
|
+
|
|
1504
|
+
if resolved_ids
|
|
1505
|
+
.iter()
|
|
1506
|
+
.any(|id| matches!(self.graph.declarations().get(id), Some(Declaration::ConstantAlias(_))))
|
|
1507
|
+
{
|
|
1508
|
+
return Outcome::Retry {
|
|
1509
|
+
partial_ancestors: false,
|
|
1490
1510
|
};
|
|
1511
|
+
}
|
|
1491
1512
|
|
|
1492
|
-
|
|
1493
|
-
|
|
1513
|
+
let Some(&namespace_id) = resolved_ids
|
|
1514
|
+
.iter()
|
|
1515
|
+
.find(|id| matches!(self.graph.declarations().get(id), Some(Declaration::Namespace(_))))
|
|
1516
|
+
else {
|
|
1517
|
+
return Outcome::Unresolved;
|
|
1518
|
+
};
|
|
1494
1519
|
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
if matches!(target_decl, Declaration::ConstantAlias(_)) {
|
|
1498
|
-
let resolved_ids = self.resolve_alias_chains(target_decl_id);
|
|
1520
|
+
target_decl_id = namespace_id;
|
|
1521
|
+
}
|
|
1499
1522
|
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1523
|
+
// If we found a singleton reference with a resolved attached object parent scope, we
|
|
1524
|
+
// automatically create the singleton class
|
|
1525
|
+
let Some(singleton_id) =
|
|
1526
|
+
self.get_or_create_singleton_class(target_decl_id, SingletonAncestors::Enqueue)
|
|
1527
|
+
else {
|
|
1528
|
+
return Outcome::Unresolved;
|
|
1529
|
+
};
|
|
1530
|
+
self.graph.record_resolved_name(name_id, singleton_id);
|
|
1531
|
+
// `get_or_create_singleton_class` already enqueued the singleton's ancestors on
|
|
1532
|
+
// creation, so there is nothing to hand back for re-enqueueing.
|
|
1533
|
+
Outcome::Resolved(singleton_id)
|
|
1534
|
+
}
|
|
1535
|
+
ParentScope::None => {
|
|
1536
|
+
// Otherwise, it's a simple constant read and we can resolve it directly
|
|
1537
|
+
let result = self.run_resolution(&name);
|
|
1507
1538
|
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
return Outcome::Unresolved;
|
|
1512
|
-
};
|
|
1539
|
+
if let Outcome::Resolved(declaration_id) = result {
|
|
1540
|
+
self.graph.record_resolved_name(name_id, declaration_id);
|
|
1541
|
+
}
|
|
1513
1542
|
|
|
1514
|
-
|
|
1515
|
-
|
|
1543
|
+
result
|
|
1544
|
+
}
|
|
1545
|
+
ParentScope::Some(parent_scope_id) => {
|
|
1546
|
+
let NameRef::Resolved(parent_scope) = self.graph.names().get(parent_scope_id).unwrap() else {
|
|
1547
|
+
return Outcome::Retry {
|
|
1548
|
+
partial_ancestors: false,
|
|
1549
|
+
};
|
|
1550
|
+
};
|
|
1516
1551
|
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
self.get_or_create_singleton_class(target_decl_id, SingletonAncestors::Enqueue)
|
|
1521
|
-
else {
|
|
1522
|
-
return Outcome::Unresolved;
|
|
1523
|
-
};
|
|
1524
|
-
self.graph.record_resolved_name(name_id, singleton_id);
|
|
1525
|
-
// `get_or_create_singleton_class` already enqueued the singleton's ancestors on
|
|
1526
|
-
// creation, so there is nothing to hand back for re-enqueueing.
|
|
1527
|
-
Outcome::Resolved(singleton_id)
|
|
1528
|
-
}
|
|
1529
|
-
ParentScope::None => {
|
|
1530
|
-
// Otherwise, it's a simple constant read and we can resolve it directly
|
|
1531
|
-
let result = self.run_resolution(&name);
|
|
1552
|
+
// Resolve the namespace in case it's an alias (e.g., ALIAS::CONST where ALIAS = Foo)
|
|
1553
|
+
// An alias can have multiple targets, so we try all of them in order.
|
|
1554
|
+
let resolved_ids = self.resolve_alias_chains(*parent_scope.declaration_id());
|
|
1532
1555
|
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1556
|
+
// Search each resolved target for the constant. Return early if found.
|
|
1557
|
+
let mut missing_partial = false;
|
|
1558
|
+
let mut found_namespace = false;
|
|
1536
1559
|
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1560
|
+
for &id in &resolved_ids {
|
|
1561
|
+
match self.graph.declarations().get(&id) {
|
|
1562
|
+
Some(Declaration::ConstantAlias(_)) => {
|
|
1563
|
+
// Alias not fully resolved yet
|
|
1541
1564
|
return Outcome::Retry {
|
|
1542
1565
|
partial_ancestors: false,
|
|
1543
1566
|
};
|
|
1544
|
-
}
|
|
1567
|
+
}
|
|
1568
|
+
Some(Declaration::Namespace(_)) => {
|
|
1569
|
+
found_namespace = true;
|
|
1545
1570
|
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
// Search each resolved target for the constant. Return early if found.
|
|
1551
|
-
let mut missing_partial = false;
|
|
1552
|
-
let mut found_namespace = false;
|
|
1553
|
-
|
|
1554
|
-
for &id in &resolved_ids {
|
|
1555
|
-
match self.graph.declarations().get(&id) {
|
|
1556
|
-
Some(Declaration::ConstantAlias(_)) => {
|
|
1557
|
-
// Alias not fully resolved yet
|
|
1558
|
-
return Outcome::Retry {
|
|
1559
|
-
partial_ancestors: false,
|
|
1560
|
-
};
|
|
1571
|
+
match self.search_ancestors(id, *name.str()) {
|
|
1572
|
+
Outcome::Resolved(declaration_id) => {
|
|
1573
|
+
self.graph.record_resolved_name(name_id, declaration_id);
|
|
1574
|
+
return Outcome::Resolved(declaration_id);
|
|
1561
1575
|
}
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
Outcome::Resolved(declaration_id) => {
|
|
1567
|
-
self.graph.record_resolved_name(name_id, declaration_id);
|
|
1568
|
-
return Outcome::Resolved(declaration_id);
|
|
1569
|
-
}
|
|
1570
|
-
Outcome::Retry {
|
|
1571
|
-
partial_ancestors: true,
|
|
1572
|
-
} => {
|
|
1573
|
-
missing_partial = true;
|
|
1574
|
-
}
|
|
1575
|
-
Outcome::Unresolved => {}
|
|
1576
|
-
Outcome::Retry {
|
|
1577
|
-
partial_ancestors: false,
|
|
1578
|
-
} => {
|
|
1579
|
-
unreachable!("search_ancestors never returns a non-partial Retry")
|
|
1580
|
-
}
|
|
1581
|
-
}
|
|
1576
|
+
Outcome::Retry {
|
|
1577
|
+
partial_ancestors: true,
|
|
1578
|
+
} => {
|
|
1579
|
+
missing_partial = true;
|
|
1582
1580
|
}
|
|
1583
|
-
|
|
1584
|
-
|
|
1581
|
+
Outcome::Unresolved => {}
|
|
1582
|
+
Outcome::Retry {
|
|
1583
|
+
partial_ancestors: false,
|
|
1584
|
+
} => {
|
|
1585
|
+
unreachable!("search_ancestors never returns a non-partial Retry")
|
|
1585
1586
|
}
|
|
1586
1587
|
}
|
|
1587
1588
|
}
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
if !found_namespace {
|
|
1591
|
-
return Outcome::Unresolved;
|
|
1592
|
-
}
|
|
1593
|
-
|
|
1594
|
-
// Member not found in any namespace yet - retry in case it's added later. `partial` records
|
|
1595
|
-
// whether the miss was due to a still-partial ancestor chain, so the unit is re-checked once
|
|
1596
|
-
// that chain completes.
|
|
1597
|
-
Outcome::Retry {
|
|
1598
|
-
partial_ancestors: missing_partial,
|
|
1589
|
+
_ => {
|
|
1590
|
+
// Not a namespace (e.g., a constant) - skip
|
|
1599
1591
|
}
|
|
1600
1592
|
}
|
|
1601
1593
|
}
|
|
1594
|
+
|
|
1595
|
+
// If no namespaces were found, this constant path can never resolve.
|
|
1596
|
+
if !found_namespace {
|
|
1597
|
+
return Outcome::Unresolved;
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
// Member not found in any namespace yet - retry in case it's added later. `partial` records
|
|
1601
|
+
// whether the miss was due to a still-partial ancestor chain, so the unit is re-checked once
|
|
1602
|
+
// that chain completes.
|
|
1603
|
+
Outcome::Retry {
|
|
1604
|
+
partial_ancestors: missing_partial,
|
|
1605
|
+
}
|
|
1602
1606
|
}
|
|
1603
|
-
NameRef::Resolved(resolved) => Outcome::Resolved(*resolved.declaration_id()),
|
|
1604
1607
|
}
|
|
1605
1608
|
}
|
|
1606
1609
|
|
|
@@ -1725,8 +1728,13 @@ impl<'a> Resolver<'a> {
|
|
|
1725
1728
|
if is_module || chain_incomplete {
|
|
1726
1729
|
let object_outcome = self.search_ancestors(*OBJECT_ID, str_id);
|
|
1727
1730
|
|
|
1728
|
-
|
|
1729
|
-
|
|
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 => {}
|
|
1730
1738
|
}
|
|
1731
1739
|
}
|
|
1732
1740
|
|
|
@@ -1741,7 +1749,7 @@ impl<'a> Resolver<'a> {
|
|
|
1741
1749
|
|
|
1742
1750
|
/// Search for a member in a declaration's ancestor chain.
|
|
1743
1751
|
fn search_ancestors(&mut self, declaration_id: DeclarationId, str_id: StringId) -> Outcome {
|
|
1744
|
-
match self.
|
|
1752
|
+
match self.linearize_ancestors(declaration_id) {
|
|
1745
1753
|
Ancestors::Complete(ids) | Ancestors::Cyclic(ids) => ids
|
|
1746
1754
|
.iter()
|
|
1747
1755
|
.find_map(|ancestor_id| {
|
|
@@ -1819,9 +1827,15 @@ impl<'a> Resolver<'a> {
|
|
|
1819
1827
|
Outcome::Unresolved
|
|
1820
1828
|
}
|
|
1821
1829
|
|
|
1822
|
-
///
|
|
1823
|
-
///
|
|
1824
|
-
///
|
|
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:
|
|
1825
1839
|
///
|
|
1826
1840
|
/// ```ruby
|
|
1827
1841
|
/// module Foo
|
|
@@ -1831,9 +1845,8 @@ impl<'a> Resolver<'a> {
|
|
|
1831
1845
|
/// end
|
|
1832
1846
|
/// ```
|
|
1833
1847
|
///
|
|
1834
|
-
///
|
|
1835
|
-
///
|
|
1836
|
-
/// 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:
|
|
1837
1850
|
///
|
|
1838
1851
|
/// ```ruby
|
|
1839
1852
|
/// module Foo
|
|
@@ -1843,64 +1856,9 @@ impl<'a> Resolver<'a> {
|
|
|
1843
1856
|
/// end
|
|
1844
1857
|
/// ```
|
|
1845
1858
|
///
|
|
1846
|
-
/// In this case
|
|
1847
|
-
///
|
|
1848
|
-
///
|
|
1849
|
-
/// parent scopes in the entire nesting, not just for the name itself
|
|
1850
|
-
///
|
|
1851
|
-
/// Compute the depth of a name in the graph by recursively summing the depths of its
|
|
1852
|
-
/// `parent_scope` and `nesting` chains. Results are memoized in `cache` (`NameId` → depth)
|
|
1853
|
-
/// so each name is computed at most once across all calls.
|
|
1854
|
-
///
|
|
1855
|
-
/// Depth represents the total complexity of a name's position in the namespace hierarchy.
|
|
1856
|
-
/// For example, in `module Foo; module Bar; class Baz; end; end; end`, Foo has depth 1
|
|
1857
|
-
/// (top-level), Bar has depth 2, and Baz has depth 3.
|
|
1858
|
-
///
|
|
1859
|
-
/// # Panics
|
|
1860
|
-
///
|
|
1861
|
-
/// Will panic if there is inconsistent data in the graph
|
|
1862
|
-
fn name_depth(
|
|
1863
|
-
name_id: NameId,
|
|
1864
|
-
names: &IdentityHashMap<NameId, NameRef>,
|
|
1865
|
-
cache: &mut IdentityHashMap<NameId, u32>,
|
|
1866
|
-
) -> u32 {
|
|
1867
|
-
if let Some(&depth) = cache.get(&name_id) {
|
|
1868
|
-
return depth;
|
|
1869
|
-
}
|
|
1870
|
-
|
|
1871
|
-
let name = names.get(&name_id).unwrap();
|
|
1872
|
-
|
|
1873
|
-
let depth = if name.parent_scope().is_top_level() {
|
|
1874
|
-
1
|
|
1875
|
-
} else {
|
|
1876
|
-
let parent_depth = name.parent_scope().map_or(0, |id| Self::name_depth(*id, names, cache));
|
|
1877
|
-
|
|
1878
|
-
let nesting_depth = name.nesting().map_or(0, |id| Self::name_depth(id, names, cache));
|
|
1879
|
-
|
|
1880
|
-
parent_depth + nesting_depth + 1
|
|
1881
|
-
};
|
|
1882
|
-
|
|
1883
|
-
cache.insert(name_id, depth);
|
|
1884
|
-
depth
|
|
1885
|
-
}
|
|
1886
|
-
|
|
1887
|
-
/// Pre-compute name depths for all names into a `NameId → depth` map. Each name's depth is
|
|
1888
|
-
/// computed once via memoized recursion, then used as an O(1) lookup key during sorting in
|
|
1889
|
-
/// `prepare_units`.
|
|
1890
|
-
pub(crate) fn compute_name_depths(names: &IdentityHashMap<NameId, NameRef>) -> IdentityHashMap<NameId, u32> {
|
|
1891
|
-
let mut cache = IdentityHashMap::with_capacity_and_hasher(names.len(), IdentityHashBuilder);
|
|
1892
|
-
|
|
1893
|
-
for &name_id in names.keys() {
|
|
1894
|
-
Self::name_depth(name_id, names, &mut cache);
|
|
1895
|
-
}
|
|
1896
|
-
|
|
1897
|
-
cache
|
|
1898
|
-
}
|
|
1899
|
-
|
|
1900
|
-
/// Drains `pending_work` and classifies items into the resolution queue.
|
|
1901
|
-
/// Namespace definitions and constant references are sorted by name depth for deterministic
|
|
1902
|
-
/// resolution order. Non-namespace definitions (methods, attrs, variables) are returned
|
|
1903
|
-
/// 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.
|
|
1904
1862
|
fn prepare_units(&mut self) -> Vec<DefinitionId> {
|
|
1905
1863
|
let work = self.graph.take_pending_work();
|
|
1906
1864
|
let estimated = work.len() / 2;
|
|
@@ -1910,7 +1868,25 @@ impl<'a> Resolver<'a> {
|
|
|
1910
1868
|
let mut const_refs = Vec::new();
|
|
1911
1869
|
let mut ancestors = vec![*BASIC_OBJECT_ID, *KERNEL_ID, *OBJECT_ID, *MODULE_ID, *CLASS_ID];
|
|
1912
1870
|
let names = self.graph.names();
|
|
1913
|
-
|
|
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();
|
|
1874
|
+
|
|
1875
|
+
// Precompute the lexicographic rank of every document URI. Definitions and references are sorted by
|
|
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.
|
|
1878
|
+
let mut uris: Vec<(&str, UriId)> = self
|
|
1879
|
+
.graph
|
|
1880
|
+
.documents()
|
|
1881
|
+
.iter()
|
|
1882
|
+
.map(|(uri_id, document)| (document.uri(), *uri_id))
|
|
1883
|
+
.collect();
|
|
1884
|
+
uris.sort_unstable();
|
|
1885
|
+
let mut uri_ranks: IdentityHashMap<UriId, u32> =
|
|
1886
|
+
IdentityHashMap::with_capacity_and_hasher(uris.len(), IdentityHashBuilder);
|
|
1887
|
+
for (rank, (_, uri_id)) in uris.into_iter().enumerate() {
|
|
1888
|
+
uri_ranks.insert(uri_id, u32::try_from(rank).expect("more documents than u32::MAX"));
|
|
1889
|
+
}
|
|
1914
1890
|
|
|
1915
1891
|
// Dedup: when multiple files are indexed before resolution runs, pending_work accumulates
|
|
1916
1892
|
// and the same definition/reference ID can be enqueued more than once.
|
|
@@ -1928,23 +1904,28 @@ impl<'a> Resolver<'a> {
|
|
|
1928
1904
|
let Some(definition) = self.graph.definitions().get(&id) else {
|
|
1929
1905
|
continue;
|
|
1930
1906
|
};
|
|
1931
|
-
let
|
|
1907
|
+
let uri_rank = *uri_ranks.get(definition.uri_id()).unwrap();
|
|
1932
1908
|
|
|
1933
1909
|
match definition {
|
|
1934
1910
|
Definition::Class(def) => {
|
|
1935
|
-
|
|
1911
|
+
let depth = depth_of(def.name_id());
|
|
1912
|
+
definitions.push((Unit::Definition(id), (depth, uri_rank, definition.offset())));
|
|
1936
1913
|
}
|
|
1937
1914
|
Definition::Module(def) => {
|
|
1938
|
-
|
|
1915
|
+
let depth = depth_of(def.name_id());
|
|
1916
|
+
definitions.push((Unit::Definition(id), (depth, uri_rank, definition.offset())));
|
|
1939
1917
|
}
|
|
1940
1918
|
Definition::Constant(def) => {
|
|
1941
|
-
|
|
1919
|
+
let depth = depth_of(def.name_id());
|
|
1920
|
+
definitions.push((Unit::Definition(id), (depth, uri_rank, definition.offset())));
|
|
1942
1921
|
}
|
|
1943
1922
|
Definition::ConstantAlias(def) => {
|
|
1944
|
-
|
|
1923
|
+
let depth = depth_of(def.name_id());
|
|
1924
|
+
definitions.push((Unit::Definition(id), (depth, uri_rank, definition.offset())));
|
|
1945
1925
|
}
|
|
1946
1926
|
Definition::SingletonClass(def) => {
|
|
1947
|
-
|
|
1927
|
+
let depth = depth_of(def.name_id());
|
|
1928
|
+
definitions.push((Unit::Definition(id), (depth, uri_rank, definition.offset())));
|
|
1948
1929
|
}
|
|
1949
1930
|
// SelfReceiver methods create singleton classes, which need
|
|
1950
1931
|
// ancestor linearization. Process them in the convergence loop
|
|
@@ -1965,11 +1946,9 @@ impl<'a> Resolver<'a> {
|
|
|
1965
1946
|
let Some(constant_ref) = self.graph.constant_references().get(&id) else {
|
|
1966
1947
|
continue;
|
|
1967
1948
|
};
|
|
1968
|
-
let
|
|
1969
|
-
|
|
1970
|
-
|
|
1971
|
-
(*constant_ref.name_id(), uri, constant_ref.offset()),
|
|
1972
|
-
));
|
|
1949
|
+
let uri_rank = *uri_ranks.get(&constant_ref.uri_id()).unwrap();
|
|
1950
|
+
let depth = depth_of(constant_ref.name_id());
|
|
1951
|
+
const_refs.push((Unit::ConstantRef(id), (depth, uri_rank, constant_ref.offset())));
|
|
1973
1952
|
}
|
|
1974
1953
|
Unit::Ancestors(id) => {
|
|
1975
1954
|
if !seen_ancestors.insert(id) {
|
|
@@ -1984,14 +1963,10 @@ impl<'a> Resolver<'a> {
|
|
|
1984
1963
|
}
|
|
1985
1964
|
|
|
1986
1965
|
// Sort namespaces based on their name complexity so that simpler names are always first
|
|
1987
|
-
// When the depth is the same, sort by URI and offset to maintain determinism
|
|
1988
|
-
definitions.
|
|
1989
|
-
(depths.get(name_a).unwrap(), uri_a, offset_a).cmp(&(depths.get(name_b).unwrap(), uri_b, offset_b))
|
|
1990
|
-
});
|
|
1966
|
+
// When the depth is the same, sort by URI rank and offset to maintain determinism
|
|
1967
|
+
definitions.sort_unstable_by_key(|(_, key)| *key);
|
|
1991
1968
|
|
|
1992
|
-
const_refs.
|
|
1993
|
-
(depths.get(name_a).unwrap(), uri_a, offset_a).cmp(&(depths.get(name_b).unwrap(), uri_b, offset_b))
|
|
1994
|
-
});
|
|
1969
|
+
const_refs.sort_unstable_by_key(|(_, key)| *key);
|
|
1995
1970
|
|
|
1996
1971
|
others.sort_unstable_by_key(|(_, key)| *key);
|
|
1997
1972
|
|
|
@@ -2011,15 +1986,11 @@ impl<'a> Resolver<'a> {
|
|
|
2011
1986
|
/// - Class: parent is the singleton class of the original parent class
|
|
2012
1987
|
/// - Singleton class: recurse as many times as necessary to wrap the original attached object's parent class
|
|
2013
1988
|
fn singleton_parent_id(&mut self, attached_id: DeclarationId) -> (DeclarationId, bool) {
|
|
2014
|
-
// Base case: if we reached `BasicObject`, then the parent is `Class`
|
|
2015
|
-
if attached_id == *BASIC_OBJECT_ID {
|
|
2016
|
-
return (*CLASS_ID, false);
|
|
2017
|
-
}
|
|
2018
|
-
|
|
2019
1989
|
let decl = self.graph.declarations().get(&attached_id).unwrap();
|
|
2020
1990
|
|
|
2021
1991
|
match decl {
|
|
2022
|
-
|
|
1992
|
+
// Todo may later become a class or module. Until then, use Module as the singleton-parent fallback.
|
|
1993
|
+
Declaration::Namespace(Namespace::Module(_) | Namespace::Todo(_)) => (*MODULE_ID, false),
|
|
2023
1994
|
Declaration::Namespace(Namespace::SingletonClass(_)) => {
|
|
2024
1995
|
// For singleton classes, we keep recursively wrapping parents until we can reach the original attached
|
|
2025
1996
|
// object
|
|
@@ -2033,14 +2004,15 @@ impl<'a> Resolver<'a> {
|
|
|
2033
2004
|
)
|
|
2034
2005
|
}
|
|
2035
2006
|
Declaration::Namespace(Namespace::Class(_)) => {
|
|
2036
|
-
// For classes (the regular case), we need to return the singleton class of its
|
|
2037
|
-
let
|
|
2038
|
-
|
|
2039
|
-
|
|
2007
|
+
// For classes (the regular case), we need to return the singleton class of its superclass
|
|
2008
|
+
let Some((superclass_id, unresolved_superclass)) = self.get_superclass(attached_id) else {
|
|
2009
|
+
// BasicObject has no superclass, but its singleton class inherits from Class
|
|
2010
|
+
return (*CLASS_ID, false);
|
|
2011
|
+
};
|
|
2040
2012
|
(
|
|
2041
|
-
self.get_or_create_singleton_class(
|
|
2042
|
-
.expect("
|
|
2043
|
-
|
|
2013
|
+
self.get_or_create_singleton_class(superclass_id, SingletonAncestors::Deferred)
|
|
2014
|
+
.expect("superclass should always be a namespace"),
|
|
2015
|
+
unresolved_superclass.is_some(),
|
|
2044
2016
|
)
|
|
2045
2017
|
}
|
|
2046
2018
|
_ => {
|
|
@@ -2051,11 +2023,18 @@ impl<'a> Resolver<'a> {
|
|
|
2051
2023
|
}
|
|
2052
2024
|
}
|
|
2053
2025
|
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2026
|
+
/// Returns the selected superclass and any unresolved explicit superclass. The top-level `BasicObject` declaration
|
|
2027
|
+
/// is Ruby's root class and is the only class without a superclass.
|
|
2028
|
+
fn get_superclass(&self, declaration_id: DeclarationId) -> Option<(DeclarationId, Option<NameId>)> {
|
|
2029
|
+
if declaration_id == *BASIC_OBJECT_ID {
|
|
2030
|
+
return None;
|
|
2031
|
+
}
|
|
2057
2032
|
|
|
2058
|
-
|
|
2033
|
+
let declaration = self.graph.declarations().get(&declaration_id).unwrap();
|
|
2034
|
+
let mut explicit_superclasses = Vec::new();
|
|
2035
|
+
let mut unresolved_superclass = None;
|
|
2036
|
+
|
|
2037
|
+
for definition_id in declaration.definitions() {
|
|
2059
2038
|
let definition = self.graph.definitions().get(definition_id).unwrap();
|
|
2060
2039
|
|
|
2061
2040
|
if let Definition::Class(class) = definition
|
|
@@ -2066,57 +2045,51 @@ impl<'a> Resolver<'a> {
|
|
|
2066
2045
|
|
|
2067
2046
|
match name {
|
|
2068
2047
|
NameRef::Resolved(resolved) => {
|
|
2069
|
-
if let Some(
|
|
2070
|
-
|
|
2048
|
+
if let Some(superclass_id) = self.resolve_to_namespace(*resolved.declaration_id()) {
|
|
2049
|
+
explicit_superclasses.push(superclass_id);
|
|
2071
2050
|
}
|
|
2072
2051
|
}
|
|
2073
2052
|
NameRef::Unresolved(_) => {
|
|
2074
|
-
|
|
2053
|
+
unresolved_superclass = Some(*constant_reference.name_id());
|
|
2075
2054
|
}
|
|
2076
2055
|
}
|
|
2077
2056
|
}
|
|
2078
2057
|
}
|
|
2079
2058
|
|
|
2080
|
-
// If there's more than one
|
|
2059
|
+
// If there's more than one superclass that isn't `Object` and they are different, then there's a superclass
|
|
2081
2060
|
// mismatch error. TODO: We should add a diagnostic here
|
|
2082
|
-
(
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
)
|
|
2061
|
+
Some((
|
|
2062
|
+
explicit_superclasses.first().copied().unwrap_or(*OBJECT_ID),
|
|
2063
|
+
unresolved_superclass,
|
|
2064
|
+
))
|
|
2086
2065
|
}
|
|
2087
2066
|
|
|
2088
|
-
fn
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
context: &mut LinearizationContext,
|
|
2092
|
-
) -> Ancestors {
|
|
2093
|
-
let (picked_parent, unresolved_parent) = self.get_parent_class(definition_ids);
|
|
2094
|
-
let mut result = self.linearize_ancestors(picked_parent, context);
|
|
2067
|
+
fn linearize_superclass(&mut self, declaration_id: DeclarationId, state: &mut ChainState) -> Option<Ancestors> {
|
|
2068
|
+
let (superclass_id, unresolved_superclass) = self.get_superclass(declaration_id)?;
|
|
2069
|
+
let mut result = self.linearize_ancestors(superclass_id);
|
|
2095
2070
|
|
|
2096
|
-
if let Some(name_id) =
|
|
2097
|
-
|
|
2071
|
+
if let Some(name_id) = unresolved_superclass {
|
|
2072
|
+
state.partial = true;
|
|
2098
2073
|
|
|
2099
|
-
// Insert the unresolved
|
|
2074
|
+
// Insert the unresolved superclass as a Partial ancestor at the front of the chain, so it
|
|
2100
2075
|
// appears before the default Object ancestors
|
|
2101
2076
|
let ancestors = match &mut result {
|
|
2102
2077
|
Ancestors::Complete(ids) | Ancestors::Cyclic(ids) | Ancestors::Partial(ids) => ids,
|
|
2103
2078
|
};
|
|
2104
2079
|
ancestors.insert(0, Ancestor::Partial(name_id));
|
|
2105
2080
|
|
|
2106
|
-
result.to_partial()
|
|
2107
|
-
} else {
|
|
2108
|
-
result
|
|
2081
|
+
result = result.to_partial();
|
|
2109
2082
|
}
|
|
2110
|
-
}
|
|
2111
2083
|
|
|
2112
|
-
|
|
2113
|
-
|
|
2084
|
+
Some(result)
|
|
2085
|
+
}
|
|
2114
2086
|
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
Definition::
|
|
2118
|
-
Definition::
|
|
2119
|
-
|
|
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
|
+
_ => &[],
|
|
2120
2093
|
}
|
|
2121
2094
|
}
|
|
2122
2095
|
}
|