-
Notifications
You must be signed in to change notification settings - Fork 0
Index includes, prepends, and extends with RBS indexer #617
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,19 @@ | ||
| //! Visit the RBS AST and create type definitions. | ||
|
|
||
| use ruby_rbs::node::{ | ||
| self, ClassNode, CommentNode, ConstantNode, GlobalNode, ModuleNode, Node, NodeList, TypeNameNode, Visit, | ||
| self, ClassNode, CommentNode, ConstantNode, ExtendNode, GlobalNode, IncludeNode, ModuleNode, Node, NodeList, | ||
| PrependNode, TypeNameNode, Visit, | ||
| }; | ||
|
|
||
| use crate::diagnostic::Rule; | ||
| use crate::indexing::local_graph::LocalGraph; | ||
| use crate::model::comment::Comment; | ||
| use crate::model::definitions::{ | ||
| ClassDefinition, ConstantDefinition, Definition, DefinitionFlags, GlobalVariableDefinition, ModuleDefinition, | ||
| ClassDefinition, ConstantDefinition, Definition, DefinitionFlags, ExtendDefinition, GlobalVariableDefinition, | ||
| IncludeDefinition, Mixin, ModuleDefinition, PrependDefinition, | ||
| }; | ||
| use crate::model::document::Document; | ||
| use crate::model::ids::{DefinitionId, NameId, UriId}; | ||
| use crate::model::ids::{DefinitionId, NameId, ReferenceId, UriId}; | ||
| use crate::model::name::{Name, ParentScope}; | ||
| use crate::model::references::ConstantReference; | ||
| use crate::offset::Offset; | ||
|
|
@@ -103,6 +105,35 @@ impl<'a> RBSIndexer<'a> { | |
| }) | ||
| } | ||
|
|
||
| fn add_mixin_to_current_lexical_scope(&mut self, owner_id: DefinitionId, mixin: Mixin) { | ||
| let owner = self | ||
| .local_graph | ||
| .get_definition_mut(owner_id) | ||
| .expect("owner definition should exist"); | ||
|
|
||
| match owner { | ||
| Definition::Class(class) => class.add_mixin(mixin), | ||
| Definition::Module(module) => module.add_mixin(mixin), | ||
| _ => unreachable!("RBS nesting stack only contains modules/classes"), | ||
| } | ||
| } | ||
|
|
||
| fn index_mixin(&mut self, type_name: &TypeNameNode, mixin_fn: fn(ReferenceId) -> Mixin) { | ||
| let Some(lexical_nesting_id) = self.parent_lexical_scope_id() else { | ||
| return; | ||
| }; | ||
|
|
||
| let nesting_name_id = self.nesting_name_id(Some(lexical_nesting_id)); | ||
| let name_id = self.index_type_name(type_name, nesting_name_id); | ||
| let offset = Offset::from_rbs_location(&type_name.location()); | ||
|
|
||
| let constant_ref_id = | ||
| self.local_graph | ||
| .add_constant_reference(ConstantReference::new(name_id, self.uri_id, offset)); | ||
|
|
||
| self.add_mixin_to_current_lexical_scope(lexical_nesting_id, mixin_fn(constant_ref_id)); | ||
| } | ||
|
|
||
| fn add_member_to_current_lexical_scope(&mut self, owner_id: DefinitionId, member_id: DefinitionId) { | ||
| let owner = self | ||
| .local_graph | ||
|
|
@@ -275,6 +306,24 @@ impl Visit for RBSIndexer<'_> { | |
|
|
||
| self.register_definition(definition, lexical_nesting_id); | ||
| } | ||
|
|
||
| fn visit_include_node(&mut self, include_node: &IncludeNode) { | ||
| self.index_mixin(&include_node.name(), |ref_id| { | ||
| Mixin::Include(IncludeDefinition::new(ref_id)) | ||
| }); | ||
| } | ||
|
|
||
| fn visit_prepend_node(&mut self, prepend_node: &PrependNode) { | ||
| self.index_mixin(&prepend_node.name(), |ref_id| { | ||
| Mixin::Prepend(PrependDefinition::new(ref_id)) | ||
| }); | ||
| } | ||
|
|
||
| fn visit_extend_node(&mut self, extend_node: &ExtendNode) { | ||
| self.index_mixin(&extend_node.name(), |ref_id| { | ||
| Mixin::Extend(ExtendDefinition::new(ref_id)) | ||
| }); | ||
| } | ||
| } | ||
|
|
||
| #[cfg(test)] | ||
|
|
@@ -285,7 +334,7 @@ mod tests { | |
| use crate::model::definitions::DefinitionFlags; | ||
| use crate::test_utils::LocalGraphTest; | ||
| use crate::{ | ||
| assert_def_comments_eq, assert_def_name_eq, assert_def_name_offset_eq, assert_def_str_eq, | ||
| assert_def_comments_eq, assert_def_mixins_eq, assert_def_name_eq, assert_def_name_offset_eq, assert_def_str_eq, | ||
| assert_def_superclass_ref_eq, assert_definition_at, assert_local_diagnostics_eq, assert_no_local_diagnostics, | ||
| }; | ||
|
|
||
|
|
@@ -506,6 +555,62 @@ mod tests { | |
| }); | ||
| } | ||
|
|
||
| #[test] | ||
| fn index_mixins() { | ||
| let context = index_source({ | ||
| " | ||
| class Foo | ||
| include Bar | ||
| prepend Baz | ||
| extend Qux | ||
| end | ||
| " | ||
| }); | ||
|
|
||
| assert_no_local_diagnostics!(&context); | ||
|
|
||
| assert_definition_at!(&context, "1:1-5:4", Class, |def| { | ||
| assert_def_mixins_eq!(&context, def, Include, ["Bar"]); | ||
| assert_def_mixins_eq!(&context, def, Prepend, ["Baz"]); | ||
| assert_def_mixins_eq!(&context, def, Extend, ["Qux"]); | ||
| }); | ||
| } | ||
|
|
||
| #[test] | ||
| fn index_multiple_includes() { | ||
| let context = index_source({ | ||
| " | ||
| module Foo | ||
| include Bar | ||
| include Baz | ||
vinistock marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| end | ||
| " | ||
| }); | ||
|
|
||
| assert_no_local_diagnostics!(&context); | ||
|
|
||
| assert_definition_at!(&context, "1:1-4:4", Module, |def| { | ||
| assert_def_mixins_eq!(&context, def, Include, ["Bar", "Baz"]); | ||
| }); | ||
| } | ||
|
|
||
| #[test] | ||
| fn index_include_qualified_name() { | ||
| let context = index_source({ | ||
| " | ||
| class Foo | ||
| include Bar::Baz | ||
| end | ||
| " | ||
| }); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The test name says "qualified name" but
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, we should assert on the entire string
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll take the suggestion and fix the macro to assert the full qualified path in a follow-up PR 👍 |
||
|
|
||
| assert_no_local_diagnostics!(&context); | ||
|
|
||
| assert_definition_at!(&context, "1:1-3:4", Class, |def| { | ||
| assert_def_mixins_eq!(&context, def, Include, ["Baz"]); | ||
| }); | ||
| } | ||
|
|
||
| #[test] | ||
| fn index_class_and_module_nesting() { | ||
| let context = index_source({ | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The Ruby indexer has
index_includes_at_top_levelto confirm top-level mixins are silently ignored. Should we add an equivalent here for the RBS indexer to document that behavior explicitly?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait, is that the case? If so, we have a bug. Mixin operations at the top level impact
Object:Regardless, we should add the test.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Including at the top level returns a parse error.
In RBS docs, mixins are not part of the declarations and I believe we're hitting the default case here.
Doesn't look like this is an issue for the RBS indexer