Skip to content

Commit 3351ec6

Browse files
committed
Handle instance variable references
1 parent 9a1f19b commit 3351ec6

9 files changed

Lines changed: 547 additions & 164 deletions

File tree

rust/rubydex/src/indexing/local_graph.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@ use crate::model::definitions::Definition;
55
use crate::model::document::Document;
66
use crate::model::graph::NameDependent;
77
use crate::model::identity_maps::IdentityHashMap;
8-
use crate::model::ids::{ConstantReferenceId, DefinitionId, MethodReferenceId, NameId, StringId, UriId};
8+
use crate::model::ids::{
9+
ConstantReferenceId, DefinitionId, InstanceVariableReferenceId, MethodReferenceId, NameId, StringId, UriId,
10+
};
911
use crate::model::name::{Name, NameRef};
10-
use crate::model::references::{ConstantReference, MethodRef};
12+
use crate::model::references::{ConstantReference, InstanceVariableRef, MethodRef};
1113
use crate::model::string_ref::StringRef;
1214
use crate::offset::Offset;
1315

@@ -19,6 +21,7 @@ type LocalGraphParts = (
1921
IdentityHashMap<NameId, NameRef>,
2022
IdentityHashMap<ConstantReferenceId, ConstantReference>,
2123
IdentityHashMap<MethodReferenceId, MethodRef>,
24+
IdentityHashMap<InstanceVariableReferenceId, InstanceVariableRef>,
2225
IdentityHashMap<NameId, Vec<NameDependent>>,
2326
);
2427

@@ -31,6 +34,7 @@ pub struct LocalGraph {
3134
names: IdentityHashMap<NameId, NameRef>,
3235
constant_references: IdentityHashMap<ConstantReferenceId, ConstantReference>,
3336
method_references: IdentityHashMap<MethodReferenceId, MethodRef>,
37+
instance_variable_references: IdentityHashMap<InstanceVariableReferenceId, InstanceVariableRef>,
3438
name_dependents: IdentityHashMap<NameId, Vec<NameDependent>>,
3539
}
3640

@@ -45,6 +49,7 @@ impl LocalGraph {
4549
names: IdentityHashMap::default(),
4650
constant_references: IdentityHashMap::default(),
4751
method_references: IdentityHashMap::default(),
52+
instance_variable_references: IdentityHashMap::default(),
4853
name_dependents: IdentityHashMap::default(),
4954
}
5055
}
@@ -187,6 +192,28 @@ impl LocalGraph {
187192
reference_id
188193
}
189194

195+
// Instance variable references
196+
197+
#[must_use]
198+
pub fn instance_variable_references(&self) -> &IdentityHashMap<InstanceVariableReferenceId, InstanceVariableRef> {
199+
&self.instance_variable_references
200+
}
201+
202+
pub fn add_instance_variable_reference(&mut self, reference: InstanceVariableRef) -> InstanceVariableReferenceId {
203+
let reference_id = reference.id();
204+
205+
if self
206+
.instance_variable_references
207+
.insert(reference_id, reference)
208+
.is_some()
209+
{
210+
debug_assert!(false, "InstanceVariableReferenceId collision in local graph");
211+
}
212+
213+
self.document.add_instance_variable_reference(reference_id);
214+
reference_id
215+
}
216+
190217
// Diagnostics
191218

192219
#[must_use]
@@ -218,6 +245,7 @@ impl LocalGraph {
218245
self.names,
219246
self.constant_references,
220247
self.method_references,
248+
self.instance_variable_references,
221249
self.name_dependents,
222250
)
223251
}

rust/rubydex/src/indexing/ruby_indexer.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::model::definitions::{
1313
use crate::model::document::Document;
1414
use crate::model::ids::{DefinitionId, NameId, StringId, UriId};
1515
use crate::model::name::{Name, ParentScope};
16-
use crate::model::references::{ConstantReference, MethodRef};
16+
use crate::model::references::{ConstantReference, InstanceVariableRef, MethodRef};
1717
use crate::model::visibility::Visibility;
1818
use crate::offset::Offset;
1919

@@ -555,6 +555,15 @@ impl<'a> RubyIndexer<'a> {
555555
definition_id
556556
}
557557

558+
fn add_instance_variable_reference(&mut self, location: &ruby_prism::Location) {
559+
let name = Self::location_to_string(location);
560+
let str_id = self.local_graph.intern_string(name);
561+
let offset = Offset::from_prism_location(location);
562+
let parent_nesting_id = self.parent_nesting_id();
563+
let reference = InstanceVariableRef::new(str_id, self.uri_id, offset, parent_nesting_id);
564+
self.local_graph.add_instance_variable_reference(reference);
565+
}
566+
558567
/// Adds a class variable definition.
559568
///
560569
/// Class variables use lexical scoping - they belong to the lexically enclosing class/module,
@@ -2193,7 +2202,7 @@ impl Visit<'_> for RubyIndexer<'_> {
21932202
}
21942203

21952204
fn visit_instance_variable_operator_write_node(&mut self, node: &ruby_prism::InstanceVariableOperatorWriteNode) {
2196-
self.add_instance_variable_definition(&node.name_loc());
2205+
self.add_instance_variable_reference(&node.name_loc());
21972206
self.visit(&node.value());
21982207
}
21992208

@@ -2207,6 +2216,10 @@ impl Visit<'_> for RubyIndexer<'_> {
22072216
self.visit(&node.value());
22082217
}
22092218

2219+
fn visit_instance_variable_read_node(&mut self, node: &ruby_prism::InstanceVariableReadNode) {
2220+
self.add_instance_variable_reference(&node.location());
2221+
}
2222+
22102223
fn visit_class_variable_and_write_node(&mut self, node: &ruby_prism::ClassVariableAndWriteNode) {
22112224
self.add_class_variable_definition(&node.name_loc());
22122225
self.visit(&node.value());

rust/rubydex/src/indexing/ruby_indexer_tests.rs

Lines changed: 67 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,34 @@ macro_rules! assert_not_promotable {
103103
}};
104104
}
105105

106+
macro_rules! assert_instance_variable_references_eq {
107+
($context:expr, $expected_names:expr) => {{
108+
let mut actual_references = $context
109+
.graph()
110+
.instance_variable_references()
111+
.values()
112+
.map(|r| {
113+
(
114+
r.offset().start(),
115+
$context.graph().strings().get(r.str_id()).unwrap().as_str(),
116+
)
117+
})
118+
.collect::<Vec<_>>();
119+
120+
actual_references.sort();
121+
122+
let actual_names = actual_references.iter().map(|(_, name)| *name).collect::<Vec<_>>();
123+
124+
assert_eq!(
125+
$expected_names,
126+
actual_names.as_slice(),
127+
"instance variable references mismatch: expected `{:?}`, got `{:?}`",
128+
$expected_names,
129+
actual_names
130+
);
131+
}};
132+
}
133+
106134
fn index_source(source: &str) -> LocalGraphTest {
107135
LocalGraphTest::new("file:///foo.rb", source)
108136
}
@@ -1801,6 +1829,38 @@ fn constant_with_colon_colon_call_is_promotable() {
18011829
});
18021830
}
18031831

1832+
#[test]
1833+
fn instance_variable_reads_are_indexed_as_references() {
1834+
let context = index_source({
1835+
"
1836+
class Foo
1837+
def bar
1838+
@baz
1839+
end
1840+
end
1841+
"
1842+
});
1843+
1844+
assert_no_local_diagnostics!(&context);
1845+
assert_instance_variable_references_eq!(context, ["@baz"]);
1846+
}
1847+
1848+
#[test]
1849+
fn instance_variable_operator_writes_are_references() {
1850+
let context = index_source({
1851+
"
1852+
class Foo
1853+
def bar
1854+
@baz += 1
1855+
end
1856+
end
1857+
"
1858+
});
1859+
1860+
assert_no_local_diagnostics!(&context);
1861+
assert_instance_variable_references_eq!(context, ["@baz"]);
1862+
}
1863+
18041864
mod constant_tests {
18051865
use super::*;
18061866

@@ -2075,14 +2135,12 @@ mod variable_tests {
20752135
@baz, @qux = 3, 4
20762136
end
20772137
2078-
@bar &= 5
2079-
@baz &&= 6
2080-
@qux ||= 7
2138+
@bar &&= 5
2139+
@baz ||= 6
20812140
20822141
class Bar
2083-
@foo &= 8
2084-
@bar &&= 9
2085-
@baz ||= 10
2142+
@foo &&= 8
2143+
@bar ||= 9
20862144
end
20872145
"
20882146
});
@@ -2124,29 +2182,18 @@ mod variable_tests {
21242182
assert!(def.lexical_nesting_id().is_none());
21252183
});
21262184

2127-
assert_definition_at!(&context, "10:1-10:5", InstanceVariable, |def| {
2128-
assert_def_str_eq!(&context, def, "@qux");
2129-
assert!(def.lexical_nesting_id().is_none());
2130-
});
2131-
2132-
assert_definition_at!(&context, "12:1-16:4", Class, |bar_class_def| {
2133-
assert_definition_at!(&context, "13:3-13:7", InstanceVariable, |def| {
2185+
assert_definition_at!(&context, "11:1-14:4", Class, |bar_class_def| {
2186+
assert_definition_at!(&context, "12:3-12:7", InstanceVariable, |def| {
21342187
assert_def_str_eq!(&context, def, "@foo");
21352188
assert_eq!(bar_class_def.id(), def.lexical_nesting_id().unwrap());
21362189
assert_eq!(bar_class_def.members()[0], def.id());
21372190
});
21382191

2139-
assert_definition_at!(&context, "14:3-14:7", InstanceVariable, |def| {
2192+
assert_definition_at!(&context, "13:3-13:7", InstanceVariable, |def| {
21402193
assert_def_str_eq!(&context, def, "@bar");
21412194
assert_eq!(bar_class_def.id(), def.lexical_nesting_id().unwrap());
21422195
assert_eq!(bar_class_def.members()[1], def.id());
21432196
});
2144-
2145-
assert_definition_at!(&context, "15:3-15:7", InstanceVariable, |def| {
2146-
assert_def_str_eq!(&context, def, "@baz");
2147-
assert_eq!(bar_class_def.id(), def.lexical_nesting_id().unwrap());
2148-
assert_eq!(bar_class_def.members()[2], def.id());
2149-
});
21502197
});
21512198
}
21522199

rust/rubydex/src/model/declaration.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -448,6 +448,43 @@ impl Declaration {
448448
_ => unreachable!("Cannot remove constant reference from {} declaration", self.kind()),
449449
}
450450
}
451+
452+
/// Returns the instance variable reference IDs for `InstanceVariable` declarations.
453+
/// Returns `None` for other declaration types.
454+
#[must_use]
455+
pub fn instance_variable_references(&self) -> Option<&IdentityHashSet<InstanceVariableReferenceId>> {
456+
match self {
457+
Declaration::InstanceVariable(it) => Some(it.references()),
458+
_ => None,
459+
}
460+
}
461+
462+
/// Adds an instance variable reference to this declaration.
463+
///
464+
/// # Panics
465+
///
466+
/// Panics if called on a declaration that doesn't track instance variable references.
467+
pub fn add_instance_variable_reference(&mut self, reference_id: InstanceVariableReferenceId) {
468+
match self {
469+
Declaration::InstanceVariable(it) => it.add_reference(reference_id),
470+
_ => unreachable!("Cannot add instance variable reference to {} declaration", self.kind()),
471+
}
472+
}
473+
474+
/// Removes an instance variable reference from this declaration.
475+
///
476+
/// # Panics
477+
///
478+
/// Panics if called on a declaration that doesn't track instance variable references.
479+
pub fn remove_instance_variable_reference(&mut self, reference_id: &InstanceVariableReferenceId) {
480+
match self {
481+
Declaration::InstanceVariable(it) => it.remove_reference(reference_id),
482+
_ => unreachable!(
483+
"Cannot remove instance variable reference from {} declaration",
484+
self.kind()
485+
),
486+
}
487+
}
451488
}
452489

453490
#[derive(Debug)]

rust/rubydex/src/model/document.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use line_index::LineIndex;
44
use url::Url;
55

66
use crate::diagnostic::Diagnostic;
7-
use crate::model::ids::{ConstantReferenceId, DefinitionId, MethodReferenceId};
7+
use crate::model::ids::{ConstantReferenceId, DefinitionId, InstanceVariableReferenceId, MethodReferenceId};
88

99
// Represents a document currently loaded into memory. Identified by its unique URI, it holds the edges to all
1010
// definitions and references discovered in it
@@ -15,6 +15,7 @@ pub struct Document {
1515
definition_ids: Vec<DefinitionId>,
1616
method_reference_ids: Vec<MethodReferenceId>,
1717
constant_reference_ids: Vec<ConstantReferenceId>,
18+
instance_variable_reference_ids: Vec<InstanceVariableReferenceId>,
1819
diagnostics: Vec<Diagnostic>,
1920
}
2021

@@ -27,6 +28,7 @@ impl Document {
2728
definition_ids: Vec::new(),
2829
method_reference_ids: Vec::new(),
2930
constant_reference_ids: Vec::new(),
31+
instance_variable_reference_ids: Vec::new(),
3032
diagnostics: Vec::new(),
3133
}
3234
}
@@ -73,6 +75,15 @@ impl Document {
7375
self.constant_reference_ids.push(reference_id);
7476
}
7577

78+
#[must_use]
79+
pub fn instance_variable_references(&self) -> &[InstanceVariableReferenceId] {
80+
&self.instance_variable_reference_ids
81+
}
82+
83+
pub fn add_instance_variable_reference(&mut self, reference_id: InstanceVariableReferenceId) {
84+
self.instance_variable_reference_ids.push(reference_id);
85+
}
86+
7687
#[must_use]
7788
pub fn diagnostics(&self) -> &[Diagnostic] {
7889
&self.diagnostics

0 commit comments

Comments
 (0)