Skip to content

Commit 3178cfc

Browse files
committed
Implement inline parser with method type annotations
1 parent 594ee7d commit 3178cfc

File tree

8 files changed

+263
-19
lines changed

8 files changed

+263
-19
lines changed

lib/rbs/ast/ruby/members.rb

Lines changed: 45 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -83,35 +83,69 @@ def self.build(leading_block, trailing_block, variables)
8383
def empty?
8484
type_annotations.nil?
8585
end
86+
87+
def overloads
88+
case type_annotations
89+
when Annotations::NodeTypeAssertion
90+
method_type = MethodType.new(
91+
type_params: [],
92+
type: Types::Function.empty(type_annotations.type),
93+
block: nil,
94+
location: nil
95+
)
96+
97+
[
98+
AST::Members::MethodDefinition::Overload.new(annotations: [], method_type: method_type)
99+
]
100+
when Array
101+
type_annotations.flat_map do |annotation|
102+
case annotation
103+
when Annotations::ColonMethodTypeAnnotation
104+
[
105+
AST::Members::MethodDefinition::Overload.new(
106+
annotations: annotation.annotations,
107+
method_type: annotation.method_type
108+
)
109+
]
110+
when Annotations::MethodTypesAnnotation
111+
annotation.overloads
112+
end
113+
end
114+
when nil
115+
method_type = MethodType.new(
116+
type_params: [],
117+
type: Types::UntypedFunction.new(return_type: Types::Bases::Any.new(location: nil)),
118+
block: nil,
119+
location: nil
120+
)
121+
122+
[
123+
AST::Members::MethodDefinition::Overload.new(method_type: method_type, annotations: [])
124+
]
125+
end
126+
end
86127
end
87128

88129
class DefMember < Base
89130
Overload = AST::Members::MethodDefinition::Overload
90131

91132
attr_reader :name
92133
attr_reader :node
134+
attr_reader :method_type
93135

94-
def initialize(buffer, name, node)
136+
def initialize(buffer, name, node, method_type)
95137
super(buffer)
96138
@name = name
97139
@node = node
140+
@method_type = method_type
98141
end
99142

100143
def location
101144
rbs_location(node.location)
102145
end
103146

104147
def overloads
105-
method_type = MethodType.new(
106-
type_params: [],
107-
type: Types::UntypedFunction.new(return_type: Types::Bases::Any.new(location: nil)),
108-
block: nil,
109-
location: nil
110-
)
111-
112-
[
113-
Overload.new(method_type: method_type, annotations: [])
114-
]
148+
method_type.overloads
115149
end
116150

117151
def overloading?

lib/rbs/environment.rb

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -673,7 +673,7 @@ def resolve_ruby_decl(resolver, decl, context:, prefix:)
673673
when AST::Ruby::Declarations::Base
674674
resolved.members << resolve_ruby_decl(resolver, member, context: inner_context, prefix: inner_prefix)
675675
when AST::Ruby::Members::Base
676-
resolved.members << member
676+
resolved.members << resolve_ruby_member(resolver, member, context: inner_context)
677677
else
678678
raise "Unknown member type: #{member.class}"
679679
end
@@ -701,6 +701,20 @@ def resolve_ruby_decl(resolver, decl, context:, prefix:)
701701
end
702702
end
703703

704+
def resolve_ruby_member(resolver, member, context:)
705+
case member
706+
when AST::Ruby::Members::DefMember
707+
AST::Ruby::Members::DefMember.new(
708+
member.buffer,
709+
member.name,
710+
member.node,
711+
member.method_type.map_type_name {|name, _, _| absolute_type_name(resolver, nil, name, context: context) }
712+
)
713+
else
714+
raise "Unknown member type: #{member.class}"
715+
end
716+
end
717+
704718
def resolve_member(resolver, map, member, context:)
705719
case member
706720
when AST::Members::MethodDefinition
@@ -816,7 +830,7 @@ def resolve_type_params(resolver, map, params, context:)
816830
end
817831

818832
def absolute_type_name(resolver, map, type_name, context:)
819-
type_name = map.resolve(type_name)
833+
type_name = map.resolve(type_name) if map
820834
resolver.resolve(type_name, context: context) || type_name
821835
end
822836

lib/rbs/inline_parser.rb

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ def initialize(location, message)
2727
NonConstantClassName = _ = Class.new(Base)
2828
NonConstantModuleName = _ = Class.new(Base)
2929
TopLevelMethodDefinition = _ = Class.new(Base)
30+
UnusedInlineAnnotation = _ = Class.new(Base)
31+
AnnotationSyntaxError = _ = Class.new(Base)
3032
end
3133

3234
def self.parse(buffer, prism)
@@ -38,14 +40,15 @@ def self.parse(buffer, prism)
3840
end
3941

4042
class Parser < Prism::Visitor
41-
attr_reader :module_nesting, :result
43+
attr_reader :module_nesting, :result, :comments
4244

4345
include AST::Ruby::Helpers::ConstantHelper
4446
include AST::Ruby::Helpers::LocationHelper
4547

4648
def initialize(result)
4749
@result = result
4850
@module_nesting = []
51+
@comments = CommentAssociation.build(result.buffer, result.prism_result)
4952
end
5053

5154
def buffer
@@ -85,6 +88,10 @@ def visit_class_node(node)
8588
push_module_nesting(class_decl) do
8689
visit_child_nodes(node)
8790
end
91+
92+
comments.each_enclosed_block(node) do |block|
93+
report_unused_block(block)
94+
end
8895
end
8996

9097
def visit_module_node(node)
@@ -101,6 +108,10 @@ def visit_module_node(node)
101108
push_module_nesting(module_decl) do
102109
visit_child_nodes(node)
103110
end
111+
112+
comments.each_enclosed_block(node) do |block|
113+
report_unused_block(block)
114+
end
104115
end
105116

106117
def visit_def_node(node)
@@ -114,8 +125,24 @@ def visit_def_node(node)
114125

115126
case current = current_module
116127
when AST::Ruby::Declarations::ClassDecl, AST::Ruby::Declarations::ModuleDecl
117-
defn = AST::Ruby::Members::DefMember.new(buffer, node.name, node)
128+
leading_block = comments.leading_block!(node)
129+
130+
if node.end_keyword_loc
131+
# Not an end-less def
132+
end_loc = node.rparen_loc || node.parameters&.location || node.name_loc
133+
trailing_block = comments.trailing_block!(end_loc)
134+
end
135+
136+
method_type, leading_unuseds, trailing_unused = AST::Ruby::Members::MethodTypeAnnotation.build(leading_block, trailing_block, [])
137+
report_unused_annotation(trailing_unused, *leading_unuseds)
138+
139+
defn = AST::Ruby::Members::DefMember.new(buffer, node.name, node, method_type)
118140
current.members << defn
141+
142+
# Skip other comments in `def` node
143+
comments.each_enclosed_block(node) do |block|
144+
comments.associated_blocks << block
145+
end
119146
else
120147
diagnostics << Diagnostic::TopLevelMethodDefinition.new(
121148
rbs_location(node.name_loc),
@@ -131,6 +158,32 @@ def insert_declaration(decl)
131158
result.declarations << decl
132159
end
133160
end
161+
162+
def report_unused_annotation(*annotations)
163+
annotations.each do |annotation|
164+
case annotation
165+
when AST::Ruby::CommentBlock::AnnotationSyntaxError
166+
diagnostics << Diagnostic::AnnotationSyntaxError.new(
167+
annotation.location, "Syntax error: " + annotation.error.error_message
168+
)
169+
when AST::Ruby::Annotations::Base
170+
diagnostics << Diagnostic::UnusedInlineAnnotation.new(
171+
annotation.location, "Unused inline rbs annotation"
172+
)
173+
end
174+
end
175+
end
176+
177+
def report_unused_block(block)
178+
block.each_paragraph([]) do |paragraph|
179+
case paragraph
180+
when Location
181+
# noop
182+
else
183+
report_unused_annotation(paragraph)
184+
end
185+
end
186+
end
134187
end
135188
end
136189
end

sig/ast/ruby/members.rbs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,20 @@ module RBS
3333

3434
# Returns `true` if it doesn't have any annotation
3535
def empty?: () -> bool
36+
37+
# Returns the method type overloads
38+
#
39+
def overloads: () -> Array[AST::Members::MethodDefinition::Overload]
3640
end
3741

3842
class DefMember < Base
3943
class Overload = AST::Members::MethodDefinition::Overload
4044

4145
attr_reader name: Symbol
4246
attr_reader node: Prism::DefNode
47+
attr_reader method_type: MethodTypeAnnotation
4348

44-
def initialize: (Buffer, Symbol name, Prism::DefNode node) -> void
49+
def initialize: (Buffer, Symbol name, Prism::DefNode node, MethodTypeAnnotation) -> void
4550

4651
def location: () -> Location
4752

sig/environment.rbs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,10 @@ module RBS
216216

217217
def resolve_ruby_decl: (Resolver::TypeNameResolver, AST::Ruby::Declarations::t, context: Resolver::context, prefix: Namespace) -> AST::Ruby::Declarations::t
218218

219-
def absolute_type: (Resolver::TypeNameResolver, UseMap map, Types::t, context: Resolver::context) -> Types::t
219+
def resolve_ruby_member: (Resolver::TypeNameResolver, AST::Ruby::Members::t, context: Resolver::context) -> AST::Ruby::Members::t
220220

221-
def absolute_type_name: (Resolver::TypeNameResolver, UseMap map, TypeName, context: Resolver::context) -> TypeName
221+
def absolute_type: (Resolver::TypeNameResolver, UseMap? map, Types::t, context: Resolver::context) -> Types::t
222+
223+
def absolute_type_name: (Resolver::TypeNameResolver, UseMap? map, TypeName, context: Resolver::context) -> TypeName
222224
end
223225
end

sig/inline_parser.rbs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,16 @@ module RBS
3232
class TopLevelMethodDefinition < Base
3333
end
3434

35+
class UnusedInlineAnnotation < Base
36+
end
37+
38+
class AnnotationSyntaxError < Base
39+
end
40+
3541
type t = NotImplementedYet
3642
| NonConstantClassName | NonConstantModuleName
3743
| TopLevelMethodDefinition
44+
| UnusedInlineAnnotation | AnnotationSyntaxError
3845
end
3946

4047
def self.parse: (Buffer, Prism::ParseResult) -> Result
@@ -46,6 +53,8 @@ module RBS
4653

4754
include AST::Ruby::Helpers::LocationHelper
4855

56+
attr_reader comments: CommentAssociation
57+
4958
attr_reader result: Result
5059

5160
attr_reader module_nesting: Array[module_context]
@@ -63,6 +72,10 @@ module RBS
6372
def push_module_nesting: [T] (module_context) { () -> T } -> T
6473

6574
def insert_declaration: (module_context) -> void
75+
76+
def report_unused_annotation: (*AST::Ruby::Annotations::t | nil | AST::Ruby::CommentBlock::AnnotationSyntaxError) -> void
77+
78+
def report_unused_block: (AST::Ruby::CommentBlock) -> void
6679
end
6780
end
6881
end

test/rbs/definition_builder_test.rb

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3201,7 +3201,7 @@ module A
32013201
end
32023202
end
32033203

3204-
def test_inline_decl__class_def
3204+
def test_inline_decl__class_def__untyped
32053205
SignatureManager.new do |manager|
32063206
manager.add_ruby_file("inherited.rbs", <<~RUBY)
32073207
class A
@@ -3226,4 +3226,31 @@ def hello(x) = 123
32263226
end
32273227
end
32283228
end
3229+
3230+
def test_inline_decl__class_def__typed
3231+
SignatureManager.new do |manager|
3232+
manager.add_ruby_file("inherited.rbs", <<~RUBY)
3233+
class A
3234+
# @rbs (String) -> Integer
3235+
def hello(x) = 123
3236+
end
3237+
RUBY
3238+
3239+
manager.build do |env|
3240+
builder = DefinitionBuilder.new(env: env)
3241+
3242+
builder.build_instance(type_name("::A")).tap do |definition|
3243+
definition.methods[:hello].tap do |method|
3244+
assert_equal type_name("::A"), method.defined_in
3245+
assert_equal type_name("::A"), method.implemented_in
3246+
3247+
assert_equal [parse_method_type("(::String) -> ::Integer")], method.method_types
3248+
assert_equal [parse_method_type("(::String) -> ::Integer")], method.defs.map(&:type)
3249+
3250+
assert_equal [], method.annotations
3251+
end
3252+
end
3253+
end
3254+
end
3255+
end
32293256
end

0 commit comments

Comments
 (0)