Skip to content

Commit 33ce0f1

Browse files
committed
Implement inline parser
1 parent c300c7b commit 33ce0f1

File tree

3 files changed

+169
-4
lines changed

3 files changed

+169
-4
lines changed

lib/rbs/ast/ruby/members.rb

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class DocStyle
2424
attr_reader :required_keywords
2525
attr_reader :optional_keywords
2626
attr_accessor :rest_keywords
27+
attr_accessor :block
2728

2829
def initialize
2930
@return_type_annotation = nil
@@ -34,6 +35,7 @@ def initialize
3435
@required_keywords = {}
3536
@optional_keywords = {}
3637
@rest_keywords = nil
38+
@block = nil
3739
end
3840

3941
def self.build(param_type_annotations, return_type_annotation, node)
@@ -42,8 +44,9 @@ def self.build(param_type_annotations, return_type_annotation, node)
4244

4345
splat_annotation = nil #: Annotations::SplatParamTypeAnnotation?
4446
double_splat_annotation = nil #: Annotations::DoubleSplatParamTypeAnnotation?
47+
block_annotation = nil #: Annotations::BlockParamTypeAnnotation?
4548
param_annotations = {} #: Hash[Symbol, Annotations::ParamTypeAnnotation]
46-
unused = [] #: Array[Annotations::ParamTypeAnnotation | Annotations::SplatParamTypeAnnotation | Annotations::DoubleSplatParamTypeAnnotation]
49+
unused = [] #: Array[Annotations::ParamTypeAnnotation | Annotations::SplatParamTypeAnnotation | Annotations::DoubleSplatParamTypeAnnotation | Annotations::BlockParamTypeAnnotation]
4750

4851
param_type_annotations.each do |annot|
4952
case annot
@@ -59,6 +62,12 @@ def self.build(param_type_annotations, return_type_annotation, node)
5962
else
6063
double_splat_annotation = annot
6164
end
65+
when Annotations::BlockParamTypeAnnotation
66+
if block_annotation
67+
unused << annot
68+
else
69+
block_annotation = annot
70+
end
6271
when Annotations::ParamTypeAnnotation
6372
name = annot.name_location.source.to_sym
6473
if param_annotations.key?(name)
@@ -141,11 +150,30 @@ def self.build(param_type_annotations, return_type_annotation, node)
141150
doc.rest_keywords = kw_rest.name || true
142151
end
143152
end
153+
154+
if (blk = params.block) && blk.is_a?(Prism::BlockParameterNode)
155+
if block_annotation && (block_annotation.name_location.nil? || blk.name.nil? || block_annotation.name == blk.name)
156+
doc.block = block_annotation
157+
block_annotation = nil
158+
else
159+
doc.block = blk.name || true
160+
end
161+
end
162+
end
163+
164+
if block_annotation
165+
if node.parameters&.block
166+
# Block parameter exists but name didn't match -- treat as unused
167+
else
168+
doc.block = block_annotation
169+
block_annotation = nil
170+
end
144171
end
145172

146173
unused.concat(param_annotations.values)
147174
unused << splat_annotation if splat_annotation
148175
unused << double_splat_annotation if double_splat_annotation
176+
unused << block_annotation if block_annotation
149177

150178
[doc, unused]
151179
end
@@ -160,6 +188,7 @@ def all_param_annotations
160188
required_keywords.each_value { |a| annotations << a }
161189
optional_keywords.each_value { |a| annotations << a }
162190
annotations << rest_keywords
191+
annotations << block
163192

164193
annotations
165194
end
@@ -231,6 +260,13 @@ def map_type_name(&block)
231260
else
232261
rest_keywords
233262
end
263+
new.block =
264+
case self.block
265+
when Annotations::BlockParamTypeAnnotation
266+
self.block.map_type_name(&block)
267+
else
268+
self.block
269+
end
234270
end #: self
235271
end
236272

@@ -337,10 +373,26 @@ def method_type
337373
return_type: return_type
338374
)
339375

376+
method_block =
377+
case self.block
378+
when Annotations::BlockParamTypeAnnotation
379+
Types::Block.new(
380+
type: self.block.type,
381+
required: self.block.required?
382+
)
383+
when Symbol, true
384+
Types::Block.new(
385+
type: Types::UntypedFunction.new(return_type: Types::Bases::Any.new(location: nil)),
386+
required: false
387+
)
388+
else
389+
nil
390+
end
391+
340392
MethodType.new(
341393
type_params: [],
342394
type: type,
343-
block: nil,
395+
block: method_block,
344396
location: nil
345397
)
346398
end
@@ -371,7 +423,7 @@ def self.build(leading_block, trailing_block, variables, node)
371423

372424
type_annotations = nil #: type_annotations
373425
return_annotation = nil #: Annotations::ReturnTypeAnnotation | Annotations::NodeTypeAssertion | nil
374-
param_annotations = [] #: Array[Annotations::ParamTypeAnnotation | Annotations::SplatParamTypeAnnotation | Annotations::DoubleSplatParamTypeAnnotation]
426+
param_annotations = [] #: Array[Annotations::ParamTypeAnnotation | Annotations::SplatParamTypeAnnotation | Annotations::DoubleSplatParamTypeAnnotation | Annotations::BlockParamTypeAnnotation]
375427

376428
if trailing_block
377429
case annotation = trailing_block.trailing_annotation(variables)
@@ -405,7 +457,7 @@ def self.build(leading_block, trailing_block, variables, node)
405457
next
406458
end
407459
end
408-
when Annotations::ParamTypeAnnotation, Annotations::SplatParamTypeAnnotation, Annotations::DoubleSplatParamTypeAnnotation
460+
when Annotations::ParamTypeAnnotation, Annotations::SplatParamTypeAnnotation, Annotations::DoubleSplatParamTypeAnnotation, Annotations::BlockParamTypeAnnotation
409461
unless type_annotations
410462
param_annotations << paragraph
411463
next

sig/ast/ruby/members.rbs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ module RBS
2222
type param_type_annotation = Annotations::ParamTypeAnnotation
2323
| Annotations::SplatParamTypeAnnotation
2424
| Annotations::DoubleSplatParamTypeAnnotation
25+
| Annotations::BlockParamTypeAnnotation
2526

2627
attr_accessor return_type_annotation: Annotations::ReturnTypeAnnotation | Annotations::NodeTypeAssertion | nil
2728

@@ -32,6 +33,7 @@ module RBS
3233
attr_reader required_keywords: Hash[Symbol, Annotations::ParamTypeAnnotation | Symbol]
3334
attr_reader optional_keywords: Hash[Symbol, Annotations::ParamTypeAnnotation | Symbol]
3435
attr_accessor rest_keywords: Annotations::DoubleSplatParamTypeAnnotation | Symbol | true | nil
36+
attr_accessor block: Annotations::BlockParamTypeAnnotation | Symbol | true | nil
3537

3638
def initialize: () -> void
3739

test/rbs/inline_parser_test.rb

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,117 @@ def baz(baz_arg, *baz_args, baz_kw:, **baz_kwargs) #: void
466466
end
467467
end
468468

469+
def test_parse__def_method_docs__block
470+
result = parse(<<~RUBY)
471+
class Foo
472+
# @rbs &block: () -> void
473+
def foo(&block)
474+
end
475+
476+
# @rbs &: ? () -> void
477+
def bar(&)
478+
end
479+
480+
# @rbs &block: ? () -> untyped
481+
def baz(&block)
482+
end
483+
484+
# @rbs &: () -> void
485+
def qux(&blk)
486+
end
487+
end
488+
RUBY
489+
490+
assert_empty result.diagnostics
491+
492+
result.declarations[0].tap do |decl|
493+
decl.members[0].tap do |member|
494+
assert_instance_of RBS::AST::Ruby::Members::DefMember, member
495+
assert_equal ["() { () -> void } -> untyped"], member.overloads.map { _1.method_type.to_s }
496+
end
497+
498+
decl.members[1].tap do |member|
499+
assert_instance_of RBS::AST::Ruby::Members::DefMember, member
500+
assert_equal ["() ?{ () -> void } -> untyped"], member.overloads.map { _1.method_type.to_s }
501+
end
502+
503+
decl.members[2].tap do |member|
504+
assert_instance_of RBS::AST::Ruby::Members::DefMember, member
505+
assert_equal ["() ?{ () -> untyped } -> untyped"], member.overloads.map { _1.method_type.to_s }
506+
end
507+
508+
decl.members[3].tap do |member|
509+
assert_instance_of RBS::AST::Ruby::Members::DefMember, member
510+
assert_equal ["() { () -> void } -> untyped"], member.overloads.map { _1.method_type.to_s }
511+
end
512+
end
513+
end
514+
515+
def test_parse__def_method_docs__block__no_block_param
516+
result = parse(<<~RUBY)
517+
class Foo
518+
# @rbs &block: () -> void
519+
def foo(x)
520+
end
521+
end
522+
RUBY
523+
524+
assert_equal 0, result.diagnostics.size
525+
526+
result.declarations[0].tap do |decl|
527+
decl.members[0].tap do |member|
528+
assert_instance_of RBS::AST::Ruby::Members::DefMember, member
529+
assert_equal :foo, member.name
530+
assert_equal "(untyped x) { () -> void } -> untyped", member.overloads[0].method_type.to_s
531+
end
532+
end
533+
end
534+
535+
def test_error__def_method_docs__block__name_mismatch
536+
result = parse(<<~RUBY)
537+
class Foo
538+
# @rbs &block: () -> void
539+
def foo(&callback)
540+
end
541+
end
542+
RUBY
543+
544+
assert_equal 1, result.diagnostics.size
545+
546+
assert_any!(result.diagnostics) do |diagnostic|
547+
assert_instance_of RBS::InlineParser::Diagnostic::UnusedInlineAnnotation, diagnostic
548+
assert_equal "@rbs &block: () -> void", diagnostic.location.source
549+
end
550+
end
551+
552+
def test_parse__def_method_docs__unannotated_block_param
553+
result = parse(<<~RUBY)
554+
class Foo
555+
def foo(&) #: void
556+
end
557+
558+
def bar(&block) #: void
559+
end
560+
end
561+
RUBY
562+
563+
assert_empty result.diagnostics
564+
565+
result.declarations[0].tap do |decl|
566+
decl.members[0].tap do |member|
567+
assert_instance_of RBS::AST::Ruby::Members::DefMember, member
568+
assert_equal :foo, member.name
569+
assert_equal ["() ?{ (?) -> untyped } -> void"], member.overloads.map { _1.method_type.to_s }
570+
end
571+
572+
decl.members[1].tap do |member|
573+
assert_instance_of RBS::AST::Ruby::Members::DefMember, member
574+
assert_equal :bar, member.name
575+
assert_equal ["() ?{ (?) -> untyped } -> void"], member.overloads.map { _1.method_type.to_s }
576+
end
577+
end
578+
end
579+
469580
def test_parse__skip_class_module
470581
result = parse(<<~RUBY)
471582
# @rbs skip -- not a constant

0 commit comments

Comments
 (0)