Skip to content

Commit e572c2c

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

3 files changed

Lines changed: 484 additions & 1 deletion

File tree

lib/rbi.rb

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

0 commit comments

Comments
 (0)