@@ -4354,6 +4354,90 @@ mod incremental_resolution_tests {
43544354 assert_declaration_exists ! ( incremental, "Foo::<Foo>#bar()" ) ;
43554355 }
43564356
4357+ /// Same delete/re-add gap, but with explicit singleton method syntax
4358+ /// (`def Foo.bar`). The constant receiver is unresolved while `Foo` is
4359+ /// missing, so the method definition must be re-queued rather than dropped.
4360+ #[ test]
4361+ fn explicit_singleton_method_survives_receiver_delete_readd ( ) {
4362+ let mut incremental = GraphTest :: new ( ) ;
4363+ incremental. index_uri ( "file:///foo.rb" , "class Foo; end" ) ;
4364+ incremental. index_uri ( "file:///singleton.rb" , "def Foo.bar; end" ) ;
4365+ incremental. resolve ( ) ;
4366+ assert_declaration_exists ! ( incremental, "Foo::<Foo>#bar()" ) ;
4367+
4368+ incremental. delete_uri ( "file:///foo.rb" ) ;
4369+ incremental. resolve ( ) ;
4370+ assert_declaration_does_not_exist ! ( incremental, "Foo::<Foo>#bar()" ) ;
4371+
4372+ incremental. index_uri ( "file:///foo.rb" , "class Foo; end" ) ;
4373+ incremental. resolve ( ) ;
4374+
4375+ let mut fresh = GraphTest :: new ( ) ;
4376+ fresh. index_uri ( "file:///foo.rb" , "class Foo; end" ) ;
4377+ fresh. index_uri ( "file:///singleton.rb" , "def Foo.bar; end" ) ;
4378+ fresh. resolve ( ) ;
4379+
4380+ assert_declaration_ids_match ( & incremental, & fresh) ;
4381+ assert_declaration_exists ! ( incremental, "Foo::<Foo>#bar()" ) ;
4382+ }
4383+
4384+ /// Instance variables inside explicit singleton methods also depend on
4385+ /// resolving the constant receiver. Both the method and ivar definitions
4386+ /// must survive the temporary unresolved receiver.
4387+ #[ test]
4388+ fn explicit_singleton_method_ivar_survives_receiver_delete_readd ( ) {
4389+ let mut incremental = GraphTest :: new ( ) ;
4390+ incremental. index_uri ( "file:///foo.rb" , "class Foo; end" ) ;
4391+ incremental. index_uri ( "file:///singleton.rb" , "def Foo.bar; @x = 1; end" ) ;
4392+ incremental. resolve ( ) ;
4393+ assert_declaration_exists ! ( incremental, "Foo::<Foo>#bar()" ) ;
4394+ assert_declaration_exists ! ( incremental, "Foo::<Foo>#@x" ) ;
4395+
4396+ incremental. delete_uri ( "file:///foo.rb" ) ;
4397+ incremental. resolve ( ) ;
4398+ assert_declaration_does_not_exist ! ( incremental, "Foo::<Foo>#bar()" ) ;
4399+ assert_declaration_does_not_exist ! ( incremental, "Foo::<Foo>#@x" ) ;
4400+
4401+ incremental. index_uri ( "file:///foo.rb" , "class Foo; end" ) ;
4402+ incremental. resolve ( ) ;
4403+
4404+ let mut fresh = GraphTest :: new ( ) ;
4405+ fresh. index_uri ( "file:///foo.rb" , "class Foo; end" ) ;
4406+ fresh. index_uri ( "file:///singleton.rb" , "def Foo.bar; @x = 1; end" ) ;
4407+ fresh. resolve ( ) ;
4408+
4409+ assert_declaration_ids_match ( & incremental, & fresh) ;
4410+ assert_declaration_exists ! ( incremental, "Foo::<Foo>#bar()" ) ;
4411+ assert_declaration_exists ! ( incremental, "Foo::<Foo>#@x" ) ;
4412+ }
4413+
4414+ /// Method aliases with a constant receiver (`Foo.alias_method`) use a
4415+ /// separate receiver path from `def Foo.bar`; it must also preserve work
4416+ /// while the receiver is temporarily absent.
4417+ #[ test]
4418+ fn constant_receiver_method_alias_survives_receiver_delete_readd ( ) {
4419+ let mut incremental = GraphTest :: new ( ) ;
4420+ incremental. index_uri ( "file:///foo.rb" , "class Foo; end" ) ;
4421+ incremental. index_uri ( "file:///alias.rb" , "Foo.alias_method :new_name, :old_name" ) ;
4422+ incremental. resolve ( ) ;
4423+ assert_declaration_exists ! ( incremental, "Foo#new_name()" ) ;
4424+
4425+ incremental. delete_uri ( "file:///foo.rb" ) ;
4426+ incremental. resolve ( ) ;
4427+ assert_declaration_does_not_exist ! ( incremental, "Foo#new_name()" ) ;
4428+
4429+ incremental. index_uri ( "file:///foo.rb" , "class Foo; end" ) ;
4430+ incremental. resolve ( ) ;
4431+
4432+ let mut fresh = GraphTest :: new ( ) ;
4433+ fresh. index_uri ( "file:///foo.rb" , "class Foo; end" ) ;
4434+ fresh. index_uri ( "file:///alias.rb" , "Foo.alias_method :new_name, :old_name" ) ;
4435+ fresh. resolve ( ) ;
4436+
4437+ assert_declaration_ids_match ( & incremental, & fresh) ;
4438+ assert_declaration_exists ! ( incremental, "Foo#new_name()" ) ;
4439+ }
4440+
43574441 /// Same shape as `singleton_definition_survives_receiver_delete_readd` but
43584442 /// for a method alias inside the singleton block. Without re-queueing the
43594443 /// def, `alias new old` would be silently dropped while `Foo` is missing
0 commit comments