Skip to content

Commit ae559a7

Browse files
committed
Rewrite attr_* into def methods
1 parent 799ce4f commit ae559a7

File tree

4 files changed

+484
-1
lines changed

4 files changed

+484
-1
lines changed

lib/rbi.rb

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
require "stringio"
66

77
module RBI
8-
class Error < StandardError; end
8+
class Error < StandardError
9+
extend T::Sig
10+
end
911
end
1012

1113
require "rbi/loc"
@@ -21,6 +23,7 @@ class Error < StandardError; end
2123
require "rbi/rewriters/nest_non_public_methods"
2224
require "rbi/rewriters/group_nodes"
2325
require "rbi/rewriters/remove_known_definitions"
26+
require "rbi/rewriters/attr_to_methods"
2427
require "rbi/rewriters/sort_nodes"
2528
require "rbi/parser"
2629
require "rbi/printer"

lib/rbi/model.rb

+3
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,9 @@ def initialize(name, names, visibility: Public.new, sigs: [], loc: nil, comments
382382

383383
sig { abstract.returns(T::Array[String]) }
384384
def fully_qualified_names; end
385+
386+
sig { abstract.returns(T::Array[Method]) }
387+
def convert_to_methods; end
385388
end
386389

387390
class AttrAccessor < Attr

lib/rbi/rewriters/attr_to_methods.rb

+166
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# typed: strict
2+
# frozen_string_literal: true
3+
4+
module RBI
5+
class UnexpectedMultipleSigsError < Error
6+
attr_reader :node
7+
8+
sig { params(node: Node).void }
9+
def initialize(node)
10+
super(<<~MSG)
11+
This declaration cannot have more than one sig.
12+
13+
#{node.string.chomp}
14+
MSG
15+
16+
@node = node
17+
end
18+
end
19+
20+
module Rewriters
21+
class AttrToMethods < Visitor
22+
extend T::Sig
23+
24+
sig { override.params(node: T.nilable(Node)).void }
25+
def visit(node)
26+
case node
27+
when Tree
28+
visit_all(node.nodes.dup)
29+
30+
when Attr
31+
new_methods = node.convert_to_methods
32+
replace(node, with: new_methods)
33+
end
34+
end
35+
36+
private
37+
38+
sig { params(node: Node, with: T::Array[Node]).void }
39+
def replace(node, with:)
40+
tree = node.parent_tree
41+
raise ReplaceNodeError, "Can't replace #{self} without a parent tree" unless tree
42+
43+
node.detach
44+
with.each { |node| tree << node }
45+
end
46+
end
47+
end
48+
49+
class Tree
50+
extend T::Sig
51+
52+
sig { void }
53+
def replace_attributes_with_methods!
54+
visitor = Rewriters::AttrToMethods.new
55+
visitor.visit(self)
56+
end
57+
end
58+
59+
class Attr
60+
private
61+
62+
sig(:final) { returns([T.nilable(Sig), T.nilable(String)]) }
63+
def parse_sig
64+
raise UnexpectedMultipleSigsError.new(self) if 1 < sigs.count
65+
66+
sig = sigs.first
67+
return [nil, nil] unless sig
68+
69+
attribute_type = case self
70+
when AttrReader, AttrAccessor then sig.return_type
71+
when AttrWriter then sig.params.first&.type
72+
end
73+
74+
[sig, attribute_type]
75+
end
76+
77+
sig do
78+
params(
79+
name: String,
80+
sig: T.nilable(Sig),
81+
visibility: Visibility,
82+
loc: T.nilable(Loc),
83+
comments: T::Array[Comment],
84+
).returns(Method)
85+
end
86+
def create_getter_method(name, sig, visibility, loc, comments)
87+
Method.new(
88+
name,
89+
params: [],
90+
visibility: visibility,
91+
sigs: sig ? [sig] : [],
92+
loc: loc,
93+
comments: comments,
94+
)
95+
end
96+
97+
sig do
98+
params(
99+
name: String,
100+
sig: T.nilable(Sig),
101+
attribute_type: T.nilable(String),
102+
visibility: Visibility,
103+
loc: T.nilable(Loc),
104+
comments: T::Array[Comment],
105+
).returns(Method)
106+
end
107+
def create_setter_method(name, sig, attribute_type, visibility, loc, comments) # rubocop:disable Metrics/ParameterLists
108+
sig = if sig # Modify the original sig to correct the name, and remove the return type
109+
params = attribute_type ? [SigParam.new(name, attribute_type)] : []
110+
111+
Sig.new(
112+
params: params,
113+
return_type: "void",
114+
is_abstract: sig.is_abstract,
115+
is_override: sig.is_override,
116+
is_overridable: sig.is_overridable,
117+
is_final: sig.is_final,
118+
type_params: sig.type_params,
119+
checked: sig.checked,
120+
loc: sig.loc,
121+
)
122+
end
123+
124+
Method.new(
125+
"#{name}=",
126+
params: [ReqParam.new(name)],
127+
visibility: visibility,
128+
sigs: sig ? [sig] : [],
129+
loc: loc,
130+
comments: comments,
131+
)
132+
end
133+
end
134+
135+
class AttrAccessor
136+
sig { override.returns(T::Array[Method]) }
137+
def convert_to_methods
138+
sig, attribute_type = parse_sig
139+
140+
names.flat_map do |name|
141+
[
142+
create_getter_method(name.to_s, sig, visibility, loc, comments),
143+
create_setter_method(name.to_s, sig, attribute_type, visibility, loc, comments),
144+
]
145+
end
146+
end
147+
end
148+
149+
class AttrReader
150+
sig { override.returns(T::Array[Method]) }
151+
def convert_to_methods
152+
sig, _ = parse_sig
153+
154+
names.map { |name| create_getter_method(name.to_s, sig, visibility, loc, comments) }
155+
end
156+
end
157+
158+
class AttrWriter
159+
sig { override.returns(T::Array[Method]) }
160+
def convert_to_methods
161+
sig, attribute_type = parse_sig
162+
163+
names.map { |name| create_setter_method(name.to_s, sig, attribute_type, visibility, loc, comments) }
164+
end
165+
end
166+
end

0 commit comments

Comments
 (0)