diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a1d197a0..3c9ce9453 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## master +* Fix constant resolution [#288](https://github.com/ruby/rbs/pull/288) + ## 0.2.0 * The first release of RBS gem. diff --git a/lib/rbs/cli.rb b/lib/rbs/cli.rb index dc0ae1bab..25e27cc4c 100644 --- a/lib/rbs/cli.rb +++ b/lib/rbs/cli.rb @@ -362,7 +362,7 @@ def run_constant(args, options) name = Namespace.parse(args[0]).to_type_name stdout.puts "Constant name: #{name}" - constant = table.resolve_constant_reference(name, context: namespace) + constant = table.resolve_constant_reference(name, context: namespace.ascend.to_a) if constant stdout.puts " => #{constant.name}: #{constant.type}" diff --git a/lib/rbs/constant_table.rb b/lib/rbs/constant_table.rb index 55f17d6e3..71583dd90 100644 --- a/lib/rbs/constant_table.rb +++ b/lib/rbs/constant_table.rb @@ -30,17 +30,18 @@ def split_name(name) end def resolve_constant_reference(name, context:) + raise "Context cannot be empty: Specify `[Namespace.root]`" if context.empty? + head, *tail = split_name(name) head_constant = case when name.absolute? name_to_constant(TypeName.new(name: head, namespace: Namespace.root)) - when !context || context.empty? + when context == [Namespace.root] name_to_constant(TypeName.new(name: head, namespace: Namespace.root)) else resolve_constant_reference_context(head, context: context) || - resolve_constant_reference_inherit(head, - scopes: constant_scopes(context.to_type_name)) + resolve_constant_reference_inherit(head, scopes: constant_scopes(context.first.to_type_name)) end if head_constant @@ -53,11 +54,11 @@ def resolve_constant_reference(name, context:) end def resolve_constant_reference_context(name, context:) - if context.empty? - nil - else - name_to_constant(TypeName.new(name: name, namespace: context)) || - resolve_constant_reference_context(name, context: context.parent) + head, *tail = context + + if head + name_to_constant(TypeName.new(name: name, namespace: head)) || + resolve_constant_reference_context(name, context: tail) end end @@ -125,7 +126,7 @@ def constant_scopes0(name, scopes: []) scopes.unshift namespace else - raise "Unexpected declaration: #{name}" + raise "Unexpected declaration: #{name} (#{decl.class})" end env.each_extension(name).sort_by {|e| e.extension_name.to_s }.each do |extension| diff --git a/lib/rbs/namespace.rb b/lib/rbs/namespace.rb index 8277b6d48..d21ea22aa 100644 --- a/lib/rbs/namespace.rb +++ b/lib/rbs/namespace.rb @@ -87,5 +87,23 @@ def self.parse(string) new(path: string.split("::").map(&:to_sym), absolute: false) end end + + + def ascend + if block_given? + current = self + + until current.empty? + yield current + current = current.parent + end + + yield current + + self + else + enum_for(:ascend) + end + end end end diff --git a/test/rbs/constant_table_test.rb b/test/rbs/constant_table_test.rb index 01e554dd3..2c71316a0 100644 --- a/test/rbs/constant_table_test.rb +++ b/test/rbs/constant_table_test.rb @@ -43,13 +43,19 @@ def test_reference_top_level builder = DefinitionBuilder.new(env: env) table = ConstantTable.new(builder: builder) - table.resolve_constant_reference(TypeName.new(name: :Name, namespace: Namespace.empty), context: nil).tap do |constant| + table.resolve_constant_reference( + TypeName.new(name: :Name, namespace: Namespace.empty), + context: [Namespace.root] + ).tap do |constant| assert_instance_of Constant, constant assert_equal "::Name", constant.name.to_s assert_equal "::String", constant.type.to_s end - table.resolve_constant_reference(TypeName.new(name: :ABC, namespace: Namespace.empty), context: nil).tap do |constant| + table.resolve_constant_reference( + TypeName.new(name: :ABC, namespace: Namespace.empty), + context: [Namespace.root] + ).tap do |constant| assert_nil constant end end @@ -68,15 +74,21 @@ class Foo manager.build do |env| builder = DefinitionBuilder.new(env: env) table = ConstantTable.new(builder: builder) - context = Namespace.parse("::Foo") + namespace = Namespace.parse("::Foo") - table.resolve_constant_reference(TypeName.new(name: :Name, namespace: Namespace.empty), context: context).tap do |constant| + table.resolve_constant_reference( + TypeName.new(name: :Name, namespace: Namespace.empty), + context: namespace.ascend.to_a + ).tap do |constant| assert_instance_of Constant, constant assert_equal "::Foo::Name", constant.name.to_s assert_equal '"Foo::Name"', constant.type.to_s end - table.resolve_constant_reference(TypeName.new(name: :Name, namespace: Namespace.root), context: context).tap do |constant| + table.resolve_constant_reference( + TypeName.new(name: :Name, namespace: Namespace.root), + context: namespace.ascend.to_a + ).tap do |constant| assert_instance_of Constant, constant assert_equal "::Name", constant.name.to_s assert_equal '"::Name"', constant.type.to_s @@ -85,6 +97,37 @@ class Foo end end + def test_reference_constant_nested_context + SignatureManager.new do |manager| + manager.files[Pathname("foo.rbs")] = <