rubydex 0.2.3 → 0.2.5
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/THIRD_PARTY_LICENSES.html +2 -2
- data/exe/rubydex_mcp +17 -0
- data/ext/rubydex/definition.c +56 -0
- data/ext/rubydex/extconf.rb +8 -0
- data/lib/rubydex/bin/rubydex_mcp.exe +0 -0
- data/lib/rubydex/declaration.rb +12 -0
- data/lib/rubydex/graph.rb +3 -1
- data/lib/rubydex/version.rb +1 -1
- data/rbi/rubydex.rbi +14 -1
- data/rust/Cargo.lock +4 -4
- data/rust/rubydex/src/indexing/local_graph.rs +38 -0
- data/rust/rubydex/src/indexing/ruby_indexer.rs +6 -0
- data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +5 -1
- data/rust/rubydex/src/indexing.rs +38 -12
- data/rust/rubydex/src/lib.rs +1 -0
- data/rust/rubydex/src/listing.rs +14 -3
- data/rust/rubydex/src/main.rs +27 -2
- data/rust/rubydex/src/model/definitions.rs +7 -18
- data/rust/rubydex/src/model/graph.rs +21 -4
- data/rust/rubydex/src/model/ids.rs +27 -1
- data/rust/rubydex/src/operation/applier.rs +519 -0
- data/rust/rubydex/src/operation/mod.rs +284 -0
- data/rust/rubydex/src/operation/printer.rs +260 -0
- data/rust/rubydex/src/operation/ruby_builder.rs +2915 -0
- data/rust/rubydex/src/resolution.rs +6 -1
- data/rust/rubydex/src/resolution_tests.rs +217 -209
- data/rust/rubydex/src/test_utils/graph_test.rs +19 -4
- data/rust/rubydex/src/test_utils/local_graph_test.rs +7 -6
- data/rust/rubydex-mcp/Cargo.toml +1 -1
- data/rust/rubydex-mcp/src/server.rs +10 -7
- data/rust/rubydex-sys/src/definition_api.rs +24 -0
- data/rust/rubydex-sys/src/graph_api.rs +1 -1
- metadata +9 -2
|
@@ -15,7 +15,7 @@ use crate::model::name::{Name, NameRef, ParentScope, ResolvedName};
|
|
|
15
15
|
use crate::model::references::{ConstantReference, MethodRef};
|
|
16
16
|
use crate::model::string_ref::StringRef;
|
|
17
17
|
use crate::model::visibility::Visibility;
|
|
18
|
-
use crate::stats;
|
|
18
|
+
use crate::{query, stats};
|
|
19
19
|
|
|
20
20
|
/// An entity whose validity depends on a particular `NameId`.
|
|
21
21
|
/// Used as the value type in the `name_dependents` reverse index.
|
|
@@ -650,7 +650,7 @@ impl Graph {
|
|
|
650
650
|
let definitions = declaration.definitions();
|
|
651
651
|
|
|
652
652
|
match declaration {
|
|
653
|
-
Declaration::Namespace(Namespace::Class(_) | Namespace::Module(_))
|
|
653
|
+
Declaration::Namespace(Namespace::Class(_) | Namespace::Module(_) | Namespace::Todo(_))
|
|
654
654
|
| Declaration::Constant(_)
|
|
655
655
|
| Declaration::ConstantAlias(_) => {
|
|
656
656
|
for def_id in definitions.iter().rev() {
|
|
@@ -661,25 +661,42 @@ impl Graph {
|
|
|
661
661
|
Some(Visibility::Public)
|
|
662
662
|
}
|
|
663
663
|
Declaration::Method(_) => {
|
|
664
|
+
let mut latest_alias: Option<DefinitionId> = None;
|
|
665
|
+
|
|
664
666
|
for def_id in definitions.iter().rev() {
|
|
665
667
|
let Some(definition) = self.definitions.get(def_id) else {
|
|
666
668
|
continue;
|
|
667
669
|
};
|
|
670
|
+
|
|
668
671
|
let visibility = match definition {
|
|
669
672
|
Definition::MethodVisibility(vis) => Some(*vis.visibility()),
|
|
670
673
|
Definition::Method(method) => Some(*method.visibility()),
|
|
671
674
|
Definition::AttrAccessor(attr) => Some(*attr.visibility()),
|
|
672
675
|
Definition::AttrReader(attr) => Some(*attr.visibility()),
|
|
673
676
|
Definition::AttrWriter(attr) => Some(*attr.visibility()),
|
|
677
|
+
Definition::MethodAlias(_) => {
|
|
678
|
+
if latest_alias.is_none() {
|
|
679
|
+
latest_alias = Some(*def_id);
|
|
680
|
+
}
|
|
681
|
+
None
|
|
682
|
+
}
|
|
674
683
|
_ => None,
|
|
675
684
|
};
|
|
685
|
+
|
|
676
686
|
if visibility.is_some() {
|
|
677
687
|
return visibility;
|
|
678
688
|
}
|
|
679
689
|
}
|
|
680
|
-
|
|
690
|
+
|
|
691
|
+
if let Some(alias_def_id) = latest_alias
|
|
692
|
+
&& let Ok(target_id) = query::follow_method_alias(self, alias_def_id)
|
|
693
|
+
{
|
|
694
|
+
return self.visibility(&target_id);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
Some(Visibility::Public)
|
|
681
698
|
}
|
|
682
|
-
Declaration::Namespace(Namespace::SingletonClass(_)
|
|
699
|
+
Declaration::Namespace(Namespace::SingletonClass(_))
|
|
683
700
|
| Declaration::GlobalVariable(_)
|
|
684
701
|
| Declaration::InstanceVariable(_)
|
|
685
702
|
| Declaration::ClassVariable(_) => None,
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
use crate::{
|
|
1
|
+
use crate::{
|
|
2
|
+
assert_mem_size,
|
|
3
|
+
model::{definitions::Receiver, id::Id},
|
|
4
|
+
offset::Offset,
|
|
5
|
+
};
|
|
2
6
|
|
|
3
7
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
|
|
4
8
|
pub struct DeclarationMarker;
|
|
@@ -12,6 +16,28 @@ pub struct DefinitionMarker;
|
|
|
12
16
|
pub type DefinitionId = Id<DefinitionMarker>;
|
|
13
17
|
assert_mem_size!(DefinitionId, 8);
|
|
14
18
|
|
|
19
|
+
#[must_use]
|
|
20
|
+
pub fn namespace_definition_id(uri_id: UriId, offset: &Offset, name_id: NameId) -> DefinitionId {
|
|
21
|
+
DefinitionId::from(&format!("{}{}{}", *uri_id, offset.start(), *name_id))
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
#[must_use]
|
|
25
|
+
pub fn method_definition_id(
|
|
26
|
+
uri_id: UriId,
|
|
27
|
+
offset: &Offset,
|
|
28
|
+
str_id: StringId,
|
|
29
|
+
receiver: Option<&Receiver>,
|
|
30
|
+
) -> DefinitionId {
|
|
31
|
+
let mut formatted_id = format!("{}{}{}", *uri_id, offset.start(), *str_id);
|
|
32
|
+
if let Some(receiver) = receiver {
|
|
33
|
+
match receiver {
|
|
34
|
+
Receiver::SelfReceiver(def_id) => formatted_id.push_str(&def_id.to_string()),
|
|
35
|
+
Receiver::ConstantReceiver(name_id) => formatted_id.push_str(&name_id.to_string()),
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
DefinitionId::from(&formatted_id)
|
|
39
|
+
}
|
|
40
|
+
|
|
15
41
|
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)]
|
|
16
42
|
pub struct UriMarker;
|
|
17
43
|
// UriId represents the ID of a URI, which is the unique identifier for a document
|
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
//! Converts an `OperationBuilderResult` into a `LocalGraph` by walking operations and creating definitions.
|
|
2
|
+
//!
|
|
3
|
+
//! This is the second phase of the two-phase operation pipeline:
|
|
4
|
+
//! 1. `RubyOperationBuilder` parses source → produces ordered operations
|
|
5
|
+
//! 2. `apply_operations` walks operations → creates definitions in a `LocalGraph`
|
|
6
|
+
//!
|
|
7
|
+
//! The applier maintains its own scope stack to derive `lexical_nesting_id` for definitions.
|
|
8
|
+
//! Operations carry only their own data; scope context comes from Enter/Exit scope operations.
|
|
9
|
+
|
|
10
|
+
use std::collections::HashMap;
|
|
11
|
+
|
|
12
|
+
use crate::indexing::local_graph::LocalGraph;
|
|
13
|
+
use crate::model::definitions::{
|
|
14
|
+
AttrAccessorDefinition, AttrReaderDefinition, AttrWriterDefinition, ClassDefinition, ClassVariableDefinition,
|
|
15
|
+
ConstantAliasDefinition, ConstantDefinition, ConstantVisibilityDefinition, Definition, ExtendDefinition,
|
|
16
|
+
GlobalVariableAliasDefinition, GlobalVariableDefinition, IncludeDefinition, InstanceVariableDefinition,
|
|
17
|
+
MethodAliasDefinition, MethodDefinition, MethodVisibilityDefinition, Mixin, ModuleDefinition, PrependDefinition,
|
|
18
|
+
Receiver, SingletonClassDefinition,
|
|
19
|
+
};
|
|
20
|
+
use crate::model::ids::{ConstantReferenceId, DefinitionId, NameId};
|
|
21
|
+
use crate::model::references::{ConstantReference, MethodRef};
|
|
22
|
+
use crate::model::visibility::Visibility;
|
|
23
|
+
use crate::operation::ruby_builder::OperationBuilderResult;
|
|
24
|
+
use crate::operation::{
|
|
25
|
+
AliasConstant, AliasGlobalVariable, AliasMethod, AttrKind, DefineAttribute, DefineClassVariable, DefineConstant,
|
|
26
|
+
DefineGlobalVariable, DefineInstanceVariable, EnterClass, EnterMethod, EnterModule, EnterSingletonClass, MixinKind,
|
|
27
|
+
Operation, ReferenceConstant, ReferenceMethod, SetConstantVisibility, SetMethodVisibility, Target,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
enum ApplierScope {
|
|
31
|
+
Namespace {
|
|
32
|
+
definition_id: DefinitionId,
|
|
33
|
+
is_lexical_scope: bool,
|
|
34
|
+
},
|
|
35
|
+
Method {
|
|
36
|
+
definition_id: DefinitionId,
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
struct OperationApplier {
|
|
41
|
+
local_graph: LocalGraph,
|
|
42
|
+
scope_stack: Vec<ApplierScope>,
|
|
43
|
+
scope_visibility: HashMap<Option<DefinitionId>, Visibility>,
|
|
44
|
+
// Maps the most recently emitted ReferenceConstant per name. The builder emits
|
|
45
|
+
// ReferenceConstant immediately before the operation that consumes it (Mixin,
|
|
46
|
+
// EnterClass superclass, SetConstantVisibility), so the last entry always wins.
|
|
47
|
+
constant_ref_ids: HashMap<NameId, ConstantReferenceId>,
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
impl OperationApplier {
|
|
51
|
+
fn current_owner_id(&self) -> Option<DefinitionId> {
|
|
52
|
+
self.scope_stack.iter().rev().find_map(|scope| match scope {
|
|
53
|
+
ApplierScope::Namespace { definition_id, .. } => Some(*definition_id),
|
|
54
|
+
ApplierScope::Method { .. } => None,
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
fn current_lexical_scope_id(&self) -> Option<DefinitionId> {
|
|
59
|
+
self.scope_stack.iter().rev().find_map(|scope| match scope {
|
|
60
|
+
ApplierScope::Namespace {
|
|
61
|
+
definition_id,
|
|
62
|
+
is_lexical_scope: true,
|
|
63
|
+
} => Some(*definition_id),
|
|
64
|
+
_ => None,
|
|
65
|
+
})
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
fn current_scope_id(&self) -> Option<DefinitionId> {
|
|
69
|
+
self.scope_stack.last().map(|scope| match scope {
|
|
70
|
+
ApplierScope::Namespace { definition_id, .. } | ApplierScope::Method { definition_id } => *definition_id,
|
|
71
|
+
})
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
fn resolve_receiver(&self, receiver: Option<&Target>) -> Option<Receiver> {
|
|
75
|
+
let current_owner_id = self.current_owner_id();
|
|
76
|
+
match receiver {
|
|
77
|
+
Some(Target::ExplicitSelf) => current_owner_id.map(Receiver::SelfReceiver),
|
|
78
|
+
Some(Target::Constant(name_id)) => Some(Receiver::ConstantReceiver(*name_id)),
|
|
79
|
+
Some(Target::Other) | None => None,
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
fn resolve_visibility(&self, has_receiver: bool) -> Visibility {
|
|
84
|
+
if has_receiver {
|
|
85
|
+
return Visibility::Public;
|
|
86
|
+
}
|
|
87
|
+
let scope = self.current_owner_id();
|
|
88
|
+
let default = self
|
|
89
|
+
.scope_visibility
|
|
90
|
+
.get(&scope)
|
|
91
|
+
.copied()
|
|
92
|
+
.unwrap_or(if scope.is_none() {
|
|
93
|
+
Visibility::Private
|
|
94
|
+
} else {
|
|
95
|
+
Visibility::Public
|
|
96
|
+
});
|
|
97
|
+
match default {
|
|
98
|
+
Visibility::ModuleFunction => Visibility::Private,
|
|
99
|
+
v => v,
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
fn add_member(&mut self, owner_id: Option<DefinitionId>, member_id: DefinitionId) {
|
|
104
|
+
let Some(owner_id) = owner_id else {
|
|
105
|
+
return;
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
let Some(owner) = self.local_graph.get_definition_mut(owner_id) else {
|
|
109
|
+
return;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
match owner {
|
|
113
|
+
Definition::Class(class) => class.add_member(member_id),
|
|
114
|
+
Definition::Module(module) => module.add_member(member_id),
|
|
115
|
+
Definition::SingletonClass(singleton) => singleton.add_member(member_id),
|
|
116
|
+
_ => {}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
impl OperationApplier {
|
|
122
|
+
fn apply_operation(&mut self, op: Operation) {
|
|
123
|
+
match op {
|
|
124
|
+
Operation::EnterClass(op) => self.apply_enter_class(op),
|
|
125
|
+
Operation::EnterModule(op) => self.apply_enter_module(op),
|
|
126
|
+
Operation::EnterSingletonClass(op) => self.apply_enter_singleton_class(op),
|
|
127
|
+
Operation::EnterMethod(op) => self.apply_enter_method(op),
|
|
128
|
+
Operation::ExitScope => {
|
|
129
|
+
debug_assert!(!self.scope_stack.is_empty(), "ExitScope with empty scope stack");
|
|
130
|
+
self.scope_stack.pop();
|
|
131
|
+
}
|
|
132
|
+
Operation::AliasMethod(op) => self.apply_alias_method(op),
|
|
133
|
+
Operation::SetMethodVisibility(op) => self.apply_set_method_visibility(op),
|
|
134
|
+
Operation::SetDefaultVisibility(op) => {
|
|
135
|
+
let scope = self.current_owner_id();
|
|
136
|
+
self.scope_visibility.insert(scope, op.visibility);
|
|
137
|
+
}
|
|
138
|
+
Operation::DefineConstant(op) => self.apply_define_constant(op),
|
|
139
|
+
Operation::AliasConstant(op) => self.apply_alias_constant(op),
|
|
140
|
+
Operation::SetConstantVisibility(op) => self.apply_set_constant_visibility(op),
|
|
141
|
+
Operation::Mixin(ref op) => self.apply_mixin(op),
|
|
142
|
+
Operation::DefineAttribute(op) => self.apply_define_attribute(op),
|
|
143
|
+
Operation::DefineGlobalVariable(op) => self.apply_define_global_variable(op),
|
|
144
|
+
Operation::DefineInstanceVariable(op) => self.apply_define_instance_variable(op),
|
|
145
|
+
Operation::DefineClassVariable(op) => self.apply_define_class_variable(op),
|
|
146
|
+
Operation::AliasGlobalVariable(op) => self.apply_alias_global_variable(op),
|
|
147
|
+
Operation::ReferenceConstant(op) => self.apply_reference_constant(op),
|
|
148
|
+
Operation::ReferenceMethod(op) => self.apply_reference_method(op),
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
fn apply_enter_class(&mut self, op: EnterClass) {
|
|
153
|
+
let lexical_nesting_id = self.current_lexical_scope_id();
|
|
154
|
+
let superclass_ref = op.superclass_name.and_then(|n| self.constant_ref_ids.get(&n).copied());
|
|
155
|
+
let def = ClassDefinition::new(
|
|
156
|
+
op.name_id,
|
|
157
|
+
op.uri_id,
|
|
158
|
+
op.offset,
|
|
159
|
+
op.name_offset,
|
|
160
|
+
op.comments,
|
|
161
|
+
op.flags,
|
|
162
|
+
lexical_nesting_id,
|
|
163
|
+
superclass_ref,
|
|
164
|
+
);
|
|
165
|
+
let def_id = self.local_graph.add_definition(Definition::Class(Box::new(def)));
|
|
166
|
+
self.add_member(lexical_nesting_id, def_id);
|
|
167
|
+
self.scope_stack.push(ApplierScope::Namespace {
|
|
168
|
+
definition_id: def_id,
|
|
169
|
+
is_lexical_scope: op.is_lexical_scope,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
fn apply_enter_module(&mut self, op: EnterModule) {
|
|
174
|
+
let lexical_nesting_id = self.current_lexical_scope_id();
|
|
175
|
+
let def = ModuleDefinition::new(
|
|
176
|
+
op.name_id,
|
|
177
|
+
op.uri_id,
|
|
178
|
+
op.offset,
|
|
179
|
+
op.name_offset,
|
|
180
|
+
op.comments,
|
|
181
|
+
op.flags,
|
|
182
|
+
lexical_nesting_id,
|
|
183
|
+
);
|
|
184
|
+
let def_id = self.local_graph.add_definition(Definition::Module(Box::new(def)));
|
|
185
|
+
self.add_member(lexical_nesting_id, def_id);
|
|
186
|
+
self.scope_stack.push(ApplierScope::Namespace {
|
|
187
|
+
definition_id: def_id,
|
|
188
|
+
is_lexical_scope: op.is_lexical_scope,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
fn apply_enter_singleton_class(&mut self, op: EnterSingletonClass) {
|
|
193
|
+
let lexical_nesting_id = self.current_lexical_scope_id();
|
|
194
|
+
let def = SingletonClassDefinition::new(
|
|
195
|
+
op.name_id,
|
|
196
|
+
op.uri_id,
|
|
197
|
+
op.offset,
|
|
198
|
+
op.name_offset,
|
|
199
|
+
op.comments,
|
|
200
|
+
op.flags,
|
|
201
|
+
lexical_nesting_id,
|
|
202
|
+
);
|
|
203
|
+
let def_id = self
|
|
204
|
+
.local_graph
|
|
205
|
+
.add_definition(Definition::SingletonClass(Box::new(def)));
|
|
206
|
+
self.add_member(lexical_nesting_id, def_id);
|
|
207
|
+
self.scope_stack.push(ApplierScope::Namespace {
|
|
208
|
+
definition_id: def_id,
|
|
209
|
+
is_lexical_scope: true,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
fn apply_enter_method(&mut self, op: EnterMethod) {
|
|
214
|
+
let lexical_nesting_id = self.current_owner_id();
|
|
215
|
+
let has_receiver = op.receiver.is_some();
|
|
216
|
+
let receiver = self.resolve_receiver(op.receiver.as_ref());
|
|
217
|
+
let visibility = self.resolve_visibility(has_receiver);
|
|
218
|
+
let def = MethodDefinition::new(
|
|
219
|
+
op.str_id,
|
|
220
|
+
op.uri_id,
|
|
221
|
+
op.offset,
|
|
222
|
+
op.comments,
|
|
223
|
+
op.flags,
|
|
224
|
+
lexical_nesting_id,
|
|
225
|
+
op.signatures,
|
|
226
|
+
visibility,
|
|
227
|
+
receiver,
|
|
228
|
+
);
|
|
229
|
+
let def_id = self.local_graph.add_definition(Definition::Method(Box::new(def)));
|
|
230
|
+
self.add_member(lexical_nesting_id, def_id);
|
|
231
|
+
self.scope_stack.push(ApplierScope::Method { definition_id: def_id });
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
fn apply_alias_method(&mut self, op: AliasMethod) {
|
|
235
|
+
let lexical_nesting_id = self.current_owner_id();
|
|
236
|
+
let receiver = self.resolve_receiver(op.receiver.as_ref());
|
|
237
|
+
let def = MethodAliasDefinition::new(
|
|
238
|
+
op.new_name_str_id,
|
|
239
|
+
op.old_name_str_id,
|
|
240
|
+
op.uri_id,
|
|
241
|
+
op.offset,
|
|
242
|
+
op.comments,
|
|
243
|
+
op.flags,
|
|
244
|
+
lexical_nesting_id,
|
|
245
|
+
receiver,
|
|
246
|
+
);
|
|
247
|
+
let def_id = self.local_graph.add_definition(Definition::MethodAlias(Box::new(def)));
|
|
248
|
+
self.add_member(lexical_nesting_id, def_id);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
fn apply_set_method_visibility(&mut self, op: SetMethodVisibility) {
|
|
252
|
+
let lexical_nesting_id = self.current_owner_id();
|
|
253
|
+
let def = MethodVisibilityDefinition::new(
|
|
254
|
+
op.str_id,
|
|
255
|
+
op.visibility,
|
|
256
|
+
op.uri_id,
|
|
257
|
+
op.offset,
|
|
258
|
+
Box::default(),
|
|
259
|
+
op.flags,
|
|
260
|
+
lexical_nesting_id,
|
|
261
|
+
);
|
|
262
|
+
let def_id = self
|
|
263
|
+
.local_graph
|
|
264
|
+
.add_definition(Definition::MethodVisibility(Box::new(def)));
|
|
265
|
+
self.add_member(lexical_nesting_id, def_id);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
fn apply_define_constant(&mut self, op: DefineConstant) {
|
|
269
|
+
let lexical_nesting_id = self.current_lexical_scope_id();
|
|
270
|
+
let def = ConstantDefinition::new(
|
|
271
|
+
op.name_id,
|
|
272
|
+
op.uri_id,
|
|
273
|
+
op.offset,
|
|
274
|
+
op.comments,
|
|
275
|
+
op.flags,
|
|
276
|
+
lexical_nesting_id,
|
|
277
|
+
);
|
|
278
|
+
let def_id = self.local_graph.add_definition(Definition::Constant(Box::new(def)));
|
|
279
|
+
self.add_member(lexical_nesting_id, def_id);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
fn apply_alias_constant(&mut self, op: AliasConstant) {
|
|
283
|
+
let lexical_nesting_id = self.current_lexical_scope_id();
|
|
284
|
+
let constant = ConstantDefinition::new(
|
|
285
|
+
op.name_id,
|
|
286
|
+
op.uri_id,
|
|
287
|
+
op.offset,
|
|
288
|
+
op.comments,
|
|
289
|
+
op.flags,
|
|
290
|
+
lexical_nesting_id,
|
|
291
|
+
);
|
|
292
|
+
let def = ConstantAliasDefinition::new(op.target_name_id, constant);
|
|
293
|
+
let def_id = self
|
|
294
|
+
.local_graph
|
|
295
|
+
.add_definition(Definition::ConstantAlias(Box::new(def)));
|
|
296
|
+
self.add_member(lexical_nesting_id, def_id);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
fn apply_set_constant_visibility(&mut self, op: SetConstantVisibility) {
|
|
300
|
+
let lexical_nesting_id = self.current_owner_id();
|
|
301
|
+
let receiver = match op.receiver {
|
|
302
|
+
Some(Target::Constant(name_id)) => Some(name_id),
|
|
303
|
+
Some(Target::ExplicitSelf | Target::Other) | None => None,
|
|
304
|
+
};
|
|
305
|
+
let def = ConstantVisibilityDefinition::new(
|
|
306
|
+
receiver,
|
|
307
|
+
op.target,
|
|
308
|
+
op.visibility,
|
|
309
|
+
op.uri_id,
|
|
310
|
+
op.offset,
|
|
311
|
+
op.comments,
|
|
312
|
+
op.flags,
|
|
313
|
+
lexical_nesting_id,
|
|
314
|
+
);
|
|
315
|
+
let def_id = self
|
|
316
|
+
.local_graph
|
|
317
|
+
.add_definition(Definition::ConstantVisibility(Box::new(def)));
|
|
318
|
+
self.add_member(lexical_nesting_id, def_id);
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
fn apply_mixin(&mut self, op: &crate::operation::Mixin) {
|
|
322
|
+
let Some(owner_id) = self.current_owner_id() else {
|
|
323
|
+
return;
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
let constant_reference_id = match op.target {
|
|
327
|
+
Target::Constant(name_id) => self.constant_ref_ids.get(&name_id).copied(),
|
|
328
|
+
Target::ExplicitSelf | Target::Other => None,
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
let Some(constant_reference_id) = constant_reference_id else {
|
|
332
|
+
return;
|
|
333
|
+
};
|
|
334
|
+
|
|
335
|
+
let mixin = match op.kind {
|
|
336
|
+
MixinKind::Include => Mixin::Include(IncludeDefinition::new(constant_reference_id)),
|
|
337
|
+
MixinKind::Prepend => Mixin::Prepend(PrependDefinition::new(constant_reference_id)),
|
|
338
|
+
MixinKind::Extend => Mixin::Extend(ExtendDefinition::new(constant_reference_id)),
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
if let Some(owner) = self.local_graph.get_definition_mut(owner_id) {
|
|
342
|
+
match owner {
|
|
343
|
+
Definition::Class(class) => class.add_mixin(mixin),
|
|
344
|
+
Definition::Module(module) => module.add_mixin(mixin),
|
|
345
|
+
Definition::SingletonClass(singleton) => singleton.add_mixin(mixin),
|
|
346
|
+
_ => {}
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
fn apply_define_attribute(&mut self, op: DefineAttribute) {
|
|
352
|
+
let lexical_nesting_id = self.current_scope_id();
|
|
353
|
+
let visibility = self.resolve_visibility(false);
|
|
354
|
+
let def_id = match op.kind {
|
|
355
|
+
AttrKind::Accessor => {
|
|
356
|
+
let def = AttrAccessorDefinition::new(
|
|
357
|
+
op.str_id,
|
|
358
|
+
op.uri_id,
|
|
359
|
+
op.offset,
|
|
360
|
+
op.comments,
|
|
361
|
+
op.flags,
|
|
362
|
+
lexical_nesting_id,
|
|
363
|
+
visibility,
|
|
364
|
+
);
|
|
365
|
+
self.local_graph.add_definition(Definition::AttrAccessor(Box::new(def)))
|
|
366
|
+
}
|
|
367
|
+
AttrKind::Reader => {
|
|
368
|
+
let def = AttrReaderDefinition::new(
|
|
369
|
+
op.str_id,
|
|
370
|
+
op.uri_id,
|
|
371
|
+
op.offset,
|
|
372
|
+
op.comments,
|
|
373
|
+
op.flags,
|
|
374
|
+
lexical_nesting_id,
|
|
375
|
+
visibility,
|
|
376
|
+
);
|
|
377
|
+
self.local_graph.add_definition(Definition::AttrReader(Box::new(def)))
|
|
378
|
+
}
|
|
379
|
+
AttrKind::Writer => {
|
|
380
|
+
let def = AttrWriterDefinition::new(
|
|
381
|
+
op.str_id,
|
|
382
|
+
op.uri_id,
|
|
383
|
+
op.offset,
|
|
384
|
+
op.comments,
|
|
385
|
+
op.flags,
|
|
386
|
+
lexical_nesting_id,
|
|
387
|
+
visibility,
|
|
388
|
+
);
|
|
389
|
+
self.local_graph.add_definition(Definition::AttrWriter(Box::new(def)))
|
|
390
|
+
}
|
|
391
|
+
};
|
|
392
|
+
self.add_member(lexical_nesting_id, def_id);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
fn apply_define_global_variable(&mut self, op: DefineGlobalVariable) {
|
|
396
|
+
let lexical_nesting_id = self.current_scope_id();
|
|
397
|
+
let member_owner_id = self.current_owner_id();
|
|
398
|
+
let def = GlobalVariableDefinition::new(
|
|
399
|
+
op.str_id,
|
|
400
|
+
op.uri_id,
|
|
401
|
+
op.offset,
|
|
402
|
+
op.comments,
|
|
403
|
+
op.flags,
|
|
404
|
+
lexical_nesting_id,
|
|
405
|
+
);
|
|
406
|
+
let def_id = self
|
|
407
|
+
.local_graph
|
|
408
|
+
.add_definition(Definition::GlobalVariable(Box::new(def)));
|
|
409
|
+
self.add_member(member_owner_id, def_id);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
fn apply_define_instance_variable(&mut self, op: DefineInstanceVariable) {
|
|
413
|
+
let lexical_nesting_id = self.current_scope_id();
|
|
414
|
+
let member_owner_id = self.current_owner_id();
|
|
415
|
+
let def = InstanceVariableDefinition::new(
|
|
416
|
+
op.str_id,
|
|
417
|
+
op.uri_id,
|
|
418
|
+
op.offset,
|
|
419
|
+
op.comments,
|
|
420
|
+
op.flags,
|
|
421
|
+
lexical_nesting_id,
|
|
422
|
+
);
|
|
423
|
+
let def_id = self
|
|
424
|
+
.local_graph
|
|
425
|
+
.add_definition(Definition::InstanceVariable(Box::new(def)));
|
|
426
|
+
self.add_member(member_owner_id, def_id);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
fn apply_define_class_variable(&mut self, op: DefineClassVariable) {
|
|
430
|
+
let lexical_nesting_id = self.current_lexical_scope_id();
|
|
431
|
+
let member_owner_id = self.current_owner_id();
|
|
432
|
+
let def = ClassVariableDefinition::new(
|
|
433
|
+
op.str_id,
|
|
434
|
+
op.uri_id,
|
|
435
|
+
op.offset,
|
|
436
|
+
op.comments,
|
|
437
|
+
op.flags,
|
|
438
|
+
lexical_nesting_id,
|
|
439
|
+
);
|
|
440
|
+
let def_id = self
|
|
441
|
+
.local_graph
|
|
442
|
+
.add_definition(Definition::ClassVariable(Box::new(def)));
|
|
443
|
+
self.add_member(member_owner_id, def_id);
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
fn apply_alias_global_variable(&mut self, op: AliasGlobalVariable) {
|
|
447
|
+
let lexical_nesting_id = self.current_scope_id();
|
|
448
|
+
let def = GlobalVariableAliasDefinition::new(
|
|
449
|
+
op.new_name_str_id,
|
|
450
|
+
op.old_name_str_id,
|
|
451
|
+
op.uri_id,
|
|
452
|
+
op.offset,
|
|
453
|
+
op.comments,
|
|
454
|
+
op.flags,
|
|
455
|
+
lexical_nesting_id,
|
|
456
|
+
);
|
|
457
|
+
self.local_graph
|
|
458
|
+
.add_definition(Definition::GlobalVariableAlias(Box::new(def)));
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
fn apply_reference_constant(&mut self, op: ReferenceConstant) {
|
|
462
|
+
let ref_id = self
|
|
463
|
+
.local_graph
|
|
464
|
+
.add_constant_reference(ConstantReference::new(op.name_id, op.uri_id, op.offset));
|
|
465
|
+
self.constant_ref_ids.insert(op.name_id, ref_id);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
fn apply_reference_method(&mut self, op: ReferenceMethod) {
|
|
469
|
+
let receiver = match op.receiver {
|
|
470
|
+
Some(Target::Constant(name_id)) => Some(name_id),
|
|
471
|
+
Some(Target::ExplicitSelf | Target::Other) | None => None,
|
|
472
|
+
};
|
|
473
|
+
self.local_graph
|
|
474
|
+
.add_method_reference(MethodRef::new(op.str_id, op.uri_id, op.offset, receiver));
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/// Converts an `OperationBuilderResult` into a `LocalGraph`.
|
|
479
|
+
///
|
|
480
|
+
/// Walks the operations in order, creating `Definition` objects and registering members/mixins.
|
|
481
|
+
/// Scope context is derived from the scope stack maintained by Enter/Exit operations.
|
|
482
|
+
#[must_use]
|
|
483
|
+
pub fn apply_operations(result: OperationBuilderResult) -> LocalGraph {
|
|
484
|
+
let OperationBuilderResult {
|
|
485
|
+
uri_id,
|
|
486
|
+
document,
|
|
487
|
+
operations,
|
|
488
|
+
strings,
|
|
489
|
+
names,
|
|
490
|
+
} = result;
|
|
491
|
+
|
|
492
|
+
let mut applier = OperationApplier {
|
|
493
|
+
local_graph: LocalGraph::from_parts(uri_id, document, strings, names),
|
|
494
|
+
scope_stack: Vec::new(),
|
|
495
|
+
scope_visibility: HashMap::new(),
|
|
496
|
+
constant_ref_ids: HashMap::new(),
|
|
497
|
+
};
|
|
498
|
+
|
|
499
|
+
for op in operations {
|
|
500
|
+
applier.apply_operation(op);
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
applier.local_graph
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
#[cfg(test)]
|
|
507
|
+
fn backend() -> crate::indexing::IndexerBackend {
|
|
508
|
+
crate::indexing::IndexerBackend::OperationBuilder
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
#[cfg(test)]
|
|
512
|
+
#[allow(clippy::duplicate_mod)]
|
|
513
|
+
#[path = "../indexing/ruby_indexer_tests.rs"]
|
|
514
|
+
mod applier_tests;
|
|
515
|
+
|
|
516
|
+
#[cfg(test)]
|
|
517
|
+
#[allow(clippy::duplicate_mod)]
|
|
518
|
+
#[path = "../resolution_tests.rs"]
|
|
519
|
+
mod resolution_tests;
|