Skip to content

Commit 01afaa8

Browse files
authored
Merge pull request #2935 from RYOTA-KOBA/support-singleton-method-def-in-inline-parser
Support `def self.method_name` singleton method in inline parser
2 parents 4bb4367 + dca8db2 commit 01afaa8

6 files changed

Lines changed: 217 additions & 15 deletions

File tree

lib/rbs/ast/ruby/members.rb

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -549,17 +549,27 @@ class DefMember < Base
549549

550550
attr_reader :name
551551
attr_reader :node
552+
attr_reader :kind
552553
attr_reader :method_type
553554
attr_reader :leading_comment
554555

555-
def initialize(buffer, name, node, method_type, leading_comment)
556+
def initialize(buffer, name, node, method_type, leading_comment, kind: :instance)
556557
super(buffer)
557558
@name = name
558559
@node = node
560+
@kind = kind
559561
@method_type = method_type
560562
@leading_comment = leading_comment
561563
end
562564

565+
def singleton?
566+
kind == :singleton
567+
end
568+
569+
def instance?
570+
kind == :instance
571+
end
572+
563573
def location
564574
rbs_location(node.location)
565575
end
@@ -583,6 +593,7 @@ def name_location
583593
def type_fingerprint
584594
[
585595
"members/def",
596+
kind.to_s,
586597
name.to_s,
587598
method_type.type_fingerprint,
588599
leading_comment&.as_comment&.string

lib/rbs/definition_builder/method_builder.rb

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -143,12 +143,14 @@ def build_instance(type_name)
143143
decl.members.each do |member|
144144
case member
145145
when AST::Ruby::Members::DefMember
146-
build_method(
147-
methods,
148-
type,
149-
member: member,
150-
accessibility: :public
151-
)
146+
if member.instance?
147+
build_method(
148+
methods,
149+
type,
150+
member: member,
151+
accessibility: :public
152+
)
153+
end
152154
when AST::Ruby::Members::AttrReaderMember, AST::Ruby::Members::AttrWriterMember, AST::Ruby::Members::AttrAccessorMember
153155
build_ruby_attribute(methods, type, member: member, accessibility: :public)
154156
end
@@ -181,6 +183,10 @@ def build_singleton(type_name)
181183
if member.kind == :singleton
182184
build_alias(methods, type, member: member)
183185
end
186+
when AST::Ruby::Members::DefMember
187+
if member.singleton?
188+
build_method(methods, type, member: member, accessibility: :public)
189+
end
184190
end
185191
end
186192
end

lib/rbs/environment.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -795,7 +795,8 @@ def resolve_ruby_member(resolver, member, context:)
795795
member.name,
796796
member.node,
797797
member.method_type.map_type_name {|name, _, _| absolute_type_name(resolver, nil, name, context: context) },
798-
member.leading_comment
798+
member.leading_comment,
799+
kind: member.kind
799800
)
800801
when AST::Ruby::Members::IncludeMember
801802
resolved_annotation = member.annotation&.map_type_name {|name, _, _| absolute_type_name(resolver, nil, name, context: context) }

lib/rbs/inline_parser.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -202,14 +202,16 @@ def visit_class_or_module_body(decl, node)
202202
def visit_def_node(node)
203203
return if skip_node?(node)
204204

205-
if node.receiver
205+
if node.receiver && !node.receiver.is_a?(Prism::SelfNode)
206206
diagnostics << Diagnostic::NotImplementedYet.new(
207207
rbs_location(node.receiver.location),
208-
"Singleton method definition is not supported yet"
208+
"Method definition with non-self receiver is not supported"
209209
)
210210
return
211211
end
212212

213+
kind = node.receiver ? :singleton : :instance #: :singleton | :instance
214+
213215
case current = current_module
214216
when AST::Ruby::Declarations::ClassDecl, AST::Ruby::Declarations::ModuleDecl
215217
leading_block = comments.leading_block!(node)
@@ -223,7 +225,7 @@ def visit_def_node(node)
223225
method_type, leading_unuseds, trailing_unused = AST::Ruby::Members::MethodTypeAnnotation.build(leading_block, trailing_block, [], node)
224226
report_unused_annotation(trailing_unused, *leading_unuseds)
225227

226-
defn = AST::Ruby::Members::DefMember.new(buffer, node.name, node, method_type, leading_block)
228+
defn = AST::Ruby::Members::DefMember.new(buffer, node.name, node, method_type, leading_block, kind: kind)
227229
current.members << defn
228230

229231
# Skip other comments in `def` node

sig/ast/ruby/members.rbs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,15 @@ module RBS
8484

8585
attr_reader name: Symbol
8686
attr_reader node: Prism::DefNode
87+
attr_reader kind: :instance | :singleton
8788
attr_reader method_type: MethodTypeAnnotation
8889
attr_reader leading_comment: CommentBlock?
8990

90-
def initialize: (Buffer, Symbol name, Prism::DefNode node, MethodTypeAnnotation, CommentBlock? leading_comment) -> void
91+
def initialize: (Buffer, Symbol name, Prism::DefNode node, MethodTypeAnnotation, CommentBlock? leading_comment, ?kind: :instance | :singleton) -> void
92+
93+
def singleton?: () -> bool
94+
95+
def instance?: () -> bool
9196

9297
def location: () -> Location
9398

test/rbs/inline_parser_test.rb

Lines changed: 180 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,18 +125,195 @@ def foo; end
125125
assert_empty result.declarations
126126
end
127127

128-
def test_error__def__singleton
128+
def test_parse__def__singleton
129129
result = parse(<<~RUBY)
130130
module Foo
131131
def self.foo; end
132132
end
133133
RUBY
134134

135+
assert_empty result.diagnostics
136+
137+
result.declarations[0].tap do |decl|
138+
decl.members[0].tap do |member|
139+
assert_instance_of RBS::AST::Ruby::Members::DefMember, member
140+
assert_equal :foo, member.name
141+
assert_predicate member, :singleton?
142+
refute_predicate member, :instance?
143+
assert_equal :singleton, member.kind
144+
end
145+
end
146+
end
147+
148+
def test_parse__def__singleton__colon_type
149+
result = parse(<<~RUBY)
150+
class Foo
151+
#: () -> void
152+
def self.hello
153+
end
154+
end
155+
RUBY
156+
157+
assert_empty result.diagnostics
158+
159+
result.declarations[0].tap do |decl|
160+
decl.members[0].tap do |member|
161+
assert_instance_of RBS::AST::Ruby::Members::DefMember, member
162+
assert_equal :hello, member.name
163+
assert_predicate member, :singleton?
164+
assert_equal ["() -> void"], member.overloads.map { _1.method_type.to_s }
165+
end
166+
end
167+
end
168+
169+
def test_parse__def__singleton__rbs_annotation
170+
result = parse(<<~RUBY)
171+
class Foo
172+
# @rbs () -> void
173+
def self.hello
174+
end
175+
end
176+
RUBY
177+
178+
assert_empty result.diagnostics
179+
180+
result.declarations[0].tap do |decl|
181+
decl.members[0].tap do |member|
182+
assert_instance_of RBS::AST::Ruby::Members::DefMember, member
183+
assert_equal :hello, member.name
184+
assert_predicate member, :singleton?
185+
assert_equal ["() -> void"], member.overloads.map { _1.method_type.to_s }
186+
end
187+
end
188+
end
189+
190+
def test_parse__def__singleton__with_params
191+
result = parse(<<~RUBY)
192+
class Foo
193+
# @rbs n: Integer
194+
# @rbs return: Integer
195+
def self.double(n)
196+
n * 2
197+
end
198+
end
199+
RUBY
200+
201+
assert_empty result.diagnostics
202+
203+
result.declarations[0].tap do |decl|
204+
decl.members[0].tap do |member|
205+
assert_instance_of RBS::AST::Ruby::Members::DefMember, member
206+
assert_equal :double, member.name
207+
assert_predicate member, :singleton?
208+
assert_equal ["(Integer n) -> Integer"], member.overloads.map { _1.method_type.to_s }
209+
end
210+
end
211+
end
212+
213+
def test_parse__def__singleton__mixed_with_instance
214+
result = parse(<<~RUBY)
215+
class Foo
216+
#: () -> void
217+
def self.class_method
218+
end
219+
220+
#: () -> String
221+
def instance_method
222+
""
223+
end
224+
end
225+
RUBY
226+
227+
assert_empty result.diagnostics
228+
229+
result.declarations[0].tap do |decl|
230+
assert_equal 2, decl.members.size
231+
232+
decl.members[0].tap do |member|
233+
assert_instance_of RBS::AST::Ruby::Members::DefMember, member
234+
assert_equal :class_method, member.name
235+
assert_predicate member, :singleton?
236+
assert_equal ["() -> void"], member.overloads.map { _1.method_type.to_s }
237+
end
238+
239+
decl.members[1].tap do |member|
240+
assert_instance_of RBS::AST::Ruby::Members::DefMember, member
241+
assert_equal :instance_method, member.name
242+
assert_predicate member, :instance?
243+
assert_equal ["() -> String"], member.overloads.map { _1.method_type.to_s }
244+
end
245+
end
246+
end
247+
248+
def test_parse__def__singleton__skip
249+
result = parse(<<~RUBY)
250+
class Foo
251+
# @rbs skip
252+
def self.skipped_method
253+
end
254+
end
255+
RUBY
256+
257+
assert_empty result.diagnostics
258+
259+
result.declarations[0].tap do |decl|
260+
assert_empty decl.members
261+
end
262+
end
263+
264+
def test_error__def__singleton__non_self_receiver
265+
result = parse(<<~RUBY)
266+
module Foo
267+
def other_obj.foo; end
268+
end
269+
RUBY
270+
135271
assert_equal 1, result.diagnostics.size
136272
assert_any!(result.diagnostics) do |diagnostic|
137273
assert_instance_of RBS::InlineParser::Diagnostic::NotImplementedYet, diagnostic
138-
assert_equal "self", diagnostic.location.source
139-
assert_equal "Singleton method definition is not supported yet", diagnostic.message
274+
assert_equal "other_obj", diagnostic.location.source
275+
assert_equal "Method definition with non-self receiver is not supported", diagnostic.message
276+
end
277+
end
278+
279+
def test_parse__def__singleton__in_module
280+
result = parse(<<~RUBY)
281+
module Foo
282+
#: () -> void
283+
def self.module_method
284+
end
285+
end
286+
RUBY
287+
288+
assert_empty result.diagnostics
289+
290+
result.declarations[0].tap do |decl|
291+
decl.members[0].tap do |member|
292+
assert_instance_of RBS::AST::Ruby::Members::DefMember, member
293+
assert_equal :module_method, member.name
294+
assert_predicate member, :singleton?
295+
assert_equal ["() -> void"], member.overloads.map { _1.method_type.to_s }
296+
end
297+
end
298+
end
299+
300+
def test_parse__def__instance_method_kind
301+
result = parse(<<~RUBY)
302+
class Foo
303+
def instance_method; end
304+
end
305+
RUBY
306+
307+
assert_empty result.diagnostics
308+
309+
result.declarations[0].tap do |decl|
310+
decl.members[0].tap do |member|
311+
assert_instance_of RBS::AST::Ruby::Members::DefMember, member
312+
assert_equal :instance_method, member.name
313+
assert_predicate member, :instance?
314+
refute_predicate member, :singleton?
315+
assert_equal :instance, member.kind
316+
end
140317
end
141318
end
142319

0 commit comments

Comments
 (0)