Skip to content

Commit e59a2ce

Browse files
authored
Merge pull request #2632 from ruby/inline—instance-variables
Add instance variable declaration annotation
2 parents fa15a75 + 3f4a14d commit e59a2ce

20 files changed

+610
-52
lines changed

config.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -497,3 +497,17 @@ nodes:
497497
c_type: rbs_location
498498
- name: comma_locations
499499
c_type: rbs_location_list
500+
- name: RBS::AST::Ruby::Annotations::InstanceVariableAnnotation
501+
fields:
502+
- name: prefix_location
503+
c_type: rbs_location
504+
- name: ivar_name
505+
c_type: rbs_ast_symbol
506+
- name: ivar_name_location
507+
c_type: rbs_location
508+
- name: colon_location
509+
c_type: rbs_location
510+
- name: type
511+
c_type: rbs_node
512+
- name: comment_location
513+
c_type: rbs_location

docs/inline.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -360,3 +360,31 @@ Modules accessible through ancestors (super-class/included modules) are not supp
360360

361361
- Only single module arguments are supported (no `include A, B` syntax)
362362
- Module names must be constants
363+
364+
## Instance Variables
365+
366+
Inline RBS declaration allows defining instance variables.
367+
368+
```ruby
369+
class Person
370+
# @rbs @name: String
371+
# @rbs @age: Integer? --
372+
# how old is the person?
373+
# `nil` means it's unspecified.
374+
375+
# @rbs (String name, Integer? age) -> void
376+
def initialize(name, age)
377+
@name = name
378+
@age = age
379+
end
380+
end
381+
```
382+
383+
The `@rbs @VAR-NAME: TYPE` syntax enclosed in `class`/`module` syntax declares instance variables.
384+
You can add the documentation of the variable followed by two hyphones (`--`).
385+
386+
Instance variable declarations must be under the `class`/`module` syntax, and they are ignored if written inside method definitions.
387+
388+
### Current Limitations
389+
390+
- Only instance variables of class/module instances are allowed

ext/rbs_extension/ast_translation.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,24 @@ VALUE rbs_struct_to_ruby_value(rbs_translation_context_t ctx, rbs_node_t *instan
588588
&h
589589
);
590590
}
591+
case RBS_AST_RUBY_ANNOTATIONS_INSTANCE_VARIABLE_ANNOTATION: {
592+
rbs_ast_ruby_annotations_instance_variable_annotation_t *node = (rbs_ast_ruby_annotations_instance_variable_annotation_t *) instance;
593+
594+
VALUE h = rb_hash_new();
595+
rb_hash_aset(h, ID2SYM(rb_intern("location")), rbs_loc_to_ruby_location(ctx, node->base.location));
596+
rb_hash_aset(h, ID2SYM(rb_intern("prefix_location")), rbs_loc_to_ruby_location(ctx, node->prefix_location));
597+
rb_hash_aset(h, ID2SYM(rb_intern("ivar_name")), rbs_struct_to_ruby_value(ctx, (rbs_node_t *) node->ivar_name)); // rbs_ast_symbol
598+
rb_hash_aset(h, ID2SYM(rb_intern("ivar_name_location")), rbs_loc_to_ruby_location(ctx, node->ivar_name_location));
599+
rb_hash_aset(h, ID2SYM(rb_intern("colon_location")), rbs_loc_to_ruby_location(ctx, node->colon_location));
600+
rb_hash_aset(h, ID2SYM(rb_intern("type")), rbs_struct_to_ruby_value(ctx, (rbs_node_t *) node->type)); // rbs_node
601+
rb_hash_aset(h, ID2SYM(rb_intern("comment_location")), rbs_loc_to_ruby_location(ctx, node->comment_location));
602+
603+
return CLASS_NEW_INSTANCE(
604+
RBS_AST_Ruby_Annotations_InstanceVariableAnnotation,
605+
1,
606+
&h
607+
);
608+
}
591609
case RBS_AST_RUBY_ANNOTATIONS_METHOD_TYPES_ANNOTATION: {
592610
rbs_ast_ruby_annotations_method_types_annotation_t *node = (rbs_ast_ruby_annotations_method_types_annotation_t *) instance;
593611

ext/rbs_extension/class_constants.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ VALUE RBS_AST_Members_Prepend;
5050
VALUE RBS_AST_Members_Private;
5151
VALUE RBS_AST_Members_Public;
5252
VALUE RBS_AST_Ruby_Annotations_ColonMethodTypeAnnotation;
53+
VALUE RBS_AST_Ruby_Annotations_InstanceVariableAnnotation;
5354
VALUE RBS_AST_Ruby_Annotations_MethodTypesAnnotation;
5455
VALUE RBS_AST_Ruby_Annotations_NodeTypeAssertion;
5556
VALUE RBS_AST_Ruby_Annotations_ReturnTypeAnnotation;
@@ -136,6 +137,7 @@ void rbs__init_constants(void) {
136137
IMPORT_CONSTANT(RBS_AST_Members_Private, RBS_AST_Members, "Private");
137138
IMPORT_CONSTANT(RBS_AST_Members_Public, RBS_AST_Members, "Public");
138139
IMPORT_CONSTANT(RBS_AST_Ruby_Annotations_ColonMethodTypeAnnotation, RBS_AST_Ruby_Annotations, "ColonMethodTypeAnnotation");
140+
IMPORT_CONSTANT(RBS_AST_Ruby_Annotations_InstanceVariableAnnotation, RBS_AST_Ruby_Annotations, "InstanceVariableAnnotation");
139141
IMPORT_CONSTANT(RBS_AST_Ruby_Annotations_MethodTypesAnnotation, RBS_AST_Ruby_Annotations, "MethodTypesAnnotation");
140142
IMPORT_CONSTANT(RBS_AST_Ruby_Annotations_NodeTypeAssertion, RBS_AST_Ruby_Annotations, "NodeTypeAssertion");
141143
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
@@ -56,6 +56,7 @@ extern VALUE RBS_AST_Members_Prepend;
5656
extern VALUE RBS_AST_Members_Private;
5757
extern VALUE RBS_AST_Members_Public;
5858
extern VALUE RBS_AST_Ruby_Annotations_ColonMethodTypeAnnotation;
59+
extern VALUE RBS_AST_Ruby_Annotations_InstanceVariableAnnotation;
5960
extern VALUE RBS_AST_Ruby_Annotations_MethodTypesAnnotation;
6061
extern VALUE RBS_AST_Ruby_Annotations_NodeTypeAssertion;
6162
extern VALUE RBS_AST_Ruby_Annotations_ReturnTypeAnnotation;

include/rbs/ast.h

Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -46,43 +46,44 @@ enum rbs_node_type {
4646
RBS_AST_MEMBERS_PRIVATE = 30,
4747
RBS_AST_MEMBERS_PUBLIC = 31,
4848
RBS_AST_RUBY_ANNOTATIONS_COLON_METHOD_TYPE_ANNOTATION = 32,
49-
RBS_AST_RUBY_ANNOTATIONS_METHOD_TYPES_ANNOTATION = 33,
50-
RBS_AST_RUBY_ANNOTATIONS_NODE_TYPE_ASSERTION = 34,
51-
RBS_AST_RUBY_ANNOTATIONS_RETURN_TYPE_ANNOTATION = 35,
52-
RBS_AST_RUBY_ANNOTATIONS_SKIP_ANNOTATION = 36,
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,
49+
RBS_AST_RUBY_ANNOTATIONS_INSTANCE_VARIABLE_ANNOTATION = 33,
50+
RBS_AST_RUBY_ANNOTATIONS_METHOD_TYPES_ANNOTATION = 34,
51+
RBS_AST_RUBY_ANNOTATIONS_NODE_TYPE_ASSERTION = 35,
52+
RBS_AST_RUBY_ANNOTATIONS_RETURN_TYPE_ANNOTATION = 36,
53+
RBS_AST_RUBY_ANNOTATIONS_SKIP_ANNOTATION = 37,
54+
RBS_AST_RUBY_ANNOTATIONS_TYPE_APPLICATION_ANNOTATION = 38,
55+
RBS_AST_STRING = 39,
56+
RBS_AST_TYPE_PARAM = 40,
57+
RBS_METHOD_TYPE = 41,
58+
RBS_NAMESPACE = 42,
59+
RBS_SIGNATURE = 43,
60+
RBS_TYPE_NAME = 44,
61+
RBS_TYPES_ALIAS = 45,
62+
RBS_TYPES_BASES_ANY = 46,
63+
RBS_TYPES_BASES_BOOL = 47,
64+
RBS_TYPES_BASES_BOTTOM = 48,
65+
RBS_TYPES_BASES_CLASS = 49,
66+
RBS_TYPES_BASES_INSTANCE = 50,
67+
RBS_TYPES_BASES_NIL = 51,
68+
RBS_TYPES_BASES_SELF = 52,
69+
RBS_TYPES_BASES_TOP = 53,
70+
RBS_TYPES_BASES_VOID = 54,
71+
RBS_TYPES_BLOCK = 55,
72+
RBS_TYPES_CLASS_INSTANCE = 56,
73+
RBS_TYPES_CLASS_SINGLETON = 57,
74+
RBS_TYPES_FUNCTION = 58,
75+
RBS_TYPES_FUNCTION_PARAM = 59,
76+
RBS_TYPES_INTERFACE = 60,
77+
RBS_TYPES_INTERSECTION = 61,
78+
RBS_TYPES_LITERAL = 62,
79+
RBS_TYPES_OPTIONAL = 63,
80+
RBS_TYPES_PROC = 64,
81+
RBS_TYPES_RECORD = 65,
82+
RBS_TYPES_RECORD_FIELD_TYPE = 66,
83+
RBS_TYPES_TUPLE = 67,
84+
RBS_TYPES_UNION = 68,
85+
RBS_TYPES_UNTYPED_FUNCTION = 69,
86+
RBS_TYPES_VARIABLE = 70,
8687
RBS_KEYWORD,
8788
RBS_AST_SYMBOL,
8889
};
@@ -406,6 +407,17 @@ typedef struct rbs_ast_ruby_annotations_colon_method_type_annotation {
406407
struct rbs_node *method_type;
407408
} rbs_ast_ruby_annotations_colon_method_type_annotation_t;
408409

410+
typedef struct rbs_ast_ruby_annotations_instance_variable_annotation {
411+
rbs_node_t base;
412+
413+
struct rbs_location *prefix_location;
414+
struct rbs_ast_symbol *ivar_name;
415+
struct rbs_location *ivar_name_location;
416+
struct rbs_location *colon_location;
417+
struct rbs_node *type;
418+
struct rbs_location *comment_location;
419+
} rbs_ast_ruby_annotations_instance_variable_annotation_t;
420+
409421
typedef struct rbs_ast_ruby_annotations_method_types_annotation {
410422
rbs_node_t base;
411423

@@ -718,6 +730,7 @@ rbs_ast_members_prepend_t *rbs_ast_members_prepend_new(rbs_allocator_t *allocato
718730
rbs_ast_members_private_t *rbs_ast_members_private_new(rbs_allocator_t *allocator, rbs_location_t *location);
719731
rbs_ast_members_public_t *rbs_ast_members_public_new(rbs_allocator_t *allocator, rbs_location_t *location);
720732
rbs_ast_ruby_annotations_colon_method_type_annotation_t *rbs_ast_ruby_annotations_colon_method_type_annotation_new(rbs_allocator_t *allocator, rbs_location_t *location, rbs_location_t *prefix_location, rbs_node_list_t *annotations, rbs_node_t *method_type);
733+
rbs_ast_ruby_annotations_instance_variable_annotation_t *rbs_ast_ruby_annotations_instance_variable_annotation_new(rbs_allocator_t *allocator, rbs_location_t *location, rbs_location_t *prefix_location, rbs_ast_symbol_t *ivar_name, rbs_location_t *ivar_name_location, rbs_location_t *colon_location, rbs_node_t *type, rbs_location_t *comment_location);
721734
rbs_ast_ruby_annotations_method_types_annotation_t *rbs_ast_ruby_annotations_method_types_annotation_new(rbs_allocator_t *allocator, rbs_location_t *location, rbs_location_t *prefix_location, rbs_node_list_t *overloads, rbs_location_list_t *vertical_bar_locations);
722735
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);
723736
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);

lib/rbs/ast/ruby/annotations.rb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,31 @@ def map_type_name(&block)
136136
) #: self
137137
end
138138
end
139+
140+
class InstanceVariableAnnotation < Base
141+
attr_reader :ivar_name, :ivar_name_location, :colon_location, :type, :comment_location
142+
143+
def initialize(location:, prefix_location:, ivar_name:, ivar_name_location:, colon_location:, type:, comment_location:)
144+
super(location, prefix_location)
145+
@ivar_name = ivar_name
146+
@ivar_name_location = ivar_name_location
147+
@colon_location = colon_location
148+
@type = type
149+
@comment_location = comment_location
150+
end
151+
152+
def map_type_name(&block)
153+
self.class.new(
154+
location:,
155+
prefix_location:,
156+
ivar_name:,
157+
ivar_name_location:,
158+
colon_location:,
159+
type: type.map_type_name { yield _1 },
160+
comment_location:
161+
) #: self
162+
end
163+
end
139164
end
140165
end
141166
end

lib/rbs/ast/ruby/members.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,27 @@ class AttrWriterMember < AttributeMember
294294

295295
class AttrAccessorMember < AttributeMember
296296
end
297+
298+
class InstanceVariableMember < Base
299+
attr_reader :annotation
300+
301+
def initialize(buffer, annotation)
302+
super(buffer)
303+
@annotation = annotation
304+
end
305+
306+
def name
307+
annotation.ivar_name
308+
end
309+
310+
def type
311+
annotation.type
312+
end
313+
314+
def location
315+
annotation.location
316+
end
317+
end
297318
end
298319
end
299320
end

lib/rbs/definition_builder.rb

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ def define_instance(definition, type_name, subst, define_class_vars:)
155155
member.names.each do |name|
156156
ivar_name = :"@#{name}"
157157
attr_type = member.type || Types::Bases::Any.new(location: nil)
158-
158+
159159
insert_variable(
160160
type_name,
161161
definition.instance_variables,
@@ -165,6 +165,15 @@ def define_instance(definition, type_name, subst, define_class_vars:)
165165
)
166166
end
167167

168+
when AST::Ruby::Members::InstanceVariableMember
169+
insert_variable(
170+
type_name,
171+
definition.instance_variables,
172+
name: member.name,
173+
type: member.type,
174+
source: member
175+
)
176+
168177
when AST::Members::InstanceVariable
169178
insert_variable(
170179
type_name,
@@ -565,6 +574,7 @@ def insert_variable(type_name, variables, name:, type:, source:)
565574
declared_in: type_name,
566575
source: source
567576
)
577+
568578
validate_variable(variables[name])
569579
end
570580

@@ -575,7 +585,15 @@ def validate_variable(var)
575585
variables = [] #: Array[Definition::Variable]
576586
tmp_var = var
577587
while tmp_var
578-
variables << tmp_var if tmp_var.source.is_a?(AST::Members::Var)
588+
case tmp_var.source
589+
when AST::Members::AttrReader, AST::Members::AttrWriter, AST::Members::AttrAccessor
590+
# nop
591+
when AST::Ruby::Members::AttrReaderMember, AST::Ruby::Members::AttrWriterMember, AST::Ruby::Members::AttrAccessorMember
592+
# nop
593+
else
594+
variables << tmp_var
595+
end
596+
579597
tmp_var = tmp_var.parent_variable
580598
end
581599

@@ -593,6 +611,10 @@ def validate_variable(var)
593611
if r.source.instance_of?(AST::Members::ClassInstanceVariable) && l.declared_in == r.declared_in
594612
raise ClassInstanceVariableDuplicationError.new(type_name: l.declared_in, variable_name: l.source.name, location: l.source.location)
595613
end
614+
when AST::Ruby::Members::InstanceVariableMember
615+
if l.declared_in == r.declared_in
616+
raise InstanceVariableDuplicationError.new(type_name: l.declared_in, variable_name: l.source.name, location: l.source.location)
617+
end
596618
end
597619
end
598620

lib/rbs/environment.rb

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -378,11 +378,10 @@ def insert_ruby_decl(decl, context:, namespace:)
378378
if entry.is_a?(ModuleEntry)
379379
raise DuplicatedDeclarationError.new(name, decl, *entry.each_decl.to_a)
380380
end
381+
else
382+
entry = class_decls[name] = ClassEntry.new(name)
381383
end
382384

383-
entry = ClassEntry.new(name)
384-
class_decls[name] = entry
385-
386385
entry << [context, decl]
387386

388387
inner_context = [context, name] #: Resolver::context
@@ -400,11 +399,10 @@ def insert_ruby_decl(decl, context:, namespace:)
400399
if entry.is_a?(ClassEntry)
401400
raise DuplicatedDeclarationError.new(name, decl, *entry.each_decl.to_a)
402401
end
402+
else
403+
entry = class_decls[name] = ModuleEntry.new(name)
403404
end
404405

405-
entry = ModuleEntry.new(name)
406-
class_decls[name] = entry
407-
408406
entry << [context, decl]
409407

410408
inner_context = [context, name] #: Resolver::context
@@ -785,6 +783,12 @@ def resolve_ruby_member(resolver, member, context:)
785783
member.leading_comment,
786784
resolved_type_annotation
787785
)
786+
when AST::Ruby::Members::InstanceVariableMember
787+
resolved_annotation = member.annotation.map_type_name {|name| absolute_type_name(resolver, nil, name, context: context) }
788+
AST::Ruby::Members::InstanceVariableMember.new(
789+
member.buffer,
790+
resolved_annotation
791+
)
788792
else
789793
raise "Unknown member type: #{member.class}"
790794
end

0 commit comments

Comments
 (0)