From 73aee600c5a4558b2a5951c7f3232cf5753400f3 Mon Sep 17 00:00:00 2001 From: Jeff Carbonella Date: Wed, 1 Dec 2021 12:33:23 -0700 Subject: [PATCH 1/3] Add flatten_scopes rewriter --- lib/rbi.rb | 1 + lib/rbi/rewriters/flatten_scopes.rb | 44 ++++++++++ test/rbi/rewriters/flatten_scopes_test.rb | 102 ++++++++++++++++++++++ 3 files changed, 147 insertions(+) create mode 100644 lib/rbi/rewriters/flatten_scopes.rb create mode 100644 test/rbi/rewriters/flatten_scopes_test.rb diff --git a/lib/rbi.rb b/lib/rbi.rb index cb32d3aa..87426c10 100644 --- a/lib/rbi.rb +++ b/lib/rbi.rb @@ -15,6 +15,7 @@ class Error < StandardError; end require "rbi/rewriters/add_sig_templates" require "rbi/rewriters/annotate" require "rbi/rewriters/deannotate" +require "rbi/rewriters/flatten_scopes" require "rbi/rewriters/merge_trees" require "rbi/rewriters/nest_singleton_methods" require "rbi/rewriters/nest_non_public_methods" diff --git a/lib/rbi/rewriters/flatten_scopes.rb b/lib/rbi/rewriters/flatten_scopes.rb new file mode 100644 index 00000000..43ace55e --- /dev/null +++ b/lib/rbi/rewriters/flatten_scopes.rb @@ -0,0 +1,44 @@ +# typed: strict +# frozen_string_literal: true + +module RBI + module Rewriters + class FlattenScopes < Visitor + extend T::Sig + + sig { override.params(node: T.nilable(Node)).void } + def visit(node) + return unless node + + case node + when Tree + visit_all(node.nodes) + + parent_tree = node.parent_tree + return unless parent_tree + + node.nodes.dup.each do |child| + next unless child.is_a?(Class) || child.is_a?(Module) + + parent_scope = child.parent_scope + next unless parent_scope.is_a?(Class) || parent_scope.is_a?(Module) + + child.detach + child.name = "#{parent_scope.name}::#{child.name}" + parent_tree << child + end + end + end + end + end + + class Tree + extend T::Sig + + sig { void } + def flatten_scopes! + visitor = Rewriters::FlattenScopes.new + visitor.visit(self) + end + end +end diff --git a/test/rbi/rewriters/flatten_scopes_test.rb b/test/rbi/rewriters/flatten_scopes_test.rb new file mode 100644 index 00000000..f1cf2e48 --- /dev/null +++ b/test/rbi/rewriters/flatten_scopes_test.rb @@ -0,0 +1,102 @@ +# typed: true +# frozen_string_literal: true + +require "test_helper" + +module RBI + class FlattenScopesTest < Minitest::Test + def test_flatten_scopes_with_empty_scopes + rbi = RBI::Tree.new + scope1 = RBI::Module.new("A") + scope2 = RBI::Class.new("B") + scope3 = RBI::Class.new("C") + scope4 = RBI::Module.new("D") + scope5 = RBI::Module.new("E") + scope3 << scope4 + scope2 << scope3 + scope1 << scope2 + rbi << scope1 + rbi << scope5 + + rbi.flatten_scopes! + + assert_equal(<<~RBI, rbi.string) + module A; end + module E; end + class A::B; end + class A::B::C; end + module A::B::C::D; end + RBI + end + + def test_flatten_scopes_with_nonempty_scopes + rbi = RBI::Tree.new + scope1 = RBI::Module.new("A") + scope1 << RBI::Const.new("A1", "42") + scope2 = RBI::Class.new("B") + scope3 = RBI::Class.new("C") + scope3 << RBI::Const.new("C1", "42") + scope4 = RBI::Module.new("D") + scope5 = RBI::Module.new("E") + scope5 << RBI::Const.new("E1", "42") + scope3 << scope4 + scope2 << scope3 + scope1 << scope2 + rbi << scope1 + rbi << scope5 + + rbi.flatten_scopes! + + assert_equal(<<~RBI, rbi.string) + module A + A1 = 42 + end + + module E + E1 = 42 + end + + class A::B; end + + class A::B::C + C1 = 42 + end + + module A::B::C::D; end + RBI + end + + def test_flatten_scopes_with_singleton_classes + rbi = RBI::Tree.new + scope1 = RBI::Module.new("A") + scope2 = RBI::Class.new("B") + scope3 = RBI::Class.new("C") + scope3_singleton = RBI::SingletonClass.new + scope3_singleton << RBI::Method.new("m1") + scope4 = RBI::Module.new("D") + scope5 = RBI::Module.new("E") + scope3 << scope4 + scope3 << scope3_singleton + scope2 << scope3 + scope1 << scope2 + rbi << scope1 + rbi << scope5 + + rbi.flatten_scopes! + + assert_equal(<<~RBI, rbi.string) + module A; end + module E; end + class A::B; end + + class A::B::C + class << self + def m1; end + end + end + + module A::B::C::D; end + RBI + end + end +end From f9949c048f98a318394a079c41dc6f00cd7ade65 Mon Sep 17 00:00:00 2001 From: Jeff Carbonella Date: Wed, 1 Dec 2021 20:35:06 -0700 Subject: [PATCH 2/3] Use RBI::Parser.parse_string for test setup --- test/rbi/rewriters/flatten_scopes_test.rb | 82 ++++++++++++----------- 1 file changed, 43 insertions(+), 39 deletions(-) diff --git a/test/rbi/rewriters/flatten_scopes_test.rb b/test/rbi/rewriters/flatten_scopes_test.rb index f1cf2e48..c5ef22ba 100644 --- a/test/rbi/rewriters/flatten_scopes_test.rb +++ b/test/rbi/rewriters/flatten_scopes_test.rb @@ -6,17 +6,17 @@ module RBI class FlattenScopesTest < Minitest::Test def test_flatten_scopes_with_empty_scopes - rbi = RBI::Tree.new - scope1 = RBI::Module.new("A") - scope2 = RBI::Class.new("B") - scope3 = RBI::Class.new("C") - scope4 = RBI::Module.new("D") - scope5 = RBI::Module.new("E") - scope3 << scope4 - scope2 << scope3 - scope1 << scope2 - rbi << scope1 - rbi << scope5 + rbi = RBI::Parser.parse_string(<<~RBI) + module A + class B + class C + module D; end + end + end + end + + module E; end + RBI rbi.flatten_scopes! @@ -30,20 +30,23 @@ module A::B::C::D; end end def test_flatten_scopes_with_nonempty_scopes - rbi = RBI::Tree.new - scope1 = RBI::Module.new("A") - scope1 << RBI::Const.new("A1", "42") - scope2 = RBI::Class.new("B") - scope3 = RBI::Class.new("C") - scope3 << RBI::Const.new("C1", "42") - scope4 = RBI::Module.new("D") - scope5 = RBI::Module.new("E") - scope5 << RBI::Const.new("E1", "42") - scope3 << scope4 - scope2 << scope3 - scope1 << scope2 - rbi << scope1 - rbi << scope5 + rbi = RBI::Parser.parse_string(<<~RBI) + module A + A1 = 42 + + class B + class C + C1 = 42 + + module D; end + end + end + end + + module E + E1 = 42 + end + RBI rbi.flatten_scopes! @@ -67,20 +70,21 @@ module A::B::C::D; end end def test_flatten_scopes_with_singleton_classes - rbi = RBI::Tree.new - scope1 = RBI::Module.new("A") - scope2 = RBI::Class.new("B") - scope3 = RBI::Class.new("C") - scope3_singleton = RBI::SingletonClass.new - scope3_singleton << RBI::Method.new("m1") - scope4 = RBI::Module.new("D") - scope5 = RBI::Module.new("E") - scope3 << scope4 - scope3 << scope3_singleton - scope2 << scope3 - scope1 << scope2 - rbi << scope1 - rbi << scope5 + rbi = RBI::Parser.parse_string(<<~RBI) + module A + class B + class C + module D; end + + class << self + def m1; end + end + end + end + end + + module E; end + RBI rbi.flatten_scopes! From 9598683b0715017429a366c21e3c6ee52eccb040 Mon Sep 17 00:00:00 2001 From: Jeff Carbonella Date: Wed, 1 Dec 2021 20:46:21 -0700 Subject: [PATCH 3/3] Add tests for T::Struct and T::Enum --- test/rbi/rewriters/flatten_scopes_test.rb | 65 +++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/test/rbi/rewriters/flatten_scopes_test.rb b/test/rbi/rewriters/flatten_scopes_test.rb index c5ef22ba..14ed4a81 100644 --- a/test/rbi/rewriters/flatten_scopes_test.rb +++ b/test/rbi/rewriters/flatten_scopes_test.rb @@ -102,5 +102,70 @@ def m1; end module A::B::C::D; end RBI end + + def test_flatten_scopes_with_t_struct + rbi = RBI::Parser.parse_string(<<~RBI) + module A + class B < T::Struct + const :foo, String + + class C + module D; end + end + end + end + + module E; end + RBI + + rbi.flatten_scopes! + + assert_equal(<<~RBI, rbi.string) + module A; end + module E; end + + class A::B < T::Struct + const :foo, String + end + + class A::B::C; end + module A::B::C::D; end + RBI + end + + def test_flatten_scopes_with_t_enum + rbi = RBI::Parser.parse_string(<<~RBI) + module A + class B + class C + class D < T::Enum + enums do + Foo = new + Bar = new + end + end + end + end + end + + module E; end + RBI + + rbi.flatten_scopes! + + assert_equal(<<~RBI, rbi.string) + module A; end + module E; end + class A::B; end + class A::B::C; end + + class A::B::C::D < T::Enum + enums do + Foo = new + Bar = new + end + end + RBI + end end end