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