Skip to content

Commit

Permalink
Fix constant resolution
Browse files Browse the repository at this point in the history
  • Loading branch information
soutaro committed May 20, 2020
1 parent d336bb0 commit 0da69ee
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 24 deletions.
2 changes: 1 addition & 1 deletion lib/rbs/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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}"
Expand Down
19 changes: 10 additions & 9 deletions lib/rbs/constant_table.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -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|
Expand Down
18 changes: 18 additions & 0 deletions lib/rbs/namespace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
98 changes: 84 additions & 14 deletions test/rbs/constant_table_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -85,6 +97,37 @@ class Foo
end
end

def test_reference_constant_nested_context
SignatureManager.new do |manager|
manager.files[Pathname("foo.rbs")] = <<EOF
class Foo
end
class Foo::Bar
end
class Foo::Bar::Baz
end
Foo::Bar::X: "Foo::Bar::X"
X: "::X"
EOF
manager.build do |env|
builder = DefinitionBuilder.new(env: env)
table = ConstantTable.new(builder: builder)

table.resolve_constant_reference(
TypeName.new(name: :X, namespace: Namespace.empty),
context: [Namespace.parse("::Foo"), Namespace.parse("::Foo::Bar::Baz")]
).tap do |constant|
assert_instance_of Constant, constant
assert_equal "::X", constant.name.to_s
assert_equal '"::X"', constant.type.to_s
end
end
end
end

def test_reference_constant_inherit
SignatureManager.new do |manager|
manager.files[Pathname("foo.rbs")] = <<EOF
Expand All @@ -106,13 +149,19 @@ module Mix
builder = DefinitionBuilder.new(env: env)
table = ConstantTable.new(builder: builder)

table.resolve_constant_reference(TypeName.new(name: :MAX, namespace: Namespace.empty), context: Namespace.parse("::Child")).tap do |constant|
table.resolve_constant_reference(
TypeName.new(name: :MAX, namespace: Namespace.empty),
context: Namespace.parse("::Child").ascend.to_a
).tap do |constant|
assert_instance_of Constant, constant
assert_equal "::Parent::MAX", constant.name.to_s
assert_equal "10000", constant.type.to_s
end

table.resolve_constant_reference(TypeName.new(name: :MIN, namespace: Namespace.empty), context: Namespace.parse("::Child")).tap do |constant|
table.resolve_constant_reference(
TypeName.new(name: :MIN, namespace: Namespace.empty),
context: Namespace.parse("::Child").ascend.to_a
).tap do |constant|
assert_instance_of Constant, constant
assert_equal "::Mix::MIN", constant.name.to_s
assert_equal '0', constant.type.to_s
Expand All @@ -139,13 +188,19 @@ module Foo::Bar
builder = DefinitionBuilder.new(env: env)
table = ConstantTable.new(builder: builder)

table.resolve_constant_reference(TypeName.new(name: :Set, namespace: Namespace.empty), context: Namespace.parse("::Foo::Bar")).tap do |constant|
table.resolve_constant_reference(
TypeName.new(name: :Set, namespace: Namespace.empty),
context: Namespace.parse("::Foo::Bar").ascend.to_a
).tap do |constant|
assert_instance_of Constant, constant
assert_equal "::Set", constant.name.to_s
assert_equal 'singleton(::Set)', constant.type.to_s
end

table.resolve_constant_reference(TypeName.new(name: :X, namespace: Namespace.empty), context: Namespace.parse("::Foo::Bar")).tap do |constant|
table.resolve_constant_reference(
TypeName.new(name: :X, namespace: Namespace.empty),
context: Namespace.parse("::Foo::Bar").ascend.to_a
).tap do |constant|
assert_instance_of Constant, constant
assert_equal "::Baz::X", constant.name.to_s
assert_equal '::Integer', constant.type.to_s
Expand All @@ -166,7 +221,10 @@ class Foo
builder = DefinitionBuilder.new(env: env)
table = ConstantTable.new(builder: builder)

table.resolve_constant_reference(TypeName.new(name: :Name, namespace: Namespace.parse("Foo")), context: Namespace.parse("::Foo")).tap do |constant|
table.resolve_constant_reference(
TypeName.new(name: :Name, namespace: Namespace.parse("Foo")),
context: Namespace.parse("::Foo").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
Expand All @@ -190,21 +248,33 @@ class Stuff
builder = DefinitionBuilder.new(env: env)
table = ConstantTable.new(builder: builder)

table.resolve_constant_reference(TypeName.new(name: :ONE, namespace: Namespace.parse("Stuff")), context: nil).tap do |constant|
table.resolve_constant_reference(
TypeName.new(name: :ONE, namespace: Namespace.parse("Stuff")),
context: [Namespace.root]
).tap do |constant|
assert_nil constant
end

table.resolve_constant_reference(TypeName.new(name: :TWO, namespace: Namespace.parse("Stuff")), context: nil).tap do |constant|
table.resolve_constant_reference(
TypeName.new(name: :TWO, namespace: Namespace.parse("Stuff")),
context: [Namespace.root]
).tap do |constant|
assert_nil constant
end

table.resolve_constant_reference(TypeName.new(name: :THREE, namespace: Namespace.parse("Stuff")), context: nil).tap do |constant|
table.resolve_constant_reference(
TypeName.new(name: :THREE, namespace: Namespace.parse("Stuff")),
context: [Namespace.root]
).tap do |constant|
assert_instance_of Constant, constant
assert_equal "::Kernel::THREE", constant.name.to_s
assert_equal "3", constant.type.to_s
end

table.resolve_constant_reference(TypeName.new(name: :FOUR, namespace: Namespace.parse("Stuff")), context: nil).tap do |constant|
table.resolve_constant_reference(
TypeName.new(name: :FOUR, namespace: Namespace.parse("Stuff")),
context: [Namespace.root]
).tap do |constant|
assert_instance_of Constant, constant
assert_equal "::BasicObject::FOUR", constant.name.to_s
assert_equal "4", constant.type.to_s
Expand Down

0 comments on commit 0da69ee

Please sign in to comment.