@@ -7,7 +7,7 @@ use crate::model::{
77 declaration:: {
88 Ancestor , Ancestors , ClassDeclaration , ClassVariableDeclaration , ConstantAliasDeclaration , ConstantDeclaration ,
99 Declaration , GlobalVariableDeclaration , InstanceVariableDeclaration , MethodDeclaration , ModuleDeclaration ,
10- Namespace , SingletonClassDeclaration ,
10+ Namespace , SingletonClassDeclaration , TodoDeclaration ,
1111 } ,
1212 definitions:: { Definition , Mixin , Receiver } ,
1313 graph:: { CLASS_ID , Graph , MODULE_ID , OBJECT_ID } ,
@@ -211,6 +211,7 @@ impl<'a> Resolver<'a> {
211211 }
212212 Outcome :: Unresolved ( None ) => {
213213 // We couldn't resolve this name. Emit a diagnostic
214+ self . unit_queue . push_back ( unit_id) ;
214215 }
215216 Outcome :: Retry ( Some ( id_needing_linearization) ) | Outcome :: Unresolved ( Some ( id_needing_linearization) ) => {
216217 self . unit_queue . push_back ( unit_id) ;
@@ -1027,12 +1028,51 @@ impl<'a> Resolver<'a> {
10271028 fn name_owner_id ( & mut self , name_id : NameId ) -> Outcome {
10281029 let name_ref = self . graph . names ( ) . get ( & name_id) . unwrap ( ) ;
10291030
1030- if let Some ( parent_scope) = name_ref. parent_scope ( ) . as_ref ( ) {
1031+ if let Some ( & parent_scope) = name_ref. parent_scope ( ) . as_ref ( ) {
10311032 // If we have `A::B`, the owner of `B` is whatever `A` resolves to.
10321033 // If `A` is an alias, resolve through to get the actual namespace.
1033- match self . resolve_constant_internal ( * parent_scope) {
1034+ // On `Retry`, we don't create a Todo: the parent may still resolve through inheritance once ancestors are
1035+ // linearized. We only create Todos for `Unresolved` outcomes where the parent is genuinely unknown.
1036+ match self . resolve_constant_internal ( parent_scope) {
10341037 Outcome :: Resolved ( id, linearization) => self . resolve_to_primary_namespace ( id, linearization) ,
1035- other => other,
1038+ // Retry or Unresolved(Some(_)) means we might find it later through ancestor linearization
1039+ Outcome :: Retry ( id) => Outcome :: Retry ( id) ,
1040+ Outcome :: Unresolved ( Some ( id) ) => Outcome :: Unresolved ( Some ( id) ) ,
1041+ // Only create a Todo when genuinely unresolvable (no pending linearizations)
1042+ Outcome :: Unresolved ( None ) => {
1043+ let parent_name = self . graph . names ( ) . get ( & parent_scope) . unwrap ( ) ;
1044+ let parent_str_id = * parent_name. str ( ) ;
1045+
1046+ let parent_owner_id = match self . name_owner_id ( parent_scope) {
1047+ Outcome :: Resolved ( id, _) => id,
1048+ _ => * OBJECT_ID ,
1049+ } ;
1050+
1051+ let fully_qualified_name = if parent_owner_id == * OBJECT_ID {
1052+ self . graph . strings ( ) . get ( & parent_str_id) . unwrap ( ) . to_string ( )
1053+ } else {
1054+ format ! (
1055+ "{}::{}" ,
1056+ self . graph. declarations( ) . get( & parent_owner_id) . unwrap( ) . name( ) ,
1057+ self . graph. strings( ) . get( & parent_str_id) . unwrap( ) . as_str( )
1058+ )
1059+ } ;
1060+
1061+ let declaration_id = DeclarationId :: from ( & fully_qualified_name) ;
1062+
1063+ if !self . graph . declarations ( ) . contains_key ( & declaration_id) {
1064+ self . graph . declarations_mut ( ) . insert (
1065+ declaration_id,
1066+ Declaration :: Namespace ( Namespace :: Todo ( Box :: new ( TodoDeclaration :: new (
1067+ fully_qualified_name,
1068+ parent_owner_id,
1069+ ) ) ) ) ,
1070+ ) ;
1071+ self . graph . add_member ( & parent_owner_id, declaration_id, parent_str_id) ;
1072+ }
1073+
1074+ Outcome :: Resolved ( declaration_id, None )
1075+ }
10361076 }
10371077 } else if let Some ( nesting_id) = name_ref. nesting ( )
10381078 && !name_ref. parent_scope ( ) . is_top_level ( )
@@ -1633,8 +1673,8 @@ mod tests {
16331673 assert_constant_reference_to, assert_declaration_definitions_count_eq, assert_declaration_does_not_exist,
16341674 assert_declaration_exists, assert_declaration_kind_eq, assert_declaration_references_count_eq,
16351675 assert_descendants, assert_diagnostics_eq, assert_instance_variables_eq, assert_members_eq,
1636- assert_no_constant_alias_target, assert_no_diagnostics, assert_no_members, assert_owner_eq ,
1637- assert_singleton_class_eq,
1676+ assert_no_constant_alias_target, assert_no_diagnostics, assert_no_members, assert_no_todo_declarations ,
1677+ assert_owner_eq , assert_singleton_class_eq, assert_todo_declarations_eq ,
16381678 } ;
16391679
16401680 #[ test]
@@ -1762,6 +1802,7 @@ mod tests {
17621802 context. resolve ( ) ;
17631803
17641804 assert_no_diagnostics ! ( & context) ;
1805+ assert_no_todo_declarations ! ( context) ;
17651806
17661807 assert_members_eq ! ( context, "Foo" , [ "Bar" , "Baz" ] ) ;
17671808 assert_owner_eq ! ( context, "Foo" , "Object" ) ;
@@ -1819,6 +1860,7 @@ mod tests {
18191860 context. resolve ( ) ;
18201861
18211862 assert_no_diagnostics ! ( & context) ;
1863+ assert_no_todo_declarations ! ( context) ;
18221864
18231865 assert_no_members ! ( context, "Foo" ) ;
18241866 assert_owner_eq ! ( context, "Foo" , "Object" ) ;
@@ -1843,6 +1885,7 @@ mod tests {
18431885 context. resolve ( ) ;
18441886
18451887 assert_no_diagnostics ! ( & context) ;
1888+ assert_no_todo_declarations ! ( context) ;
18461889
18471890 assert_no_members ! ( context, "Foo" ) ;
18481891 assert_owner_eq ! ( context, "Foo" , "Object" ) ;
@@ -1869,9 +1912,12 @@ mod tests {
18691912
18701913 assert_no_diagnostics ! ( & context) ;
18711914
1872- assert_declaration_does_not_exist ! ( context, "Foo" ) ;
1873- assert_declaration_does_not_exist ! ( context, "Foo::Bar" ) ;
1874- assert_declaration_does_not_exist ! ( context, "Foo::Bar::Baz" ) ;
1915+ assert_todo_declarations_eq ! ( context, [ "Foo" ] ) ;
1916+
1917+ assert_members_eq ! ( context, "Object" , vec![ "Foo" ] ) ;
1918+ assert_members_eq ! ( context, "Foo" , vec![ "Bar" ] ) ;
1919+ assert_members_eq ! ( context, "Foo::Bar" , vec![ "Baz" ] ) ;
1920+ assert_no_members ! ( context, "Foo::Bar::Baz" ) ;
18751921 }
18761922
18771923 #[ test]
@@ -4106,6 +4152,7 @@ mod tests {
41064152 context. resolve ( ) ;
41074153
41084154 assert_no_diagnostics ! ( & context) ;
4155+ assert_no_todo_declarations ! ( context) ;
41094156
41104157 // In the same order of appearence
41114158 assert_declaration_exists ! ( context, "Foo" ) ;
@@ -4151,6 +4198,7 @@ mod tests {
41514198 context. resolve ( ) ;
41524199
41534200 assert_no_diagnostics ! ( & context, & [ Rule :: ParseWarning ] ) ;
4201+ assert_no_todo_declarations ! ( context) ;
41544202
41554203 // FIXME: this is wrong, the reference is not to `Bar::Foo`, but to `Foo`
41564204 assert_constant_reference_to ! ( context, "Bar::Foo" , "file:///foo.rb:4:3-4:6" ) ;
@@ -4967,4 +5015,32 @@ mod tests {
49675015 assert_declaration_exists ! ( context, "Baz" ) ;
49685016 assert_constant_reference_to ! ( context, "CONST" , "file:///reopen.rb:5:5-5:10" ) ;
49695017 }
5018+
5019+ #[ test]
5020+ fn resolve_missing_declaration_to_todo ( ) {
5021+ let mut context = GraphTest :: new ( ) ;
5022+ context. index_uri ( "file:///foo.rb" , {
5023+ r"
5024+ class Foo::Bar
5025+ include Foo::Baz
5026+
5027+ def bar; end
5028+ end
5029+
5030+ module Foo::Baz
5031+ def baz; end
5032+ end
5033+ "
5034+ } ) ;
5035+ context. resolve ( ) ;
5036+
5037+ assert_no_diagnostics ! ( & context) ;
5038+
5039+ assert_todo_declarations_eq ! ( context, [ "Foo" ] ) ;
5040+
5041+ assert_members_eq ! ( context, "Object" , vec![ "Foo" ] ) ;
5042+ assert_members_eq ! ( context, "Foo" , vec![ "Bar" , "Baz" ] ) ;
5043+ assert_members_eq ! ( context, "Foo::Bar" , vec![ "bar()" ] ) ;
5044+ assert_members_eq ! ( context, "Foo::Baz" , vec![ "baz()" ] ) ;
5045+ }
49705046}
0 commit comments