From 8d6831fdcc0ee20e642d36697409d218e43d1261 Mon Sep 17 00:00:00 2001
From: Soutaro Matsumoto <matsumoto@soutaro.com>
Date: Tue, 5 Apr 2022 11:23:22 +0900
Subject: [PATCH] Toplevel constant must be referenced through inheritance

---
 .../definition_builder/ancestor_builder.rb    |  1 +
 lib/rbs/resolver/constant_resolver.rb         | 35 +++++++++++----
 test/rbs/resolver/constant_resolver_test.rb   | 43 +++++++++++++++++++
 3 files changed, 71 insertions(+), 8 deletions(-)

diff --git a/lib/rbs/definition_builder/ancestor_builder.rb b/lib/rbs/definition_builder/ancestor_builder.rb
index 8a14384b4..204ed8bb6 100644
--- a/lib/rbs/definition_builder/ancestor_builder.rb
+++ b/lib/rbs/definition_builder/ancestor_builder.rb
@@ -398,6 +398,7 @@ def instance_ancestors(type_name, building_ancestors: [])
 
         one_ancestors = one_instance_ancestors(type_name)
 
+        # @type var ancestors: Array[::RBS::Definition::Ancestor::t]
         ancestors = []
 
         case entry
diff --git a/lib/rbs/resolver/constant_resolver.rb b/lib/rbs/resolver/constant_resolver.rb
index 9c670111b..c25a3cfc4 100644
--- a/lib/rbs/resolver/constant_resolver.rb
+++ b/lib/rbs/resolver/constant_resolver.rb
@@ -106,10 +106,10 @@ def load_context_constants(context)
         # @type var consts: Hash[Symbol, Constant]
         consts = {}
 
-        if context
-          if last = context[1]
-            constants_from_ancestors(last, constants: consts)
-          end
+        if last = context&.[](1)
+          constants_from_ancestors(last, constants: consts)
+        else
+          constants_from_ancestors(BuiltinNames::Object.name, constants: consts)
         end
         constants_from_context(context, constants: consts) or return
         constants_itself(context, constants: consts)
@@ -122,7 +122,7 @@ def load_child_constants(name)
         constants = {}
 
         if table.children(name)
-          builder.ancestor_builder.instance_ancestors(name).ancestors.each do |ancestor|
+          builder.ancestor_builder.instance_ancestors(name).ancestors.reverse_each do |ancestor|
             if ancestor.is_a?(Definition::Ancestor::Instance)
               if ancestor.name == BuiltinNames::Object.name
                 if name != BuiltinNames::Object.name
@@ -152,19 +152,38 @@ def constants_from_context(context, constants:)
             consts = table.children(last) or return false
             constants.merge!(consts)
           end
-        else
-          constants.merge!(table.toplevel)
         end
 
         true
       end
 
       def constants_from_ancestors(module_name, constants:)
-        builder.ancestor_builder.instance_ancestors(module_name).ancestors.each do |ancestor|
+        if (entry = builder.env.class_decls[module_name]).is_a?(Environment::ModuleEntry)
+          self_types = entry.self_types
+          if self_types.empty?
+            self_types << AST::Declarations::Module::Self.new(
+              name: BuiltinNames::Object.name,
+              args: [],
+              location: nil
+            )
+          end
+
+          self_types.each do |self_type|
+            if self_type.name.class?
+              constants_from_ancestors(self_type.name, constants: constants)
+            end
+          end
+        end
+
+        builder.ancestor_builder.instance_ancestors(module_name).ancestors.reverse_each do |ancestor|
           if ancestor.is_a?(Definition::Ancestor::Instance)
             case ancestor.source
             when AST::Members::Include, :super, nil
               consts = table.children(ancestor.name) or raise
+              if ancestor.name == BuiltinNames::Object.name
+                # Insert toplevel constants as ::Object's constants
+                consts.merge!(table.toplevel)
+              end
               constants.merge!(consts)
             end
           end
diff --git a/test/rbs/resolver/constant_resolver_test.rb b/test/rbs/resolver/constant_resolver_test.rb
index 7b7fd0ef5..a9c00a46c 100644
--- a/test/rbs/resolver/constant_resolver_test.rb
+++ b/test/rbs/resolver/constant_resolver_test.rb
@@ -282,4 +282,47 @@ class Stuff
       end
     end
   end
+
+  def test_reference_constant_toplevel
+    SignatureManager.new do |manager|
+      manager.files[Pathname("foo.rbs")] = <<EOF
+CONST: Integer
+
+class Foo
+  CONST: String
+end
+
+class Bar < Foo
+end
+EOF
+      manager.build do |env|
+        builder = DefinitionBuilder.new(env: env)
+        resolver = Resolver::ConstantResolver.new(builder: builder)
+
+        resolver.resolve(:CONST, context: [nil, TypeName("::Bar")]).tap do |constant|
+          assert_equal "::Foo::CONST", constant.name.to_s
+        end
+      end
+    end
+  end
+
+  def test_reference_constant_toplevel2
+    SignatureManager.new do |manager|
+      manager.files[Pathname("foo.rbs")] = <<EOF
+CONST: Integer
+
+class BasicObject
+  CONST: String
+end
+EOF
+      manager.build do |env|
+        builder = DefinitionBuilder.new(env: env)
+        resolver = Resolver::ConstantResolver.new(builder: builder)
+
+        resolver.resolve(:CONST, context: [nil, TypeName("::String")]).tap do |constant|
+          assert_equal "::CONST", constant.name.to_s
+        end
+      end
+    end
+  end
 end