Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,9 @@ nodes:
c_type: rbs_node_list
- name: vertical_bar_locations
c_type: rbs_location_range_list
- name: dot3_location
c_type: rbs_location_range
optional: true
- name: RBS::AST::Ruby::Annotations::SkipAnnotation
rust_name: SkipAnnotationNode
fields:
Expand Down
12 changes: 12 additions & 0 deletions docs/inline.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,18 @@ The type of both methods is `(Integer, Integer) -> Integer | (Float, Float) -> F
> The `@rbs METHOD-TYPE` syntax allows overloads with the `|` operator, just like in RBS files.
> Multiple `: METHOD-TYPE` declarations are required for overloads.

The `@rbs METHOD-TYPE` syntax allows having `...` at the last part.

```ruby
class Calculator2 < Calculator
# @rbs (Float, Float) -> Float | ...
def add(x, y) = x + y

# @rbs ...
def subtract(x, y) = super
end
```

#### Doc-style syntax

The `@rbs return: T` syntax declares the return type of a method:
Expand Down
1 change: 1 addition & 0 deletions ext/rbs_extension/ast_translation.c
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,7 @@ VALUE rbs_struct_to_ruby_value(rbs_translation_context_t ctx, rbs_node_t *instan
rb_hash_aset(h, ID2SYM(rb_intern("prefix_location")), rbs_location_range_to_ruby_location(ctx, node->prefix_location));
rb_hash_aset(h, ID2SYM(rb_intern("overloads")), rbs_node_list_to_ruby_array(ctx, node->overloads));
rb_hash_aset(h, ID2SYM(rb_intern("vertical_bar_locations")), rbs_location_range_list_to_ruby_array(ctx, node->vertical_bar_locations));
rb_hash_aset(h, ID2SYM(rb_intern("dot3_location")), rbs_location_range_to_ruby_location(ctx, node->dot3_location)); // optional

return CLASS_NEW_INSTANCE(
RBS_AST_Ruby_Annotations_MethodTypesAnnotation,
Expand Down
3 changes: 2 additions & 1 deletion include/rbs/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,7 @@ typedef struct rbs_ast_ruby_annotations_method_types_annotation {
rbs_location_range prefix_location;
struct rbs_node_list *overloads;
rbs_location_range_list_t *vertical_bar_locations;
rbs_location_range dot3_location; /* Optional */
} rbs_ast_ruby_annotations_method_types_annotation_t;

typedef struct rbs_ast_ruby_annotations_module_alias_annotation {
Expand Down Expand Up @@ -925,7 +926,7 @@ rbs_ast_members_public_t *rbs_ast_members_public_new(rbs_allocator_t *allocator,
rbs_ast_ruby_annotations_class_alias_annotation_t *rbs_ast_ruby_annotations_class_alias_annotation_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_location_range prefix_location, rbs_location_range keyword_location, rbs_type_name_t *type_name, rbs_location_range type_name_location);
rbs_ast_ruby_annotations_colon_method_type_annotation_t *rbs_ast_ruby_annotations_colon_method_type_annotation_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_location_range prefix_location, rbs_node_list_t *annotations, rbs_node_t *method_type);
rbs_ast_ruby_annotations_instance_variable_annotation_t *rbs_ast_ruby_annotations_instance_variable_annotation_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_location_range prefix_location, rbs_ast_symbol_t *ivar_name, rbs_location_range ivar_name_location, rbs_location_range colon_location, rbs_node_t *type, rbs_location_range comment_location);
rbs_ast_ruby_annotations_method_types_annotation_t *rbs_ast_ruby_annotations_method_types_annotation_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_location_range prefix_location, rbs_node_list_t *overloads, rbs_location_range_list_t *vertical_bar_locations);
rbs_ast_ruby_annotations_method_types_annotation_t *rbs_ast_ruby_annotations_method_types_annotation_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_location_range prefix_location, rbs_node_list_t *overloads, rbs_location_range_list_t *vertical_bar_locations, rbs_location_range dot3_location);
rbs_ast_ruby_annotations_module_alias_annotation_t *rbs_ast_ruby_annotations_module_alias_annotation_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_location_range prefix_location, rbs_location_range keyword_location, rbs_type_name_t *type_name, rbs_location_range type_name_location);
rbs_ast_ruby_annotations_node_type_assertion_t *rbs_ast_ruby_annotations_node_type_assertion_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_location_range prefix_location, rbs_node_t *type);
rbs_ast_ruby_annotations_return_type_annotation_t *rbs_ast_ruby_annotations_return_type_annotation_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_location_range prefix_location, rbs_location_range return_location, rbs_location_range colon_location, rbs_node_t *return_type, rbs_location_range comment_location);
Expand Down
10 changes: 6 additions & 4 deletions lib/rbs/ast/ruby/annotations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,12 +109,13 @@ def type_fingerprint
class MethodTypesAnnotation < Base
Overload = AST::Members::MethodDefinition::Overload

attr_reader :overloads, :vertical_bar_locations
attr_reader :overloads, :vertical_bar_locations, :dot3_location

def initialize(location:, prefix_location:, overloads:, vertical_bar_locations:)
def initialize(location:, prefix_location:, overloads:, vertical_bar_locations:, dot3_location:)
super(location, prefix_location)
@overloads = overloads
@vertical_bar_locations = vertical_bar_locations
@dot3_location = dot3_location
end

def map_type_name(&block)
Expand All @@ -125,13 +126,14 @@ def map_type_name(&block)
)
end

self.class.new(location:, prefix_location:, overloads: ovs, vertical_bar_locations:) #: self
self.class.new(location:, prefix_location:, overloads: ovs, vertical_bar_locations:, dot3_location:) #: self
end

def type_fingerprint
[
"annots/method_types",
overloads.map { |o| [o.annotations.map(&:to_s), o.method_type.to_s] }
overloads.map { |o| [o.annotations.map(&:to_s), o.method_type.to_s] },
overloading: dot3_location ? true : false
]
end
end
Expand Down
13 changes: 12 additions & 1 deletion lib/rbs/ast/ruby/members.rb
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,17 @@ def overloads
end
end

def overloading?
case type_annotations
when Array
type_annotations.any? do |annotation|
annotation.is_a?(Annotations::MethodTypesAnnotation) && annotation.dot3_location
end
else
false
end
end

def type_fingerprint
case type_annotations
when DocStyle
Expand Down Expand Up @@ -217,7 +228,7 @@ def overloads
end

def overloading?
false
method_type.overloading?
end

def annotations
Expand Down
12 changes: 8 additions & 4 deletions sig/ast/ruby/annotations.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,11 @@ module RBS
# `@rbs METHOD-TYPEs` annotation in leading comments
#
# ```
# @rbs () -> void | %a{foo} () -> String
# ^^^^ -- prefix_location
# ^ -- vertical_bar_locations[0]
# @rbs () -> void | %a{foo} () -> String | ...
# ^^^^ -- prefix_location
# ^ -- vertical_bar_locations[0]
# ^ -- vertical_bar_locations[1]
# ^^^ -- dot3_location
# ```
class MethodTypesAnnotation < Base
class Overload = AST::Members::MethodDefinition::Overload
Expand All @@ -115,7 +117,9 @@ module RBS

attr_reader vertical_bar_locations: Array[Location]

def initialize: (location: Location, prefix_location: Location, overloads: Array[Overload], vertical_bar_locations: Array[Location]) -> void
attr_reader dot3_location: Location?

def initialize: (location: Location, prefix_location: Location, overloads: Array[Overload], vertical_bar_locations: Array[Location], dot3_location: Location?) -> void

def map_type_name: () { (TypeName) -> TypeName } -> self

Expand Down
2 changes: 2 additions & 0 deletions sig/ast/ruby/members.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ module RBS
#
def overloads: () -> Array[AST::Members::MethodDefinition::Overload]

def overloading?: () -> bool

def type_fingerprint: () -> untyped
end

Expand Down
3 changes: 2 additions & 1 deletion src/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -934,7 +934,7 @@ rbs_ast_ruby_annotations_instance_variable_annotation_t *rbs_ast_ruby_annotation
return instance;
}
#line 140 "prism/templates/src/ast.c.erb"
rbs_ast_ruby_annotations_method_types_annotation_t *rbs_ast_ruby_annotations_method_types_annotation_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_location_range prefix_location, rbs_node_list_t *overloads, rbs_location_range_list_t *vertical_bar_locations) {
rbs_ast_ruby_annotations_method_types_annotation_t *rbs_ast_ruby_annotations_method_types_annotation_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_location_range prefix_location, rbs_node_list_t *overloads, rbs_location_range_list_t *vertical_bar_locations, rbs_location_range dot3_location) {
rbs_ast_ruby_annotations_method_types_annotation_t *instance = rbs_allocator_alloc(allocator, rbs_ast_ruby_annotations_method_types_annotation_t);

*instance = (rbs_ast_ruby_annotations_method_types_annotation_t) {
Expand All @@ -945,6 +945,7 @@ rbs_ast_ruby_annotations_method_types_annotation_t *rbs_ast_ruby_annotations_met
.prefix_location = prefix_location,
.overloads = overloads,
.vertical_bar_locations = vertical_bar_locations,
.dot3_location = dot3_location,
};

return instance;
Expand Down
41 changes: 37 additions & 4 deletions src/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -3575,12 +3575,13 @@ static bool parse_method_overload(rbs_parser_t *parser, rbs_node_list_t *annotat
}

/*
inline_method_overloads ::= {} <overload> -- returns true
inline_method_overloads ::= {} <overload> -- returns true
| {} overload `|` ... `|` overload -- returns true
| {} overload `|` ... `|` `...` -- returns true (dot3_location is set)
| {<>} -- returns false
*/
NODISCARD
static bool parse_inline_method_overloads(rbs_parser_t *parser, rbs_node_list_t *overloads, rbs_location_range_list_t *bar_locations) {
static bool parse_inline_method_overloads(rbs_parser_t *parser, rbs_node_list_t *overloads, rbs_location_range_list_t *bar_locations, rbs_location_range *dot3_location) {
while (true) {
rbs_node_list_t *annotations = rbs_node_list_new(ALLOCATOR());
rbs_method_type_t *method_type = NULL;
Expand All @@ -3605,6 +3606,12 @@ static bool parse_inline_method_overloads(rbs_parser_t *parser, rbs_node_list_t

rbs_location_range_list_append(bar_locations, bar_range);

if (parser->next_token.type == pDOT3) {
*dot3_location = RBS_RANGE_LEX2AST(parser->next_token.range);
rbs_parser_advance(parser);
return true;
}

continue;
}

Expand Down Expand Up @@ -3660,14 +3667,39 @@ static bool parse_inline_leading_annotation(rbs_parser_t *parser, rbs_ast_ruby_a
rbs_parser_advance(parser);

switch (parser->next_token.type) {
case pDOT3: {
rbs_location_range dot3_range = RBS_RANGE_LEX2AST(parser->next_token.range);
rbs_parser_advance(parser);

rbs_node_list_t *overloads = rbs_node_list_new(ALLOCATOR());
rbs_location_range_list_t *bar_locations = rbs_location_range_list_new(ALLOCATOR());

rbs_range_t full_range = {
.start = rbs_range.start,
.end = parser->current_token.range.end
};

rbs_location_range full_loc = RBS_RANGE_LEX2AST(full_range);

*annotation = (rbs_ast_ruby_annotations_t *) rbs_ast_ruby_annotations_method_types_annotation_new(
ALLOCATOR(),
full_loc,
RBS_RANGE_LEX2AST(rbs_range),
overloads,
bar_locations,
dot3_range
);
return true;
}
case pLPAREN:
case pLBRACKET:
case pLBRACE:
case tANNOTATION: {
rbs_node_list_t *overloads = rbs_node_list_new(ALLOCATOR());
rbs_location_range_list_t *bar_locations = rbs_location_range_list_new(ALLOCATOR());
rbs_location_range dot3_location = RBS_LOCATION_NULL_RANGE;

if (!parse_inline_method_overloads(parser, overloads, bar_locations)) {
if (!parse_inline_method_overloads(parser, overloads, bar_locations, &dot3_location)) {
return false;
}

Expand All @@ -3683,7 +3715,8 @@ static bool parse_inline_leading_annotation(rbs_parser_t *parser, rbs_ast_ruby_a
full_loc,
RBS_RANGE_LEX2AST(rbs_range),
overloads,
bar_locations
bar_locations,
dot3_location
);
return true;
}
Expand Down
38 changes: 38 additions & 0 deletions test/rbs/definition_builder_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3767,4 +3767,42 @@ def initialize(name, age)
end
end
end

def test_inline_method_types__overloading
SignatureManager.new do |manager|
manager.files[Pathname("base.rbs")] = <<~RBS
class Base
def foo: () -> String
def bar: () -> String
end
RBS

manager.add_ruby_file("child.rb", <<~RUBY)
class Child < Base
# @rbs (Integer) -> String | ...
def foo(x = nil) = ""

# @rbs ...
def bar = ""
end
RUBY

manager.build do |env|
builder = DefinitionBuilder.new(env: env)

builder.build_instance(type_name("::Child")).tap do |definition|
definition.methods[:foo].tap do |method|
assert_equal ["(::Integer) -> ::String", "() -> ::String"], method.method_types.map(&:to_s)
assert_equal type_name("::Child"), method.defs[0].defined_in
assert_equal type_name("::Base"), method.defs[1].defined_in
end

definition.methods[:bar].tap do |method|
assert_equal ["() -> ::String"], method.method_types.map(&:to_s)
assert_equal type_name("::Base"), method.defs[0].defined_in
end
end
end
end
end
end
70 changes: 70 additions & 0 deletions test/rbs/inline_annotation_parsing_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -292,4 +292,74 @@ def test_error__module_alias_with_type_variable
Parser.parse_inline_trailing_annotation(": module-alias element", 0...)
end
end

def test_parse__method_types_annotation
Parser.parse_inline_leading_annotation("@rbs (String) -> void", 0...).tap do |annot|
assert_instance_of AST::Ruby::Annotations::MethodTypesAnnotation, annot
assert_equal "@rbs (String) -> void", annot.location.source
assert_equal "@rbs", annot.prefix_location.source
assert_equal 1, annot.overloads.size
annot.overloads[0].tap do |overload|
assert_equal "(String) -> void", overload.method_type.location.source
assert_empty overload.annotations
end
assert_empty annot.vertical_bar_locations
assert_nil annot.dot3_location
end

Parser.parse_inline_leading_annotation("@rbs (String) -> void | (Integer) -> void", 0...).tap do |annot|
assert_instance_of AST::Ruby::Annotations::MethodTypesAnnotation, annot
assert_equal "@rbs (String) -> void | (Integer) -> void", annot.location.source
assert_equal "@rbs", annot.prefix_location.source
assert_equal 2, annot.overloads.size
annot.overloads[0].tap do |overload|
assert_equal "(String) -> void", overload.method_type.location.source
end
annot.overloads[1].tap do |overload|
assert_equal "(Integer) -> void", overload.method_type.location.source
end
assert_equal ["|"], annot.vertical_bar_locations.map(&:source)
assert_nil annot.dot3_location
end
end

def test_parse__method_types_annotation__with_dot3
Parser.parse_inline_leading_annotation("@rbs (Float, Float) -> Float | ...", 0...).tap do |annot|
assert_instance_of AST::Ruby::Annotations::MethodTypesAnnotation, annot
assert_equal "@rbs (Float, Float) -> Float | ...", annot.location.source
assert_equal "@rbs", annot.prefix_location.source
assert_equal 1, annot.overloads.size
annot.overloads[0].tap do |overload|
assert_equal "(Float, Float) -> Float", overload.method_type.location.source
end
assert_equal ["|"], annot.vertical_bar_locations.map(&:source)
assert_equal "...", annot.dot3_location.source
end

Parser.parse_inline_leading_annotation("@rbs () -> void | %a{foo} () -> String | ...", 0...).tap do |annot|
assert_instance_of AST::Ruby::Annotations::MethodTypesAnnotation, annot
assert_equal "@rbs () -> void | %a{foo} () -> String | ...", annot.location.source
assert_equal 2, annot.overloads.size
annot.overloads[0].tap do |overload|
assert_equal "() -> void", overload.method_type.location.source
end
annot.overloads[1].tap do |overload|
assert_equal "() -> String", overload.method_type.location.source
assert_equal ["foo"], overload.annotations.map(&:string)
end
assert_equal ["|", "|"], annot.vertical_bar_locations.map(&:source)
assert_equal "...", annot.dot3_location.source
end
end

def test_parse__method_types_annotation__only_dot3
Parser.parse_inline_leading_annotation("@rbs ...", 0...).tap do |annot|
assert_instance_of AST::Ruby::Annotations::MethodTypesAnnotation, annot
assert_equal "@rbs ...", annot.location.source
assert_equal "@rbs", annot.prefix_location.source
assert_empty annot.overloads
assert_empty annot.vertical_bar_locations
assert_equal "...", annot.dot3_location.source
end
end
end
Loading