@@ -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 ( ) )
@@ -4965,4 +4998,125 @@ mod tests {
49654998 context. resolve ( ) ;
49664999 assert_declaration_exists ! ( context, "CONST" ) ;
49675000 }
5001+
5002+ #[ test]
5003+ fn including_unresolved_alias ( ) {
5004+ let mut context = GraphTest :: new ( ) ;
5005+ context. index_uri ( "file:///a.rb" , {
5006+ r"
5007+ module Foo; end
5008+ Foo::Bar = Bar
5009+
5010+ module Baz
5011+ include Foo::Bar
5012+ end
5013+ "
5014+ } ) ;
5015+
5016+ context. resolve ( ) ;
5017+ assert_ancestors_eq ! ( context, "Baz" , [ "Baz" ] ) ;
5018+ }
5019+
5020+ #[ test]
5021+ fn prepending_unresolved_alias ( ) {
5022+ let mut context = GraphTest :: new ( ) ;
5023+ context. index_uri ( "file:///a.rb" , {
5024+ r"
5025+ module Foo; end
5026+ Foo::Bar = Bar
5027+
5028+ module Baz
5029+ prepend Foo::Bar
5030+ end
5031+ "
5032+ } ) ;
5033+
5034+ context. resolve ( ) ;
5035+ assert_ancestors_eq ! ( context, "Baz" , [ "Baz" ] ) ;
5036+ }
5037+
5038+ #[ test]
5039+ fn inheriting_unresolved_alias ( ) {
5040+ let mut context = GraphTest :: new ( ) ;
5041+ context. index_uri ( "file:///a.rb" , {
5042+ r"
5043+ module Foo; end
5044+ Foo::Bar = Bar
5045+
5046+ class Baz < Foo::Bar
5047+ end
5048+ "
5049+ } ) ;
5050+
5051+ context. resolve ( ) ;
5052+ assert_ancestors_eq ! ( context, "Baz" , [ "Baz" , "Object" ] ) ;
5053+ }
5054+
5055+ #[ test]
5056+ fn re_opening_unresolved_alias ( ) {
5057+ let mut context = GraphTest :: new ( ) ;
5058+ context. index_uri ( "file:///a.rb" , {
5059+ r"
5060+ module Foo; end
5061+ Foo::Bar = Bar
5062+
5063+ module Foo::Bar
5064+ CONST = 123
5065+ @class_ivar = 123
5066+ @@class_var = 789
5067+
5068+ attr_reader :some_attr
5069+
5070+ def self.class_method; end
5071+
5072+ def initialize
5073+ @instance_ivar = 456
5074+ end
5075+ end
5076+ "
5077+ } ) ;
5078+
5079+ context. resolve ( ) ;
5080+ assert_declaration_does_not_exist ! ( context, "Foo::Bar::CONST" ) ;
5081+ assert_declaration_does_not_exist ! ( context, "Foo::Bar::<Bar>#@class_ivar" ) ;
5082+ assert_declaration_does_not_exist ! ( context, "Foo::Bar#@instance_ivar" ) ;
5083+ assert_declaration_does_not_exist ! ( context, "Foo::Bar#@@class_var" ) ;
5084+ assert_declaration_does_not_exist ! ( context, "Foo::Bar#some_attr()" ) ;
5085+ assert_declaration_does_not_exist ! ( context, "Foo::Bar::<Bar>#class_method()" ) ;
5086+ assert_declaration_does_not_exist ! ( context, "Foo::Bar#initialize()" ) ;
5087+ }
5088+
5089+ #[ test]
5090+ fn re_opening_namespace_alias ( ) {
5091+ let mut context = GraphTest :: new ( ) ;
5092+ context. index_uri ( "file:///a.rb" , {
5093+ r"
5094+ module Foo; end
5095+ ALIAS = Foo
5096+
5097+ module ALIAS
5098+ CONST = 123
5099+ @class_ivar = 123
5100+ @@class_var = 789
5101+
5102+ attr_reader :some_attr
5103+
5104+ def self.class_method; end
5105+
5106+ def initialize
5107+ @instance_ivar = 456
5108+ end
5109+ end
5110+ "
5111+ } ) ;
5112+
5113+ context. resolve ( ) ;
5114+ assert_declaration_exists ! ( context, "Foo::CONST" ) ;
5115+ assert_declaration_exists ! ( context, "Foo::<Foo>#@class_ivar" ) ;
5116+ assert_declaration_exists ! ( context, "Foo#@instance_ivar" ) ;
5117+ assert_declaration_exists ! ( context, "Foo#@@class_var" ) ;
5118+ assert_declaration_exists ! ( context, "Foo#some_attr()" ) ;
5119+ assert_declaration_exists ! ( context, "Foo::<Foo>#class_method()" ) ;
5120+ assert_declaration_exists ! ( context, "Foo#initialize()" ) ;
5121+ }
49685122}
0 commit comments