Skip to content

Commit 7b81cb0

Browse files
committed
Implement parser
1 parent 48dd410 commit 7b81cb0

File tree

4 files changed

+361
-0
lines changed

4 files changed

+361
-0
lines changed

lib/rbs/inline_parser.rb

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ def initialize(location, message)
2929
TopLevelMethodDefinition = _ = Class.new(Base)
3030
UnusedInlineAnnotation = _ = Class.new(Base)
3131
AnnotationSyntaxError = _ = Class.new(Base)
32+
MixinMultipleArguments = _ = Class.new(Base)
33+
MixinNonConstantModule = _ = Class.new(Base)
3234
end
3335

3436
def self.parse(buffer, prism)
@@ -168,6 +170,80 @@ def visit_def_node(node)
168170
end
169171
end
170172

173+
def visit_call_node(node)
174+
return unless node.receiver.nil? # Only handle top-level calls like include, extend, prepend
175+
176+
case node.name
177+
when :include, :extend, :prepend
178+
return if skip_node?(node)
179+
180+
case current = current_module
181+
when AST::Ruby::Declarations::ClassDecl, AST::Ruby::Declarations::ModuleDecl
182+
parse_mixin_call(node)
183+
end
184+
else
185+
visit_child_nodes(node)
186+
end
187+
end
188+
189+
private
190+
191+
def parse_mixin_call(node)
192+
# Check for multiple arguments
193+
if node.arguments && node.arguments.arguments.length > 1
194+
diagnostics << Diagnostic::MixinMultipleArguments.new(
195+
rbs_location(node.location),
196+
"Mixing multiple modules with one call is not supported"
197+
)
198+
return
199+
end
200+
201+
# Check for missing arguments
202+
unless node.arguments && node.arguments.arguments.length == 1
203+
# This shouldn't happen in valid Ruby code, but handle it gracefully
204+
return
205+
end
206+
207+
first_arg = node.arguments.arguments.first
208+
209+
# Check if the argument is a constant
210+
unless module_name = constant_as_type_name(first_arg)
211+
diagnostics << Diagnostic::MixinNonConstantModule.new(
212+
rbs_location(first_arg.location),
213+
"Module name must be a constant"
214+
)
215+
return
216+
end
217+
218+
# Look for type application annotation in trailing comments
219+
# For single-line calls like "include Bar #[String]", the annotation is trailing
220+
trailing_block = comments.trailing_block!(node.location)
221+
annotation = nil
222+
223+
if trailing_block
224+
case trailing_annotation = trailing_block.trailing_annotation([])
225+
when AST::Ruby::Annotations::TypeApplicationAnnotation
226+
annotation = trailing_annotation
227+
else
228+
report_unused_annotation(trailing_annotation)
229+
end
230+
end
231+
232+
# Create the appropriate member based on the method name
233+
member = case node.name
234+
when :include
235+
AST::Ruby::Members::IncludeMember.new(buffer, node, module_name, annotation)
236+
when :extend
237+
AST::Ruby::Members::ExtendMember.new(buffer, node, module_name, annotation)
238+
when :prepend
239+
AST::Ruby::Members::PrependMember.new(buffer, node, module_name, annotation)
240+
else
241+
raise "Unexpected mixin method: #{node.name}"
242+
end
243+
244+
current_module!.members << member
245+
end
246+
171247
def insert_declaration(decl)
172248
if current_module
173249
current_module.members << decl

sig/inline_parser.rbs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,17 @@ module RBS
3838
class AnnotationSyntaxError < Base
3939
end
4040

41+
class MixinMultipleArguments < Base
42+
end
43+
44+
class MixinNonConstantModule < Base
45+
end
46+
4147
type t = NotImplementedYet
4248
| NonConstantClassName | NonConstantModuleName
4349
| TopLevelMethodDefinition
4450
| UnusedInlineAnnotation | AnnotationSyntaxError
51+
| MixinMultipleArguments | MixinNonConstantModule
4552
end
4653

4754
def self.parse: (Buffer, Prism::ParseResult) -> Result
@@ -82,6 +89,10 @@ module RBS
8289
def report_unused_annotation: (*AST::Ruby::Annotations::t | nil | AST::Ruby::CommentBlock::AnnotationSyntaxError) -> void
8390

8491
def report_unused_block: (AST::Ruby::CommentBlock) -> void
92+
93+
private
94+
95+
def parse_mixin_call: (Prism::CallNode) -> void
8596
end
8697
end
8798
end

test/rbs/ast/ruby/comment_block_test.rb

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,4 +216,52 @@ def test_trailing_annotation
216216
assert_nil annotation
217217
end
218218
end
219+
220+
def test_trailing_annotation_type_application
221+
buffer, comments = parse_comments(<<~RUBY)
222+
foo #[String]
223+
224+
foo #[String, Integer]
225+
226+
foo #[String, Integer, void]
227+
228+
foo #[String[
229+
230+
foo #[String, Integer] # Extra comment
231+
RUBY
232+
233+
blocks = CommentBlock.build(buffer, comments)
234+
235+
blocks[0].trailing_annotation([]).tap do |annotation|
236+
assert_instance_of RBS::AST::Ruby::Annotations::TypeApplicationAnnotation, annotation
237+
assert_equal 1, annotation.type_args.size
238+
assert_equal "String", annotation.type_args[0].to_s
239+
end
240+
241+
blocks[1].trailing_annotation([]).tap do |annotation|
242+
assert_instance_of RBS::AST::Ruby::Annotations::TypeApplicationAnnotation, annotation
243+
assert_equal 2, annotation.type_args.size
244+
assert_equal "String", annotation.type_args[0].to_s
245+
assert_equal "Integer", annotation.type_args[1].to_s
246+
end
247+
248+
blocks[2].trailing_annotation([]).tap do |annotation|
249+
assert_instance_of RBS::AST::Ruby::Annotations::TypeApplicationAnnotation, annotation
250+
assert_equal 3, annotation.type_args.size
251+
assert_equal "String", annotation.type_args[0].to_s
252+
assert_equal "Integer", annotation.type_args[1].to_s
253+
assert_equal "void", annotation.type_args[2].to_s
254+
end
255+
256+
blocks[3].trailing_annotation([]).tap do |annotation|
257+
assert_instance_of CommentBlock::AnnotationSyntaxError, annotation
258+
end
259+
260+
blocks[4].trailing_annotation([]).tap do |annotation|
261+
assert_instance_of RBS::AST::Ruby::Annotations::TypeApplicationAnnotation, annotation
262+
assert_equal 2, annotation.type_args.size
263+
assert_equal "String", annotation.type_args[0].to_s
264+
assert_equal "Integer", annotation.type_args[1].to_s
265+
end
266+
end
219267
end

0 commit comments

Comments
 (0)