Conversation
Morriar
left a comment
There was a problem hiding this comment.
What's the plan for anonymous ones like this:
class Foo
Class.new do
def foo; end
end
endI think we'll need to represent class/module defs without a name (or <ANONYMOUS>?) so we can push them onto the stack and avoid registering foo in the wrong parent.
Maybe the handling of Class.new & Module.new should also move to visit_call_node?
06af351 to
fbe7590
Compare
This PR extracts just the new `Nesting` enum portion of #402, to make it a bit easier to handle the existing conflicts with main. Basically, the `definitions_stack` becomes a `nesting_stack`, where we can store the IDs with a bit of differentiation and perform the right behaviour: - LexicalScope: anywhere the `class` or `module` keywords get used, which produces a new lexical scope that binds constants and class variables - Owner: definitions that can own things, but without producing a new lexical scope (like the ones created by `Class.new`) - Method: a method. Just used for instance variables, so that we know we're inside of a method
Another extraction to simplify #402 The impl block for `ClassDefinition` was the only one that was not next to its struct. This PR just moves it next to it.
e7de98b to
8e60e29
Compare
st0012
left a comment
There was a problem hiding this comment.
Can we add these tests to resolution?
#[test]
fn resolving_global_variable_alias_inside_method() {
let mut context = GraphTest::new();
context.index_uri("file:///foo.rb", {
r"
class Foo
def setup
alias $bar $baz
end
end
"
});
context.resolve();
// Global variable aliases should still be owned by Object, regardless of where defined
let read_lock = context.graph().declarations().read().unwrap();
let object = read_lock.get(&DeclarationId::from("Object")).unwrap();
assert_members(object, &["$bar"]);
}
#[test]
fn resolving_method_defined_inside_method() {
let mut context = GraphTest::new();
context.index_uri("file:///foo.rb", {
r"
class Foo
def setup
def inner_method; end
end
end
"
});
context.resolve();
let read_lock = context.graph().declarations().read().unwrap();
let foo_class = read_lock.get(&DeclarationId::from("Foo")).unwrap();
// inner_method should be owned by Foo, not by setup
assert_members(foo_class, &["setup", "inner_method"]);
}
#[test]
fn resolving_attr_accessors_inside_method() {
let mut context = GraphTest::new();
context.index_uri("file:///foo.rb", {
r"
class Foo
def self.setup
attr_reader :reader_attr
attr_writer :writer_attr
attr_accessor :accessor_attr
end
end
"
});
context.resolve();
let read_lock = context.graph().declarations().read().unwrap();
let foo_singleton_class = read_lock.get(&DeclarationId::from("Foo::<Foo>")).unwrap();
assert_members(foo_singleton_class, &["setup"]);
let foo_class = read_lock.get(&DeclarationId::from("Foo")).unwrap();
// All attr_* should be owned by Foo, not by setup
assert_members(foo_class, &["reader_attr", "writer_attr", "accessor_attr"]);
}
| /// # Panics | ||
| /// | ||
| /// Panics if the definition is not a class, module, or singleton class | ||
| fn current_lexical_scope_name_id(&self) -> Option<NameId> { |
There was a problem hiding this comment.
Will it be better to name it like current_lexical_owner_name_id?
Owner includes class/module/singleton class/anonymous class & module. Lexical owner excludes the last one.
There was a problem hiding this comment.
I think the nomenclature needs to be a touch different. We now have 3 types of nestings:
- LexicalScope: anything that uses the
classormodulekeywords - Owner: a namespace that can own things, but isn't a new lexical scope (e.g.:
Class.new) - Method: just to indicate we're inside a method, but it doesn't represent a new lexical scope and can't own anything
I think using "lexical owner" can be confusing because it kind of mixes the first two nesting variants. Personally, I would use lexical_scope and owner separately without mixing the two.
There was a problem hiding this comment.
That's fair. Can this distinction go into the architecture doc?
Also TIL: nomenclature
8e60e29 to
065b5d9
Compare
|
Added the test cases 👍. |

Initial step for #392
This PR starts handling
Class.newandModule.new. The shared characteristic between both is that they create a new possible owner for definitions. That means, you can have methods and instance variables be associated with them.However, neither of those produces of a new lexical scope, so constant references that occur inside of the block ignore these new classes and modules. Only the
classandmodulekeywords can produce new lexical scopes.To account for this, I turned the
definitions_stackinto anesting_stackwhere we can have two types of entries: