Skip to content

Commit 3ad4466

Browse files
committed
Skip RBS rewrite when no markers are present
1 parent a50b1ef commit 3ad4466

2 files changed

Lines changed: 187 additions & 7 deletions

File tree

lib/tapioca/rbs/rewriter.rb

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,50 @@ def load_iseq(path)
3636
# Bootsnap is not in the bundle, we don't need to do anything.
3737
end
3838

39+
module Tapioca
40+
module RBS
41+
module Rewriter
42+
TYPED_FILE_PATTERN = /^\s*#\s*typed: (ignore|false|true|strict|strong|__STDLIB_INTERNAL)/
43+
RBS_ANNOTATION_MARKERS = [
44+
"# @abstract",
45+
"# @interface",
46+
"# @sealed",
47+
"# @final",
48+
"# @requires_ancestor:",
49+
"# @override",
50+
"# @overridable",
51+
"# @without_runtime",
52+
].freeze #: Array[String]
53+
54+
class << self
55+
#: (String source) -> bool
56+
def typed_file?(source)
57+
source.match?(TYPED_FILE_PATTERN)
58+
end
59+
60+
#: (String source) -> bool
61+
def possible_rbs_runtime_rewrite_syntax?(source)
62+
return true if source.include?("#:") || source.include?("#|")
63+
return false unless source.include?("# @")
64+
65+
RBS_ANNOTATION_MARKERS.any? { |marker| source.include?(marker) }
66+
end
67+
68+
#: (untyped path, String source) -> String?
69+
def rewrite(path, source)
70+
return unless typed_file?(source)
71+
return source unless possible_rbs_runtime_rewrite_syntax?(source)
72+
73+
Spoom::Sorbet::Translate.rbs_comments_to_sorbet_sigs(source, file: path)
74+
rescue Spoom::Sorbet::Translate::Error
75+
# If we can't translate the RBS comments back into Sorbet's signatures, we just skip the file.
76+
source
77+
end
78+
end
79+
end
80+
end
81+
end
82+
3983
# We need to include `T::Sig` very early to make sure that the `sig` method is available since gems using RBS comments
4084
# are unlikely to include `T::Sig` in their own classes.
4185
Module.include(T::Sig)
@@ -45,11 +89,5 @@ def load_iseq(path)
4589
# The source is most likely nil since no `source_transform` hook was triggered before this one.
4690
source ||= File.read(path, encoding: "UTF-8")
4791

48-
# For performance reasons, we only rewrite files that use Sorbet.
49-
if source =~ /^\s*#\s*typed: (ignore|false|true|strict|strong|__STDLIB_INTERNAL)/
50-
Spoom::Sorbet::Translate.rbs_comments_to_sorbet_sigs(source, file: path)
51-
end
52-
rescue Spoom::Sorbet::Translate::Error
53-
# If we can't translate the RBS comments back into Sorbet's signatures, we just skip the file.
54-
source
92+
Tapioca::RBS::Rewriter.rewrite(path, source)
5593
end

spec/tapioca/rbs/rewriter_spec.rb

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
# typed: strict
2+
# frozen_string_literal: true
3+
4+
require "spec_helper"
5+
6+
module Tapioca
7+
module RBS
8+
class RewriterSpec < Minitest::Spec
9+
describe ".typed_file?" do
10+
it "returns true for supported typed sigils" do
11+
[
12+
"# typed: ignore",
13+
"# typed: false",
14+
"# typed: true",
15+
"# typed: strict",
16+
"# typed: strong",
17+
"# typed: __STDLIB_INTERNAL",
18+
].each do |sigil|
19+
assert(Tapioca::RBS::Rewriter.typed_file?(sigil))
20+
end
21+
end
22+
23+
it "returns false when the file is not typed" do
24+
refute(Tapioca::RBS::Rewriter.typed_file?("class Foo; end"))
25+
end
26+
end
27+
28+
describe ".rewrite" do
29+
it "does not call the translator for typed files without RBS runtime rewrite syntax" do
30+
source = <<~RUBY
31+
# typed: true
32+
33+
class Foo
34+
def foo; end
35+
end
36+
RUBY
37+
38+
Spoom::Sorbet::Translate.stub(:rbs_comments_to_sorbet_sigs, ->(*) { flunk("translator should not run") }) do
39+
assert_equal(source, Tapioca::RBS::Rewriter.rewrite("foo.rb", source))
40+
end
41+
end
42+
43+
it "returns nil for untyped files" do
44+
assert_nil(Tapioca::RBS::Rewriter.rewrite("foo.rb", "class Foo; end"))
45+
end
46+
47+
it "calls the translator for typed files with RBS runtime rewrite syntax" do
48+
source = <<~RUBY
49+
# typed: true
50+
51+
class Foo
52+
#: -> String
53+
def foo; end
54+
end
55+
RUBY
56+
57+
Spoom::Sorbet::Translate.stub(:rbs_comments_to_sorbet_sigs, ->(rewritten_source, file:) {
58+
assert_equal(source, rewritten_source)
59+
assert_equal("foo.rb", file)
60+
"translated"
61+
}) do
62+
assert_equal("translated", Tapioca::RBS::Rewriter.rewrite("foo.rb", source))
63+
end
64+
end
65+
66+
it "returns the original source when translation fails" do
67+
source = <<~RUBY
68+
# typed: true
69+
70+
class Foo
71+
#: invalid
72+
def foo; end
73+
end
74+
RUBY
75+
76+
Spoom::Sorbet::Translate.stub(:rbs_comments_to_sorbet_sigs, ->(*) {
77+
raise Spoom::Sorbet::Translate::Error, "invalid"
78+
}) do
79+
assert_equal(source, Tapioca::RBS::Rewriter.rewrite("foo.rb", source))
80+
end
81+
end
82+
end
83+
84+
describe ".possible_rbs_runtime_rewrite_syntax?" do
85+
it "returns true for RBS signature comments" do
86+
assert(Tapioca::RBS::Rewriter.possible_rbs_runtime_rewrite_syntax?(<<~RUBY))
87+
# typed: true
88+
89+
class Foo
90+
#: -> String
91+
def foo; end
92+
end
93+
RUBY
94+
end
95+
96+
it "returns true for multiline RBS signature continuation comments" do
97+
assert(Tapioca::RBS::Rewriter.possible_rbs_runtime_rewrite_syntax?(<<~RUBY))
98+
# typed: true
99+
100+
class Foo
101+
#: -> Array[
102+
#| String
103+
#| ]
104+
def foo; end
105+
end
106+
RUBY
107+
end
108+
109+
it "returns true for supported RBS annotations" do
110+
Tapioca::RBS::Rewriter::RBS_ANNOTATION_MARKERS.each do |marker|
111+
assert(Tapioca::RBS::Rewriter.possible_rbs_runtime_rewrite_syntax?(<<~RUBY), marker)
112+
# typed: true
113+
114+
#{marker}
115+
class Foo; end
116+
RUBY
117+
end
118+
end
119+
120+
it "returns false for typed files without RBS runtime rewrite syntax" do
121+
refute(Tapioca::RBS::Rewriter.possible_rbs_runtime_rewrite_syntax?(<<~RUBY))
122+
# typed: true
123+
124+
class Foo
125+
def foo; end
126+
end
127+
RUBY
128+
end
129+
130+
it "returns false for unrelated YARD tags" do
131+
refute(Tapioca::RBS::Rewriter.possible_rbs_runtime_rewrite_syntax?(<<~RUBY))
132+
# typed: true
133+
134+
# @param value [String]
135+
# @return [String]
136+
def foo(value); end
137+
RUBY
138+
end
139+
end
140+
end
141+
end
142+
end

0 commit comments

Comments
 (0)