@@ -308,29 +308,41 @@ impl<'a> Resolver<'a> {
308308
309309 singleton_id
310310 }
311- None => self . resolve_lexical_owner ( * method_definition. lexical_nesting_id ( ) ) ,
311+ None => {
312+ let Some ( resolved) = self . resolve_lexical_owner ( * method_definition. lexical_nesting_id ( ) )
313+ else {
314+ continue ;
315+ } ;
316+ resolved
317+ }
312318 } ;
313319
314320 self . create_declaration ( str_id, id, owner_id, |name| {
315321 Declaration :: Method ( Box :: new ( MethodDeclaration :: new ( name, owner_id) ) )
316322 } ) ;
317323 }
318324 Definition :: AttrAccessor ( attr) => {
319- let owner_id = self . resolve_lexical_owner ( * attr. lexical_nesting_id ( ) ) ;
325+ let Some ( owner_id) = self . resolve_lexical_owner ( * attr. lexical_nesting_id ( ) ) else {
326+ continue ;
327+ } ;
320328
321329 self . create_declaration ( * attr. str_id ( ) , id, owner_id, |name| {
322330 Declaration :: Method ( Box :: new ( MethodDeclaration :: new ( name, owner_id) ) )
323331 } ) ;
324332 }
325333 Definition :: AttrReader ( attr) => {
326- let owner_id = self . resolve_lexical_owner ( * attr. lexical_nesting_id ( ) ) ;
334+ let Some ( owner_id) = self . resolve_lexical_owner ( * attr. lexical_nesting_id ( ) ) else {
335+ continue ;
336+ } ;
327337
328338 self . create_declaration ( * attr. str_id ( ) , id, owner_id, |name| {
329339 Declaration :: Method ( Box :: new ( MethodDeclaration :: new ( name, owner_id) ) )
330340 } ) ;
331341 }
332342 Definition :: AttrWriter ( attr) => {
333- let owner_id = self . resolve_lexical_owner ( * attr. lexical_nesting_id ( ) ) ;
343+ let Some ( owner_id) = self . resolve_lexical_owner ( * attr. lexical_nesting_id ( ) ) else {
344+ continue ;
345+ } ;
334346
335347 self . create_declaration ( * attr. str_id ( ) , id, owner_id, |name| {
336348 Declaration :: Method ( Box :: new ( MethodDeclaration :: new ( name, owner_id) ) )
@@ -403,7 +415,9 @@ impl<'a> Resolver<'a> {
403415 }
404416
405417 // If the method has no explicit receiver, we resolve the owner based on the lexical nesting
406- let method_owner_id = self . resolve_lexical_owner ( * method. lexical_nesting_id ( ) ) ;
418+ let Some ( method_owner_id) = self . resolve_lexical_owner ( * method. lexical_nesting_id ( ) ) else {
419+ continue ;
420+ } ;
407421
408422 // If the method is in a singleton class, the instance variable belongs to the class object
409423 // Like `class << Foo; def bar; @bar = 1; end; end`, where `@bar` is owned by `Foo::<Foo>`
@@ -436,9 +450,10 @@ impl<'a> Resolver<'a> {
436450 . definition_id_to_declaration_id ( nesting_id)
437451 . copied ( )
438452 . unwrap_or ( * OBJECT_ID ) ;
439- let owner_id = self
440- . get_or_create_singleton_class ( nesting_decl_id)
441- . expect ( "class/module nesting should always be a namespace" ) ;
453+
454+ let Some ( owner_id) = self . get_or_create_singleton_class ( nesting_decl_id) else {
455+ continue ;
456+ } ;
442457 {
443458 debug_assert ! (
444459 matches!(
@@ -494,7 +509,9 @@ impl<'a> Resolver<'a> {
494509 }
495510 }
496511 Definition :: MethodAlias ( alias) => {
497- let owner_id = self . resolve_lexical_owner ( * alias. lexical_nesting_id ( ) ) ;
512+ let Some ( owner_id) = self . resolve_lexical_owner ( * alias. lexical_nesting_id ( ) ) else {
513+ continue ;
514+ } ;
498515
499516 self . create_declaration ( * alias. new_name_str_id ( ) , id, owner_id, |name| {
500517 Declaration :: Method ( Box :: new ( MethodDeclaration :: new ( name, owner_id) ) )
@@ -537,7 +554,8 @@ impl<'a> Resolver<'a> {
537554 self . graph . add_member ( & owner_id, declaration_id, str_id) ;
538555 }
539556
540- /// Resolves owner for class variables, bypassing singleton classes.
557+ /// Resolves owner for class variables, bypassing singleton classes. Returns `None` if the owner can't be
558+ /// determined (e.g., unresolved constant alias).
541559 fn resolve_class_variable_owner ( & self , lexical_nesting_id : Option < DefinitionId > ) -> Option < DeclarationId > {
542560 let mut current_nesting = lexical_nesting_id;
543561 while let Some ( nesting_id) = current_nesting {
@@ -549,13 +567,24 @@ impl<'a> Resolver<'a> {
549567 break ;
550568 }
551569 }
552- current_nesting. and_then ( |id| self . graph . definition_id_to_declaration_id ( id) . copied ( ) )
570+ let declaration_id = current_nesting. and_then ( |id| self . graph . definition_id_to_declaration_id ( id) . copied ( ) ) ?;
571+
572+ // If the declaration is a constant alias, follow the alias chain to find the
573+ // target namespace. Returns None if the alias target is unresolved.
574+ if matches ! (
575+ self . graph. declarations( ) . get( & declaration_id) ,
576+ Some ( Declaration :: ConstantAlias ( _) )
577+ ) {
578+ self . resolve_to_namespace ( declaration_id)
579+ } else {
580+ Some ( declaration_id)
581+ }
553582 }
554583
555584 /// Resolves owner from lexical nesting.
556- fn resolve_lexical_owner ( & self , lexical_nesting_id : Option < DefinitionId > ) -> DeclarationId {
585+ fn resolve_lexical_owner ( & self , lexical_nesting_id : Option < DefinitionId > ) -> Option < DeclarationId > {
557586 let Some ( id) = lexical_nesting_id else {
558- return * OBJECT_ID ;
587+ return Some ( * OBJECT_ID ) ;
559588 } ;
560589
561590 // If no declaration exists yet for this definition, walk up the lexical chain.
@@ -566,16 +595,20 @@ impl<'a> Resolver<'a> {
566595 return self . resolve_lexical_owner ( * definition. lexical_nesting_id ( ) ) ;
567596 } ;
568597
569- let declarations = self . graph . declarations ( ) ;
598+ let decl = self . graph . declarations ( ) . get ( declaration_id ) . unwrap ( ) ;
570599
571600 // If the associated declaration is a namespace that can own things, we found the right owner. Otherwise, we might
572601 // have found something nested inside something else (like a method), in which case we have to recurse until we find
573602 // the appropriate owner
574603 if matches ! (
575- declarations . get ( declaration_id ) . unwrap ( ) ,
604+ decl ,
576605 Declaration :: Namespace ( Namespace :: Class ( _) | Namespace :: Module ( _) | Namespace :: SingletonClass ( _) )
577606 ) {
578- * declaration_id
607+ Some ( * declaration_id)
608+ } else if matches ! ( decl, Declaration :: ConstantAlias ( _) ) {
609+ // Follow the alias chain to find the target namespace. If the alias is unresolved,
610+ // the definition cannot be properly owned and should be skipped by the caller.
611+ self . resolve_to_namespace ( * declaration_id)
579612 } else {
580613 let definition = self . graph . definitions ( ) . get ( & id) . unwrap ( ) ;
581614 self . resolve_lexical_owner ( * definition. lexical_nesting_id ( ) )
@@ -5031,4 +5064,125 @@ mod tests {
50315064 context. resolve ( ) ;
50325065 assert_declaration_exists ! ( context, "CONST" ) ;
50335066 }
5067+
5068+ #[ test]
5069+ fn including_unresolved_alias ( ) {
5070+ let mut context = GraphTest :: new ( ) ;
5071+ context. index_uri ( "file:///a.rb" , {
5072+ r"
5073+ module Foo; end
5074+ Foo::Bar = Bar
5075+
5076+ module Baz
5077+ include Foo::Bar
5078+ end
5079+ "
5080+ } ) ;
5081+
5082+ context. resolve ( ) ;
5083+ assert_ancestors_eq ! ( context, "Baz" , [ "Baz" ] ) ;
5084+ }
5085+
5086+ #[ test]
5087+ fn prepending_unresolved_alias ( ) {
5088+ let mut context = GraphTest :: new ( ) ;
5089+ context. index_uri ( "file:///a.rb" , {
5090+ r"
5091+ module Foo; end
5092+ Foo::Bar = Bar
5093+
5094+ module Baz
5095+ prepend Foo::Bar
5096+ end
5097+ "
5098+ } ) ;
5099+
5100+ context. resolve ( ) ;
5101+ assert_ancestors_eq ! ( context, "Baz" , [ "Baz" ] ) ;
5102+ }
5103+
5104+ #[ test]
5105+ fn inheriting_unresolved_alias ( ) {
5106+ let mut context = GraphTest :: new ( ) ;
5107+ context. index_uri ( "file:///a.rb" , {
5108+ r"
5109+ module Foo; end
5110+ Foo::Bar = Bar
5111+
5112+ class Baz < Foo::Bar
5113+ end
5114+ "
5115+ } ) ;
5116+
5117+ context. resolve ( ) ;
5118+ assert_ancestors_eq ! ( context, "Baz" , [ "Baz" , "Object" ] ) ;
5119+ }
5120+
5121+ #[ test]
5122+ fn re_opening_unresolved_alias ( ) {
5123+ let mut context = GraphTest :: new ( ) ;
5124+ context. index_uri ( "file:///a.rb" , {
5125+ r"
5126+ module Foo; end
5127+ Foo::Bar = Bar
5128+
5129+ module Foo::Bar
5130+ CONST = 123
5131+ @class_ivar = 123
5132+ @@class_var = 789
5133+
5134+ attr_reader :some_attr
5135+
5136+ def self.class_method; end
5137+
5138+ def initialize
5139+ @instance_ivar = 456
5140+ end
5141+ end
5142+ "
5143+ } ) ;
5144+
5145+ context. resolve ( ) ;
5146+ assert_declaration_does_not_exist ! ( context, "Foo::Bar::CONST" ) ;
5147+ assert_declaration_does_not_exist ! ( context, "Foo::Bar::<Bar>#@class_ivar" ) ;
5148+ assert_declaration_does_not_exist ! ( context, "Foo::Bar#@instance_ivar" ) ;
5149+ assert_declaration_does_not_exist ! ( context, "Foo::Bar#@@class_var" ) ;
5150+ assert_declaration_does_not_exist ! ( context, "Foo::Bar#some_attr()" ) ;
5151+ assert_declaration_does_not_exist ! ( context, "Foo::Bar::<Bar>#class_method()" ) ;
5152+ assert_declaration_does_not_exist ! ( context, "Foo::Bar#initialize()" ) ;
5153+ }
5154+
5155+ #[ test]
5156+ fn re_opening_namespace_alias ( ) {
5157+ let mut context = GraphTest :: new ( ) ;
5158+ context. index_uri ( "file:///a.rb" , {
5159+ r"
5160+ module Foo; end
5161+ ALIAS = Foo
5162+
5163+ module ALIAS
5164+ CONST = 123
5165+ @class_ivar = 123
5166+ @@class_var = 789
5167+
5168+ attr_reader :some_attr
5169+
5170+ def self.class_method; end
5171+
5172+ def initialize
5173+ @instance_ivar = 456
5174+ end
5175+ end
5176+ "
5177+ } ) ;
5178+
5179+ context. resolve ( ) ;
5180+ assert_declaration_exists ! ( context, "Foo::CONST" ) ;
5181+ assert_declaration_exists ! ( context, "Foo::<Foo>#@class_ivar" ) ;
5182+ assert_declaration_exists ! ( context, "Foo#@instance_ivar" ) ;
5183+ assert_declaration_exists ! ( context, "Foo#@@class_var" ) ;
5184+ assert_declaration_exists ! ( context, "Foo#some_attr()" ) ;
5185+ assert_declaration_exists ! ( context, "Foo::<Foo>#class_method()" ) ;
5186+ assert_declaration_exists ! ( context, "Foo#initialize()" ) ;
5187+ }
50345188}
0 commit comments