Skip to content

Commit 8a03854

Browse files
authored
Merge pull request #2614 from ruby/inline--mixin
Implement mixin in inline RBS declarations
2 parents c214951 + e746976 commit 8a03854

26 files changed

+1052
-48
lines changed

Gemfile.lock

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ GEM
4343
drb (2.2.3)
4444
erb (5.0.2)
4545
extconf_compile_commands_json (0.0.7)
46-
ffi (1.17.2)
46+
ffi (1.17.2-aarch64-linux-gnu)
4747
ffi (1.17.2-arm64-darwin)
4848
ffi (1.17.2-x86_64-linux-gnu)
4949
fileutils (1.7.3)
@@ -73,6 +73,8 @@ GEM
7373
net-smtp (0.5.1)
7474
net-protocol
7575
nkf (0.2.0)
76+
nokogiri (1.18.8-aarch64-linux-gnu)
77+
racc (~> 1.4)
7678
nokogiri (1.18.8-arm64-darwin)
7779
racc (~> 1.4)
7880
nokogiri (1.18.8-x86_64-linux-gnu)
@@ -180,6 +182,7 @@ GEM
180182
zlib (3.2.1)
181183

182184
PLATFORMS
185+
aarch64-linux
183186
arm64-darwin
184187
x86_64-linux
185188

config.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,3 +487,13 @@ nodes:
487487
c_type: rbs_node
488488
- name: comment_location
489489
c_type: rbs_location
490+
- name: RBS::AST::Ruby::Annotations::TypeApplicationAnnotation
491+
fields:
492+
- name: prefix_location
493+
c_type: rbs_location
494+
- name: type_args
495+
c_type: rbs_node_list
496+
- name: close_bracket_location
497+
c_type: rbs_location
498+
- name: comma_locations
499+
c_type: rbs_location_list

ext/rbs_extension/ast_translation.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -649,6 +649,22 @@ VALUE rbs_struct_to_ruby_value(rbs_translation_context_t ctx, rbs_node_t *instan
649649
&h
650650
);
651651
}
652+
case RBS_AST_RUBY_ANNOTATIONS_TYPE_APPLICATION_ANNOTATION: {
653+
rbs_ast_ruby_annotations_type_application_annotation_t *node = (rbs_ast_ruby_annotations_type_application_annotation_t *) instance;
654+
655+
VALUE h = rb_hash_new();
656+
rb_hash_aset(h, ID2SYM(rb_intern("location")), rbs_loc_to_ruby_location(ctx, node->base.location));
657+
rb_hash_aset(h, ID2SYM(rb_intern("prefix_location")), rbs_loc_to_ruby_location(ctx, node->prefix_location));
658+
rb_hash_aset(h, ID2SYM(rb_intern("type_args")), rbs_node_list_to_ruby_array(ctx, node->type_args));
659+
rb_hash_aset(h, ID2SYM(rb_intern("close_bracket_location")), rbs_loc_to_ruby_location(ctx, node->close_bracket_location));
660+
rb_hash_aset(h, ID2SYM(rb_intern("comma_locations")), rbs_location_list_to_ruby_array(ctx, node->comma_locations));
661+
662+
return CLASS_NEW_INSTANCE(
663+
RBS_AST_Ruby_Annotations_TypeApplicationAnnotation,
664+
1,
665+
&h
666+
);
667+
}
652668
case RBS_AST_STRING: {
653669
rbs_ast_string_t *string_node = (rbs_ast_string_t *) instance;
654670
rbs_string_t s = string_node->string;

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_MethodTypesAnnotation;
5454
VALUE RBS_AST_Ruby_Annotations_NodeTypeAssertion;
5555
VALUE RBS_AST_Ruby_Annotations_ReturnTypeAnnotation;
5656
VALUE RBS_AST_Ruby_Annotations_SkipAnnotation;
57+
VALUE RBS_AST_Ruby_Annotations_TypeApplicationAnnotation;
5758
VALUE RBS_AST_TypeParam;
5859
VALUE RBS_MethodType;
5960
VALUE RBS_Namespace;
@@ -139,6 +140,7 @@ void rbs__init_constants(void) {
139140
IMPORT_CONSTANT(RBS_AST_Ruby_Annotations_NodeTypeAssertion, RBS_AST_Ruby_Annotations, "NodeTypeAssertion");
140141
IMPORT_CONSTANT(RBS_AST_Ruby_Annotations_ReturnTypeAnnotation, RBS_AST_Ruby_Annotations, "ReturnTypeAnnotation");
141142
IMPORT_CONSTANT(RBS_AST_Ruby_Annotations_SkipAnnotation, RBS_AST_Ruby_Annotations, "SkipAnnotation");
143+
IMPORT_CONSTANT(RBS_AST_Ruby_Annotations_TypeApplicationAnnotation, RBS_AST_Ruby_Annotations, "TypeApplicationAnnotation");
142144
IMPORT_CONSTANT(RBS_AST_TypeParam, RBS_AST, "TypeParam");
143145
IMPORT_CONSTANT(RBS_MethodType, RBS, "MethodType");
144146
IMPORT_CONSTANT(RBS_Namespace, RBS, "Namespace");

ext/rbs_extension/class_constants.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ extern VALUE RBS_AST_Ruby_Annotations_MethodTypesAnnotation;
6060
extern VALUE RBS_AST_Ruby_Annotations_NodeTypeAssertion;
6161
extern VALUE RBS_AST_Ruby_Annotations_ReturnTypeAnnotation;
6262
extern VALUE RBS_AST_Ruby_Annotations_SkipAnnotation;
63+
extern VALUE RBS_AST_Ruby_Annotations_TypeApplicationAnnotation;
6364
extern VALUE RBS_AST_TypeParam;
6465
extern VALUE RBS_MethodType;
6566
extern VALUE RBS_Namespace;

include/rbs/ast.h

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -50,38 +50,39 @@ enum rbs_node_type {
5050
RBS_AST_RUBY_ANNOTATIONS_NODE_TYPE_ASSERTION = 34,
5151
RBS_AST_RUBY_ANNOTATIONS_RETURN_TYPE_ANNOTATION = 35,
5252
RBS_AST_RUBY_ANNOTATIONS_SKIP_ANNOTATION = 36,
53-
RBS_AST_STRING = 37,
54-
RBS_AST_TYPE_PARAM = 38,
55-
RBS_METHOD_TYPE = 39,
56-
RBS_NAMESPACE = 40,
57-
RBS_SIGNATURE = 41,
58-
RBS_TYPE_NAME = 42,
59-
RBS_TYPES_ALIAS = 43,
60-
RBS_TYPES_BASES_ANY = 44,
61-
RBS_TYPES_BASES_BOOL = 45,
62-
RBS_TYPES_BASES_BOTTOM = 46,
63-
RBS_TYPES_BASES_CLASS = 47,
64-
RBS_TYPES_BASES_INSTANCE = 48,
65-
RBS_TYPES_BASES_NIL = 49,
66-
RBS_TYPES_BASES_SELF = 50,
67-
RBS_TYPES_BASES_TOP = 51,
68-
RBS_TYPES_BASES_VOID = 52,
69-
RBS_TYPES_BLOCK = 53,
70-
RBS_TYPES_CLASS_INSTANCE = 54,
71-
RBS_TYPES_CLASS_SINGLETON = 55,
72-
RBS_TYPES_FUNCTION = 56,
73-
RBS_TYPES_FUNCTION_PARAM = 57,
74-
RBS_TYPES_INTERFACE = 58,
75-
RBS_TYPES_INTERSECTION = 59,
76-
RBS_TYPES_LITERAL = 60,
77-
RBS_TYPES_OPTIONAL = 61,
78-
RBS_TYPES_PROC = 62,
79-
RBS_TYPES_RECORD = 63,
80-
RBS_TYPES_RECORD_FIELD_TYPE = 64,
81-
RBS_TYPES_TUPLE = 65,
82-
RBS_TYPES_UNION = 66,
83-
RBS_TYPES_UNTYPED_FUNCTION = 67,
84-
RBS_TYPES_VARIABLE = 68,
53+
RBS_AST_RUBY_ANNOTATIONS_TYPE_APPLICATION_ANNOTATION = 37,
54+
RBS_AST_STRING = 38,
55+
RBS_AST_TYPE_PARAM = 39,
56+
RBS_METHOD_TYPE = 40,
57+
RBS_NAMESPACE = 41,
58+
RBS_SIGNATURE = 42,
59+
RBS_TYPE_NAME = 43,
60+
RBS_TYPES_ALIAS = 44,
61+
RBS_TYPES_BASES_ANY = 45,
62+
RBS_TYPES_BASES_BOOL = 46,
63+
RBS_TYPES_BASES_BOTTOM = 47,
64+
RBS_TYPES_BASES_CLASS = 48,
65+
RBS_TYPES_BASES_INSTANCE = 49,
66+
RBS_TYPES_BASES_NIL = 50,
67+
RBS_TYPES_BASES_SELF = 51,
68+
RBS_TYPES_BASES_TOP = 52,
69+
RBS_TYPES_BASES_VOID = 53,
70+
RBS_TYPES_BLOCK = 54,
71+
RBS_TYPES_CLASS_INSTANCE = 55,
72+
RBS_TYPES_CLASS_SINGLETON = 56,
73+
RBS_TYPES_FUNCTION = 57,
74+
RBS_TYPES_FUNCTION_PARAM = 58,
75+
RBS_TYPES_INTERFACE = 59,
76+
RBS_TYPES_INTERSECTION = 60,
77+
RBS_TYPES_LITERAL = 61,
78+
RBS_TYPES_OPTIONAL = 62,
79+
RBS_TYPES_PROC = 63,
80+
RBS_TYPES_RECORD = 64,
81+
RBS_TYPES_RECORD_FIELD_TYPE = 65,
82+
RBS_TYPES_TUPLE = 66,
83+
RBS_TYPES_UNION = 67,
84+
RBS_TYPES_UNTYPED_FUNCTION = 68,
85+
RBS_TYPES_VARIABLE = 69,
8586
RBS_KEYWORD,
8687
RBS_AST_SYMBOL,
8788
};
@@ -438,6 +439,15 @@ typedef struct rbs_ast_ruby_annotations_skip_annotation {
438439
struct rbs_location *comment_location;
439440
} rbs_ast_ruby_annotations_skip_annotation_t;
440441

442+
typedef struct rbs_ast_ruby_annotations_type_application_annotation {
443+
rbs_node_t base;
444+
445+
struct rbs_location *prefix_location;
446+
struct rbs_node_list *type_args;
447+
struct rbs_location *close_bracket_location;
448+
struct rbs_location_list *comma_locations;
449+
} rbs_ast_ruby_annotations_type_application_annotation_t;
450+
441451
typedef struct rbs_ast_string {
442452
rbs_node_t base;
443453

@@ -712,6 +722,7 @@ rbs_ast_ruby_annotations_method_types_annotation_t *rbs_ast_ruby_annotations_met
712722
rbs_ast_ruby_annotations_node_type_assertion_t *rbs_ast_ruby_annotations_node_type_assertion_new(rbs_allocator_t *allocator, rbs_location_t *location, rbs_location_t *prefix_location, rbs_node_t *type);
713723
rbs_ast_ruby_annotations_return_type_annotation_t *rbs_ast_ruby_annotations_return_type_annotation_new(rbs_allocator_t *allocator, rbs_location_t *location, rbs_location_t *prefix_location, rbs_location_t *return_location, rbs_location_t *colon_location, rbs_node_t *return_type, rbs_location_t *comment_location);
714724
rbs_ast_ruby_annotations_skip_annotation_t *rbs_ast_ruby_annotations_skip_annotation_new(rbs_allocator_t *allocator, rbs_location_t *location, rbs_location_t *prefix_location, rbs_location_t *skip_location, rbs_location_t *comment_location);
725+
rbs_ast_ruby_annotations_type_application_annotation_t *rbs_ast_ruby_annotations_type_application_annotation_new(rbs_allocator_t *allocator, rbs_location_t *location, rbs_location_t *prefix_location, rbs_node_list_t *type_args, rbs_location_t *close_bracket_location, rbs_location_list_t *comma_locations);
715726
rbs_ast_string_t *rbs_ast_string_new(rbs_allocator_t *allocator, rbs_location_t *location, rbs_string_t string);
716727
rbs_ast_type_param_t *rbs_ast_type_param_new(rbs_allocator_t *allocator, rbs_location_t *location, rbs_ast_symbol_t *name, rbs_keyword_t *variance, rbs_node_t *upper_bound, rbs_node_t *lower_bound, rbs_node_t *default_type, bool unchecked);
717728
rbs_method_type_t *rbs_method_type_new(rbs_allocator_t *allocator, rbs_location_t *location, rbs_node_list_t *type_params, rbs_node_t *type, rbs_types_block_t *block);

lib/rbs/ast/ruby/annotations.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,29 @@ def map_type_name(&block)
113113
) #: self
114114
end
115115
end
116+
117+
class TypeApplicationAnnotation < Base
118+
attr_reader :type_args, :close_bracket_location, :comma_locations
119+
120+
def initialize(location:, prefix_location:, type_args:, close_bracket_location:, comma_locations:)
121+
super(location, prefix_location)
122+
@type_args = type_args
123+
@close_bracket_location = close_bracket_location
124+
@comma_locations = comma_locations
125+
end
126+
127+
def map_type_name(&block)
128+
mapped_type_args = type_args.map { |type| type.map_type_name { yield _1 } }
129+
130+
self.class.new(
131+
location:,
132+
prefix_location:,
133+
type_args: mapped_type_args,
134+
close_bracket_location:,
135+
comma_locations:
136+
) #: self
137+
end
138+
end
116139
end
117140
end
118141
end

lib/rbs/ast/ruby/members.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,43 @@ def name_location
211211
rbs_location(node.name_loc)
212212
end
213213
end
214+
215+
class MixinMember < Base
216+
attr_reader :node
217+
attr_reader :module_name
218+
attr_reader :annotation
219+
220+
def initialize(buffer, node, module_name, annotation)
221+
super(buffer)
222+
@node = node
223+
@module_name = module_name
224+
@annotation = annotation
225+
end
226+
227+
def location
228+
rbs_location(node.location)
229+
end
230+
231+
def name_location
232+
args = node.arguments or raise
233+
first_arg = args.arguments.first or raise
234+
235+
rbs_location(first_arg.location)
236+
end
237+
238+
def type_args
239+
annotation&.type_args || []
240+
end
241+
end
242+
243+
class IncludeMember < MixinMember
244+
end
245+
246+
class ExtendMember < MixinMember
247+
end
248+
249+
class PrependMember < MixinMember
250+
end
214251
end
215252
end
216253
end

lib/rbs/definition_builder.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -606,7 +606,7 @@ def import_methods(definition, module_name, module_methods, interfaces_methods,
606606

607607
methods.each do |method|
608608
if interface_method_duplicates.include?(method.name)
609-
member.is_a?(AST::Members::Include) || member.is_a?(AST::Members::Extend) or raise
609+
(member.is_a?(AST::Members::Include) || member.is_a?(AST::Members::Extend)) or raise
610610

611611
raise DuplicatedInterfaceMethodDefinitionError.new(
612612
type: definition.self_type,

lib/rbs/definition_builder/ancestor_builder.rb

Lines changed: 57 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -414,7 +414,60 @@ def mixin_ancestors0(decl, type_name, align_params:, included_modules:, included
414414
end
415415
end
416416
when AST::Ruby::Declarations::Base
417-
# noop
417+
decl.members.each do |member|
418+
case member
419+
when AST::Ruby::Members::IncludeMember
420+
if included_modules
421+
module_name = member.module_name
422+
module_args = member.type_args
423+
424+
# Check if mixing in a class (not allowed)
425+
if env.class_decl?(module_name)
426+
raise MixinClassError.new(type_name: type_name, member: member)
427+
end
428+
429+
# Check if module exists
430+
module_decl = env.normalized_module_entry(module_name) or raise NoMixinFoundError.new(type_name: module_name, member: member)
431+
module_args = AST::TypeParam.normalize_args(module_decl.type_params, module_args)
432+
module_name = env.normalize_module_name(module_name)
433+
included_modules << Definition::Ancestor::Instance.new(name: module_name, args: module_args, source: member)
434+
end
435+
436+
when AST::Ruby::Members::ExtendMember
437+
if extended_modules
438+
module_name = member.module_name
439+
module_args = member.type_args
440+
441+
# Check if mixing in a class (not allowed)
442+
if env.class_decl?(module_name)
443+
raise MixinClassError.new(type_name: type_name, member: member)
444+
end
445+
446+
# Check if module exists
447+
module_decl = env.normalized_module_entry(module_name) or raise NoMixinFoundError.new(type_name: module_name, member: member)
448+
module_args = AST::TypeParam.normalize_args(module_decl.type_params, module_args)
449+
module_name = env.normalize_module_name(module_name)
450+
extended_modules << Definition::Ancestor::Instance.new(name: module_name, args: module_args, source: member)
451+
end
452+
453+
when AST::Ruby::Members::PrependMember
454+
if prepended_modules
455+
module_name = member.module_name
456+
module_args = member.type_args
457+
458+
# Check if mixing in a class (not allowed)
459+
if env.class_decl?(module_name)
460+
raise MixinClassError.new(type_name: type_name, member: member)
461+
end
462+
463+
# Check if module exists
464+
module_decl = env.normalized_module_entry(module_name) or raise NoMixinFoundError.new(type_name: module_name, member: member)
465+
module_args = AST::TypeParam.normalize_args(module_decl.type_params, module_args)
466+
module_name = env.normalize_module_name(module_name)
467+
prepended_modules << Definition::Ancestor::Instance.new(name: module_name, args: module_args, source: member)
468+
end
469+
end
470+
end
418471
end
419472
end
420473

@@ -484,7 +537,7 @@ def instance_ancestors(type_name, building_ancestors: [])
484537
included_modules.each do |mod|
485538
name = mod.name
486539
arg_types = mod.args
487-
mod.source.is_a?(AST::Members::Include) or raise
540+
(mod.source.is_a?(AST::Members::Include) || mod.source.is_a?(AST::Ruby::Members::IncludeMember)) or raise
488541
mod_ancestors =
489542
instance_ancestors(name, building_ancestors: building_ancestors)
490543
.apply(arg_types, env: env, location: mod.source.location)
@@ -499,7 +552,7 @@ def instance_ancestors(type_name, building_ancestors: [])
499552
prepended_modules.each do |mod|
500553
name = mod.name
501554
arg_types = mod.args
502-
mod.source.is_a?(AST::Members::Prepend) or raise
555+
(mod.source.is_a?(AST::Members::Prepend) || mod.source.is_a?(AST::Ruby::Members::PrependMember)) or raise
503556
mod_ancestors =
504557
instance_ancestors(name, building_ancestors: building_ancestors)
505558
.apply(arg_types, env: env, location: mod.source.location)
@@ -554,7 +607,7 @@ def singleton_ancestors(type_name, building_ancestors: [])
554607
extended_modules.each do |mod|
555608
name = mod.name
556609
args = mod.args
557-
mod.source.is_a?(AST::Members::Extend) or raise
610+
(mod.source.is_a?(AST::Members::Extend) || mod.source.is_a?(AST::Ruby::Members::ExtendMember)) or raise
558611
mod_ancestors =
559612
instance_ancestors(name, building_ancestors: building_ancestors)
560613
.apply(args, env: env, location: mod.source.location)

0 commit comments

Comments
 (0)