Skip to content

Commit 504cae1

Browse files
authored
Merge pull request #2921 from ruby/module-self-annotation
Support `module-self` inline annotation
2 parents 35e03da + 91b331e commit 504cae1

22 files changed

Lines changed: 700 additions & 154 deletions

config.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -784,6 +784,30 @@ nodes:
784784
- name: type_name_location
785785
c_type: rbs_location_range
786786
optional: true
787+
- name: RBS::AST::Ruby::Annotations::ModuleSelfAnnotation
788+
rust_name: ModuleSelfAnnotationNode
789+
fields:
790+
- name: prefix_location
791+
c_type: rbs_location_range
792+
- name: keyword_location
793+
c_type: rbs_location_range
794+
- name: colon_location
795+
c_type: rbs_location_range
796+
- name: name
797+
c_type: rbs_type_name
798+
- name: args
799+
c_type: rbs_node_list
800+
- name: open_bracket_location
801+
c_type: rbs_location_range
802+
optional: true
803+
- name: close_bracket_location
804+
c_type: rbs_location_range
805+
optional: true
806+
- name: args_comma_locations
807+
c_type: rbs_location_range_list
808+
- name: comment_location
809+
c_type: rbs_location_range
810+
optional: true
787811
- name: RBS::AST::Ruby::Annotations::ParamTypeAnnotation
788812
rust_name: ParamTypeAnnotationNode
789813
fields:

docs/inline.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,10 +123,38 @@ end
123123

124124
This creates the types `::API`, `::API::V1`, and `::API::V1::Resources`.
125125

126+
### `module-self` constraint
127+
128+
The `module-self` constraint declares which classes or modules the module can be mixed into.
129+
130+
```ruby
131+
# @rbs module-self: _Each[String]
132+
module Enumerable2
133+
end
134+
```
135+
136+
This is equivalent to `module Enumerable2 : _Each[String]` in RBS, meaning `Enumerable2` can only be included in classes that satisfy the `_Each[String]` interface.
137+
138+
Multiple `module-self` constraints can be declared with separate annotations:
139+
140+
```ruby
141+
# @rbs module-self: _Each[String]
142+
# @rbs module-self: Comparable
143+
module StringCollection
144+
end
145+
```
146+
147+
You can add a description after `--`:
148+
149+
```ruby
150+
# @rbs module-self: Minitest::Test -- depending on assertion methods
151+
module TestHelper
152+
end
153+
```
154+
126155
### Current Limitations
127156

128157
- Generic module definitions are not supported
129-
- Module self-type constraints are not supported
130158

131159
## Method Definitions
132160

ext/rbs_extension/ast_translation.c

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -947,6 +947,27 @@ VALUE rbs_struct_to_ruby_value(rbs_translation_context_t ctx, rbs_node_t *instan
947947
&h
948948
);
949949
}
950+
case RBS_AST_RUBY_ANNOTATIONS_MODULE_SELF_ANNOTATION: {
951+
rbs_ast_ruby_annotations_module_self_annotation_t *node = (rbs_ast_ruby_annotations_module_self_annotation_t *) instance;
952+
953+
VALUE h = rb_hash_new();
954+
rb_hash_aset(h, ID2SYM(rb_intern("location")), rbs_location_range_to_ruby_location(ctx, node->base.location));
955+
rb_hash_aset(h, ID2SYM(rb_intern("prefix_location")), rbs_location_range_to_ruby_location(ctx, node->prefix_location));
956+
rb_hash_aset(h, ID2SYM(rb_intern("keyword_location")), rbs_location_range_to_ruby_location(ctx, node->keyword_location));
957+
rb_hash_aset(h, ID2SYM(rb_intern("colon_location")), rbs_location_range_to_ruby_location(ctx, node->colon_location));
958+
rb_hash_aset(h, ID2SYM(rb_intern("name")), rbs_struct_to_ruby_value(ctx, (rbs_node_t *) node->name)); // rbs_type_name
959+
rb_hash_aset(h, ID2SYM(rb_intern("args")), rbs_node_list_to_ruby_array(ctx, node->args));
960+
rb_hash_aset(h, ID2SYM(rb_intern("open_bracket_location")), rbs_location_range_to_ruby_location(ctx, node->open_bracket_location)); // optional
961+
rb_hash_aset(h, ID2SYM(rb_intern("close_bracket_location")), rbs_location_range_to_ruby_location(ctx, node->close_bracket_location)); // optional
962+
rb_hash_aset(h, ID2SYM(rb_intern("args_comma_locations")), rbs_location_range_list_to_ruby_array(ctx, node->args_comma_locations));
963+
rb_hash_aset(h, ID2SYM(rb_intern("comment_location")), rbs_location_range_to_ruby_location(ctx, node->comment_location)); // optional
964+
965+
return CLASS_NEW_INSTANCE(
966+
RBS_AST_Ruby_Annotations_ModuleSelfAnnotation,
967+
1,
968+
&h
969+
);
970+
}
950971
case RBS_AST_RUBY_ANNOTATIONS_NODE_TYPE_ASSERTION: {
951972
rbs_ast_ruby_annotations_node_type_assertion_t *node = (rbs_ast_ruby_annotations_node_type_assertion_t *) instance;
952973

ext/rbs_extension/class_constants.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ VALUE RBS_AST_Ruby_Annotations_DoubleSplatParamTypeAnnotation;
5454
VALUE RBS_AST_Ruby_Annotations_InstanceVariableAnnotation;
5555
VALUE RBS_AST_Ruby_Annotations_MethodTypesAnnotation;
5656
VALUE RBS_AST_Ruby_Annotations_ModuleAliasAnnotation;
57+
VALUE RBS_AST_Ruby_Annotations_ModuleSelfAnnotation;
5758
VALUE RBS_AST_Ruby_Annotations_NodeTypeAssertion;
5859
VALUE RBS_AST_Ruby_Annotations_ParamTypeAnnotation;
5960
VALUE RBS_AST_Ruby_Annotations_ReturnTypeAnnotation;
@@ -147,6 +148,7 @@ void rbs__init_constants(void) {
147148
IMPORT_CONSTANT(RBS_AST_Ruby_Annotations_InstanceVariableAnnotation, RBS_AST_Ruby_Annotations, "InstanceVariableAnnotation");
148149
IMPORT_CONSTANT(RBS_AST_Ruby_Annotations_MethodTypesAnnotation, RBS_AST_Ruby_Annotations, "MethodTypesAnnotation");
149150
IMPORT_CONSTANT(RBS_AST_Ruby_Annotations_ModuleAliasAnnotation, RBS_AST_Ruby_Annotations, "ModuleAliasAnnotation");
151+
IMPORT_CONSTANT(RBS_AST_Ruby_Annotations_ModuleSelfAnnotation, RBS_AST_Ruby_Annotations, "ModuleSelfAnnotation");
150152
IMPORT_CONSTANT(RBS_AST_Ruby_Annotations_NodeTypeAssertion, RBS_AST_Ruby_Annotations, "NodeTypeAssertion");
151153
IMPORT_CONSTANT(RBS_AST_Ruby_Annotations_ParamTypeAnnotation, RBS_AST_Ruby_Annotations, "ParamTypeAnnotation");
152154
IMPORT_CONSTANT(RBS_AST_Ruby_Annotations_ReturnTypeAnnotation, RBS_AST_Ruby_Annotations, "ReturnTypeAnnotation");

ext/rbs_extension/class_constants.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ extern VALUE RBS_AST_Ruby_Annotations_DoubleSplatParamTypeAnnotation;
6262
extern VALUE RBS_AST_Ruby_Annotations_InstanceVariableAnnotation;
6363
extern VALUE RBS_AST_Ruby_Annotations_MethodTypesAnnotation;
6464
extern VALUE RBS_AST_Ruby_Annotations_ModuleAliasAnnotation;
65+
extern VALUE RBS_AST_Ruby_Annotations_ModuleSelfAnnotation;
6566
extern VALUE RBS_AST_Ruby_Annotations_NodeTypeAssertion;
6667
extern VALUE RBS_AST_Ruby_Annotations_ParamTypeAnnotation;
6768
extern VALUE RBS_AST_Ruby_Annotations_ReturnTypeAnnotation;

include/rbs/ast.h

Lines changed: 54 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -97,44 +97,45 @@ enum rbs_node_type {
9797
RBS_AST_RUBY_ANNOTATIONS_INSTANCE_VARIABLE_ANNOTATION = 36,
9898
RBS_AST_RUBY_ANNOTATIONS_METHOD_TYPES_ANNOTATION = 37,
9999
RBS_AST_RUBY_ANNOTATIONS_MODULE_ALIAS_ANNOTATION = 38,
100-
RBS_AST_RUBY_ANNOTATIONS_NODE_TYPE_ASSERTION = 39,
101-
RBS_AST_RUBY_ANNOTATIONS_PARAM_TYPE_ANNOTATION = 40,
102-
RBS_AST_RUBY_ANNOTATIONS_RETURN_TYPE_ANNOTATION = 41,
103-
RBS_AST_RUBY_ANNOTATIONS_SKIP_ANNOTATION = 42,
104-
RBS_AST_RUBY_ANNOTATIONS_SPLAT_PARAM_TYPE_ANNOTATION = 43,
105-
RBS_AST_RUBY_ANNOTATIONS_TYPE_APPLICATION_ANNOTATION = 44,
106-
RBS_AST_STRING = 45,
107-
RBS_AST_TYPE_PARAM = 46,
108-
RBS_METHOD_TYPE = 47,
109-
RBS_NAMESPACE = 48,
110-
RBS_SIGNATURE = 49,
111-
RBS_TYPE_NAME = 50,
112-
RBS_TYPES_ALIAS = 51,
113-
RBS_TYPES_BASES_ANY = 52,
114-
RBS_TYPES_BASES_BOOL = 53,
115-
RBS_TYPES_BASES_BOTTOM = 54,
116-
RBS_TYPES_BASES_CLASS = 55,
117-
RBS_TYPES_BASES_INSTANCE = 56,
118-
RBS_TYPES_BASES_NIL = 57,
119-
RBS_TYPES_BASES_SELF = 58,
120-
RBS_TYPES_BASES_TOP = 59,
121-
RBS_TYPES_BASES_VOID = 60,
122-
RBS_TYPES_BLOCK = 61,
123-
RBS_TYPES_CLASS_INSTANCE = 62,
124-
RBS_TYPES_CLASS_SINGLETON = 63,
125-
RBS_TYPES_FUNCTION = 64,
126-
RBS_TYPES_FUNCTION_PARAM = 65,
127-
RBS_TYPES_INTERFACE = 66,
128-
RBS_TYPES_INTERSECTION = 67,
129-
RBS_TYPES_LITERAL = 68,
130-
RBS_TYPES_OPTIONAL = 69,
131-
RBS_TYPES_PROC = 70,
132-
RBS_TYPES_RECORD = 71,
133-
RBS_TYPES_RECORD_FIELD_TYPE = 72,
134-
RBS_TYPES_TUPLE = 73,
135-
RBS_TYPES_UNION = 74,
136-
RBS_TYPES_UNTYPED_FUNCTION = 75,
137-
RBS_TYPES_VARIABLE = 76,
100+
RBS_AST_RUBY_ANNOTATIONS_MODULE_SELF_ANNOTATION = 39,
101+
RBS_AST_RUBY_ANNOTATIONS_NODE_TYPE_ASSERTION = 40,
102+
RBS_AST_RUBY_ANNOTATIONS_PARAM_TYPE_ANNOTATION = 41,
103+
RBS_AST_RUBY_ANNOTATIONS_RETURN_TYPE_ANNOTATION = 42,
104+
RBS_AST_RUBY_ANNOTATIONS_SKIP_ANNOTATION = 43,
105+
RBS_AST_RUBY_ANNOTATIONS_SPLAT_PARAM_TYPE_ANNOTATION = 44,
106+
RBS_AST_RUBY_ANNOTATIONS_TYPE_APPLICATION_ANNOTATION = 45,
107+
RBS_AST_STRING = 46,
108+
RBS_AST_TYPE_PARAM = 47,
109+
RBS_METHOD_TYPE = 48,
110+
RBS_NAMESPACE = 49,
111+
RBS_SIGNATURE = 50,
112+
RBS_TYPE_NAME = 51,
113+
RBS_TYPES_ALIAS = 52,
114+
RBS_TYPES_BASES_ANY = 53,
115+
RBS_TYPES_BASES_BOOL = 54,
116+
RBS_TYPES_BASES_BOTTOM = 55,
117+
RBS_TYPES_BASES_CLASS = 56,
118+
RBS_TYPES_BASES_INSTANCE = 57,
119+
RBS_TYPES_BASES_NIL = 58,
120+
RBS_TYPES_BASES_SELF = 59,
121+
RBS_TYPES_BASES_TOP = 60,
122+
RBS_TYPES_BASES_VOID = 61,
123+
RBS_TYPES_BLOCK = 62,
124+
RBS_TYPES_CLASS_INSTANCE = 63,
125+
RBS_TYPES_CLASS_SINGLETON = 64,
126+
RBS_TYPES_FUNCTION = 65,
127+
RBS_TYPES_FUNCTION_PARAM = 66,
128+
RBS_TYPES_INTERFACE = 67,
129+
RBS_TYPES_INTERSECTION = 68,
130+
RBS_TYPES_LITERAL = 69,
131+
RBS_TYPES_OPTIONAL = 70,
132+
RBS_TYPES_PROC = 71,
133+
RBS_TYPES_RECORD = 72,
134+
RBS_TYPES_RECORD_FIELD_TYPE = 73,
135+
RBS_TYPES_TUPLE = 74,
136+
RBS_TYPES_UNION = 75,
137+
RBS_TYPES_UNTYPED_FUNCTION = 76,
138+
RBS_TYPES_VARIABLE = 77,
138139
RBS_AST_SYMBOL,
139140
};
140141

@@ -633,6 +634,20 @@ typedef struct rbs_ast_ruby_annotations_module_alias_annotation {
633634
rbs_location_range type_name_location; /* Optional */
634635
} rbs_ast_ruby_annotations_module_alias_annotation_t;
635636

637+
typedef struct rbs_ast_ruby_annotations_module_self_annotation {
638+
rbs_node_t base;
639+
640+
rbs_location_range prefix_location;
641+
rbs_location_range keyword_location;
642+
rbs_location_range colon_location;
643+
struct rbs_type_name *name;
644+
struct rbs_node_list *args;
645+
rbs_location_range open_bracket_location; /* Optional */
646+
rbs_location_range close_bracket_location; /* Optional */
647+
rbs_location_range_list_t *args_comma_locations;
648+
rbs_location_range comment_location; /* Optional */
649+
} rbs_ast_ruby_annotations_module_self_annotation_t;
650+
636651
typedef struct rbs_ast_ruby_annotations_node_type_assertion {
637652
rbs_node_t base;
638653

@@ -980,6 +995,7 @@ rbs_ast_ruby_annotations_double_splat_param_type_annotation_t *rbs_ast_ruby_anno
980995
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);
981996
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);
982997
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);
998+
rbs_ast_ruby_annotations_module_self_annotation_t *rbs_ast_ruby_annotations_module_self_annotation_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_location_range prefix_location, rbs_location_range keyword_location, rbs_location_range colon_location, rbs_type_name_t *name, rbs_node_list_t *args, rbs_location_range open_bracket_location, rbs_location_range close_bracket_location, rbs_location_range_list_t *args_comma_locations, rbs_location_range comment_location);
983999
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);
9841000
rbs_ast_ruby_annotations_param_type_annotation_t *rbs_ast_ruby_annotations_param_type_annotation_new(rbs_allocator_t *allocator, rbs_location_range location, rbs_location_range prefix_location, rbs_location_range name_location, rbs_location_range colon_location, rbs_node_t *param_type, rbs_location_range comment_location);
9851001
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);

include/rbs/lexer.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ enum RBSTokenType {
7070
kATRBS, /* @rbs */
7171
kSKIP, /* skip */
7272
kRETURN, /* return */
73+
kMODULESELF, /* module-self */
7374

7475
tLIDENT, /* Identifiers starting with lower case */
7576
tUIDENT, /* Identifiers starting with upper case */

lib/rbs/ast/ruby/annotations.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,48 @@ def type_fingerprint
353353
end
354354
end
355355

356+
class ModuleSelfAnnotation < Base
357+
attr_reader :keyword_location, :colon_location, :name, :args, :open_bracket_location, :close_bracket_location, :args_comma_locations, :comment_location
358+
359+
def initialize(location:, prefix_location:, keyword_location:, colon_location:, name:, args:, open_bracket_location:, close_bracket_location:, args_comma_locations:, comment_location:)
360+
super(location, prefix_location)
361+
@keyword_location = keyword_location
362+
@colon_location = colon_location
363+
@name = name
364+
@args = args
365+
@open_bracket_location = open_bracket_location
366+
@close_bracket_location = close_bracket_location
367+
@args_comma_locations = args_comma_locations
368+
@comment_location = comment_location
369+
end
370+
371+
def map_type_name
372+
mapped_args = args.map { |type| type.map_type_name { yield _1 } }
373+
374+
self.class.new(
375+
location:,
376+
prefix_location:,
377+
keyword_location:,
378+
colon_location:,
379+
name: yield(name),
380+
args: mapped_args,
381+
open_bracket_location:,
382+
close_bracket_location:,
383+
args_comma_locations:,
384+
comment_location:
385+
) #: self
386+
end
387+
388+
def type_fingerprint
389+
[
390+
"annots/module_self",
391+
name.to_s,
392+
args.map(&:to_s),
393+
comment_location&.source
394+
]
395+
end
396+
end
397+
356398
class BlockParamTypeAnnotation < Base
357399
attr_reader :ampersand_location, :name_location, :colon_location, :question_location, :type_location, :type, :comment_location
358400

lib/rbs/ast/ruby/declarations.rb

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,17 @@ def each_decl(&block)
137137

138138
def type_params = []
139139

140-
def self_types = []
140+
def self_types
141+
members.filter_map do |member|
142+
if member.is_a?(Members::ModuleSelfMember)
143+
AST::Declarations::Module::Self.new(
144+
name: member.name,
145+
args: member.args,
146+
location: member.location
147+
)
148+
end
149+
end
150+
end
141151

142152
def location
143153
rbs_location(node.location)

lib/rbs/ast/ruby/members.rb

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,34 @@ def type_fingerprint
717717
]
718718
end
719719
end
720+
721+
class ModuleSelfMember < Base
722+
attr_reader :annotation
723+
724+
def initialize(buffer, annotation)
725+
super(buffer)
726+
@annotation = annotation
727+
end
728+
729+
def name
730+
annotation.name
731+
end
732+
733+
def args
734+
annotation.args
735+
end
736+
737+
def location
738+
annotation.location
739+
end
740+
741+
def type_fingerprint
742+
[
743+
"members/module_self",
744+
annotation.type_fingerprint
745+
]
746+
end
747+
end
720748
end
721749
end
722750
end

0 commit comments

Comments
 (0)