Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 78 additions & 54 deletions rust/saturn/src/indexing/ruby_indexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,26 @@ enum MixinType {
Extend,
}

enum Nesting {
/// Nesting stack entries that produce a new lexical scope to which constant references must be attached to (i.e.:
/// the class and module keywords). All lexical scopes are also owner, but the opposite is not true
LexicalScope(DefinitionId),
/// An owner entry that will be associated with all members encountered, but will not produce a new lexical scope
/// (e.g.: Module.new or Class.new)
#[allow(dead_code)]
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is dead just until the work on the other PR is complete.

Owner(DefinitionId),
/// A method entry that is used to set the correct owner for instance variables, but cannot own anything itself
Method(DefinitionId),
}

impl Nesting {
fn id(&self) -> DefinitionId {
match self {
Nesting::LexicalScope(id) | Nesting::Owner(id) | Nesting::Method(id) => *id,
}
}
}

/// The indexer for the definitions found in the Ruby source code.
///
/// It implements the `Visit` trait from `ruby_prism` to visit the AST and create a hash of definitions that must be
Expand All @@ -34,7 +54,7 @@ pub struct RubyIndexer<'a> {
local_graph: LocalGraph,
source: &'a str,
comments: Vec<CommentGroup>,
definitions_stack: Vec<DefinitionId>,
nesting_stack: Vec<Nesting>,
visibility_stack: Vec<Visibility>,
}

Expand All @@ -49,7 +69,7 @@ impl<'a> RubyIndexer<'a> {
local_graph,
source,
comments: Vec::new(),
definitions_stack: Vec::new(),
nesting_stack: Vec::new(),
visibility_stack: vec![Visibility::Private],
}
}
Expand Down Expand Up @@ -271,18 +291,22 @@ impl<'a> RubyIndexer<'a> {
///
/// Panics if the definition is not a class, module, or singleton class
fn current_owner_name_id(&self) -> Option<NameId> {
for &id in self.definitions_stack.iter().rev() {
if let Some(definition) = self.local_graph.definitions().get(&id) {
match definition {
Definition::Class(class_def) => return Some(*class_def.name_id()),
Definition::Module(module_def) => return Some(*module_def.name_id()),
Definition::SingletonClass(singleton_class_def) => return Some(*singleton_class_def.name_id()),
Definition::Method(_) => {}
_ => panic!("current nesting is not a class/module/singleton class: {definition:?}"),
self.nesting_stack.iter().rev().find_map(|nesting| match nesting {
Nesting::LexicalScope(id) | Nesting::Owner(id) => {
if let Some(definition) = self.local_graph.definitions().get(id) {
match definition {
Definition::Class(class_def) => Some(*class_def.name_id()),
Definition::Module(module_def) => Some(*module_def.name_id()),
Definition::SingletonClass(singleton_class_def) => Some(*singleton_class_def.name_id()),
Definition::Method(_) => None,
_ => panic!("current nesting is not a class/module/singleton class: {definition:?}"),
}
} else {
None
}
}
}
None
Nesting::Method(_) => None,
})
}

// Runs the given closure if the given call `node` is invoked directly on `self` for each one of its string or
Expand Down Expand Up @@ -418,10 +442,10 @@ impl<'a> RubyIndexer<'a> {
let str_id = self.local_graph.intern_string(name);
let offset = Offset::from_prism_location(location);
let comments = self.find_comments_for(offset.start()).unwrap_or_default();
let lexical_nesting_id = self.parent_nesting_id().copied();
let parent_nesting_id = self.parent_nesting_id();
let uri_id = self.uri_id;

let definition = builder(str_id, offset, comments, lexical_nesting_id, uri_id);
let definition = builder(str_id, offset, comments, parent_nesting_id, uri_id);
let definition_id = self.local_graph.add_definition(definition);

self.add_member_to_current_owner(definition_id);
Expand All @@ -434,15 +458,15 @@ impl<'a> RubyIndexer<'a> {
let str_id = self.local_graph.intern_string(name);
let offset = Offset::from_prism_location(location);
let comments = self.find_comments_for(offset.start()).unwrap_or_default();
let lexical_nesting_id = self.parent_nesting_id().copied();
let parent_nesting_id = self.parent_nesting_id();
let uri_id = self.uri_id;

let definition = Definition::InstanceVariable(Box::new(InstanceVariableDefinition::new(
str_id,
uri_id,
offset,
comments,
lexical_nesting_id,
parent_nesting_id,
)));

let definition_id = self.local_graph.add_definition(definition);
Expand Down Expand Up @@ -488,7 +512,7 @@ impl<'a> RubyIndexer<'a> {

let offset = Offset::from_prism_location(&location);
let comments = self.find_comments_for(offset.start()).unwrap_or_default();
let lexical_nesting_id = self.parent_nesting_id().copied();
let lexical_nesting_id = self.parent_lexical_scope_id();

let definition = Definition::Constant(Box::new(ConstantDefinition::new(
name_id,
Expand All @@ -507,18 +531,10 @@ impl<'a> RubyIndexer<'a> {
/// Returns the definition ID of the current nesting (class, module, or singleton class),
/// but skips methods in the definitions stack.
fn current_nesting_definition_id(&self) -> Option<DefinitionId> {
self.definitions_stack
.iter()
.rev()
.find(|&&id| {
self.local_graph.definitions().get(&id).is_some_and(|def| {
matches!(
def,
Definition::Class(_) | Definition::SingletonClass(_) | Definition::Module(_)
)
})
})
.copied()
self.nesting_stack.iter().rev().find_map(|nesting| match nesting {
Nesting::LexicalScope(id) | Nesting::Owner(id) => Some(*id),
Nesting::Method(_) => None,
})
}

/// Adds a member to the current owner (class, module, or singleton class).
Expand Down Expand Up @@ -548,15 +564,15 @@ impl<'a> RubyIndexer<'a> {
return;
};

let lexical_nesting_id = self.parent_nesting_id().copied();
let parent_nesting_id = self.current_nesting_definition_id();

// Collect all arguments as constant references. Ignore anything that isn't a constant
let reference_ids: Vec<_> = arguments
.arguments()
.iter()
.filter_map(|arg| {
if arg.as_self_node().is_some() {
if lexical_nesting_id.is_none() {
if parent_nesting_id.is_none() {
self.local_graph.add_diagnostic(
Diagnostics::TopLevelMixinSelf,
Offset::from_prism_location(&arg.location()),
Expand Down Expand Up @@ -587,7 +603,7 @@ impl<'a> RubyIndexer<'a> {
return;
}

let Some(lexical_nesting_id) = lexical_nesting_id else {
let Some(lexical_nesting_id) = parent_nesting_id else {
return;
};

Expand Down Expand Up @@ -626,8 +642,16 @@ impl<'a> RubyIndexer<'a> {
}

#[must_use]
fn parent_nesting_id(&self) -> Option<&DefinitionId> {
self.definitions_stack.last()
fn parent_lexical_scope_id(&self) -> Option<DefinitionId> {
self.nesting_stack.iter().rev().find_map(|nesting| match nesting {
Nesting::LexicalScope(id) => Some(*id),
Nesting::Owner(_) | Nesting::Method(_) => None,
})
}

#[must_use]
fn parent_nesting_id(&self) -> Option<DefinitionId> {
self.nesting_stack.last().map(Nesting::id)
}

#[must_use]
Expand Down Expand Up @@ -682,7 +706,7 @@ impl Visit<'_> for RubyIndexer<'_> {
fn visit_class_node(&mut self, node: &ruby_prism::ClassNode<'_>) {
let offset = Offset::from_prism_location(&node.location());
let comments = self.find_comments_for(offset.start()).unwrap_or_default();
let lexical_nesting_id = self.parent_nesting_id().copied();
let lexical_nesting_id = self.parent_lexical_scope_id();
let superclass = node.superclass().and_then(|n| self.index_constant_reference(&n, true));

if let Some(superclass_node) = node.superclass()
Expand Down Expand Up @@ -710,19 +734,19 @@ impl Visit<'_> for RubyIndexer<'_> {
self.add_member_to_current_owner(definition_id);

if let Some(body) = node.body() {
self.definitions_stack.push(definition_id);
self.nesting_stack.push(Nesting::LexicalScope(definition_id));
self.visibility_stack.push(Visibility::Public);
self.visit(&body);
self.visibility_stack.pop();
self.definitions_stack.pop();
self.nesting_stack.pop();
}
}
}

fn visit_module_node(&mut self, node: &ruby_prism::ModuleNode) {
let offset = Offset::from_prism_location(&node.location());
let comments = self.find_comments_for(offset.start()).unwrap_or_default();
let lexical_nesting_id = self.parent_nesting_id().copied();
let lexical_nesting_id = self.parent_lexical_scope_id();

if let Some(constant_ref_id) = self.index_constant_reference(&node.constant_path(), false) {
let definition = Definition::Module(Box::new(ModuleDefinition::new(
Expand All @@ -738,11 +762,11 @@ impl Visit<'_> for RubyIndexer<'_> {
self.add_member_to_current_owner(definition_id);

if let Some(body) = node.body() {
self.definitions_stack.push(definition_id);
self.nesting_stack.push(Nesting::LexicalScope(definition_id));
self.visibility_stack.push(Visibility::Public);
self.visit(&body);
self.visibility_stack.pop();
self.definitions_stack.pop();
self.nesting_stack.pop();
}
}
}
Expand Down Expand Up @@ -778,7 +802,7 @@ impl Visit<'_> for RubyIndexer<'_> {

let offset = Offset::from_prism_location(&node.location());
let comments = self.find_comments_for(offset.start()).unwrap_or_default();
let lexical_nesting_id = self.parent_nesting_id().copied();
let lexical_nesting_id = self.parent_lexical_scope_id();

let singleton_class_name = {
let name = self
Expand Down Expand Up @@ -812,11 +836,11 @@ impl Visit<'_> for RubyIndexer<'_> {
self.add_member_to_current_owner(definition_id);

if let Some(body) = node.body() {
self.definitions_stack.push(definition_id);
self.nesting_stack.push(Nesting::LexicalScope(definition_id));
self.visibility_stack.push(Visibility::Public);
self.visit(&body);
self.visibility_stack.pop();
self.definitions_stack.pop();
self.nesting_stack.pop();
}
}

Expand Down Expand Up @@ -911,7 +935,7 @@ impl Visit<'_> for RubyIndexer<'_> {
let str_id = self.local_graph.intern_string(name);
let offset = Offset::from_prism_location(&node.location());
let comments = self.find_comments_for(offset.start()).unwrap_or_default();
let lexical_nesting_id = self.parent_nesting_id().copied();
let parent_nesting_id = self.parent_nesting_id();
let parameters = self.collect_parameters(node);
let is_singleton = node.receiver().is_some();

Expand Down Expand Up @@ -951,7 +975,7 @@ impl Visit<'_> for RubyIndexer<'_> {
self.uri_id,
offset,
comments,
lexical_nesting_id,
parent_nesting_id,
parameters,
visibility,
receiver,
Expand All @@ -962,9 +986,9 @@ impl Visit<'_> for RubyIndexer<'_> {
self.add_member_to_current_owner(definition_id);

if let Some(body) = node.body() {
self.definitions_stack.push(definition_id);
self.nesting_stack.push(Nesting::Method(definition_id));
self.visit(&body);
self.definitions_stack.pop();
self.nesting_stack.pop();
}
}

Expand All @@ -979,7 +1003,7 @@ impl Visit<'_> for RubyIndexer<'_> {
let mut index_attr = |kind: AttrKind, call: &ruby_prism::CallNode| {
Self::each_string_or_symbol_arg(call, |name, location| {
let str_id = self.local_graph.intern_string(name);
let lexical_nesting_id = self.parent_nesting_id().copied();
let parent_nesting_id = self.parent_nesting_id();
let offset = Offset::from_prism_location(&location);
let comments = self.find_comments_for(offset.start()).unwrap_or_default();

Expand All @@ -989,23 +1013,23 @@ impl Visit<'_> for RubyIndexer<'_> {
self.uri_id,
offset,
comments,
lexical_nesting_id,
parent_nesting_id,
*self.current_visibility(),
))),
AttrKind::Reader => Definition::AttrReader(Box::new(AttrReaderDefinition::new(
str_id,
self.uri_id,
offset,
comments,
lexical_nesting_id,
parent_nesting_id,
*self.current_visibility(),
))),
AttrKind::Writer => Definition::AttrWriter(Box::new(AttrWriterDefinition::new(
str_id,
self.uri_id,
offset,
comments,
lexical_nesting_id,
parent_nesting_id,
*self.current_visibility(),
))),
};
Expand Down Expand Up @@ -1083,7 +1107,7 @@ impl Visit<'_> for RubyIndexer<'_> {
self.uri_id,
offset,
comments,
self.parent_nesting_id().copied(),
self.parent_nesting_id(),
)));

let definition_id = self.local_graph.add_definition(definition);
Expand Down Expand Up @@ -1326,7 +1350,7 @@ impl Visit<'_> for RubyIndexer<'_> {
self.uri_id,
offset,
comments,
self.parent_nesting_id().copied(),
self.parent_nesting_id(),
)));

let definition_id = self.local_graph.add_definition(definition);
Expand All @@ -1349,7 +1373,7 @@ impl Visit<'_> for RubyIndexer<'_> {
self.uri_id,
offset,
comments,
self.parent_nesting_id().copied(),
self.parent_nesting_id(),
)));

let definition_id = self.local_graph.add_definition(definition);
Expand Down
Loading