rbs-inline 0.4.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -17,22 +17,22 @@ module RBS
17
17
  #
18
18
  #
19
19
  class PathCalculator
20
- attr_reader :pwd #:: Pathname
20
+ attr_reader :pwd #: Pathname
21
21
 
22
- attr_reader :base_paths #:: Array[Pathname]
22
+ attr_reader :base_paths #: Array[Pathname]
23
23
 
24
- attr_reader :output_path #:: Pathname
24
+ attr_reader :output_path #: Pathname
25
25
 
26
26
  # @rbs pwd: Pathname
27
27
  # @rbs base_paths: Array[Pathname]
28
28
  # @rbs output_path: Pathname
29
- def initialize(pwd, base_paths, output_path) #:: void
29
+ def initialize(pwd, base_paths, output_path) #: void
30
30
  @pwd = pwd
31
31
  @base_paths = base_paths
32
32
  @output_path = output_path
33
33
  end
34
34
 
35
- #:: (Pathname) -> Pathname?
35
+ #: (Pathname) -> Pathname?
36
36
  def calculate(path)
37
37
  path = pwd + path if path.relative?
38
38
  path = path.cleanpath
@@ -49,18 +49,18 @@ module RBS
49
49
 
50
50
  # @rbs path: Pathname
51
51
  # @rbs prefix: Pathname
52
- # @rbs returns bool
52
+ # @rbs return: bool
53
53
  def has_prefix?(path, prefix:)
54
54
  path.descend.include?(prefix)
55
55
  end
56
56
  end
57
57
 
58
- attr_reader :stdout, :stderr #:: IO
59
- attr_reader :logger #:: Logger
58
+ attr_reader :stdout, :stderr #: IO
59
+ attr_reader :logger #: Logger
60
60
 
61
61
  # @rbs stdout: IO
62
62
  # @rbs stderr: IO
63
- def initialize(stdout: STDOUT, stderr: STDERR) #:: void
63
+ def initialize(stdout: STDOUT, stderr: STDERR) #: void
64
64
  @stdout = stdout
65
65
  @stderr = stderr
66
66
  @logger = Logger.new(stderr)
@@ -68,7 +68,7 @@ module RBS
68
68
  end
69
69
 
70
70
  # @rbs args: Array[String]
71
- # @rbs returns Integer
71
+ # @rbs return: Integer
72
72
  def run(args)
73
73
  base_paths = [Pathname("lib"), Pathname("app")]
74
74
  output_path = nil #: Pathname?
@@ -139,10 +139,10 @@ module RBS
139
139
 
140
140
  logger.debug { "Parsing ruby file #{target}..." }
141
141
 
142
- if (uses, decls = Parser.parse(Prism.parse_file(target.to_s), opt_in: opt_in))
142
+ if (uses, decls, rbs_decls = Parser.parse(Prism.parse_file(target.to_s), opt_in: opt_in))
143
143
  writer = Writer.new()
144
144
  writer.header("Generated from #{target.relative? ? target : target.relative_path_from(Pathname.pwd)} with RBS::Inline")
145
- writer.write(uses, decls)
145
+ writer.write(uses, decls, rbs_decls)
146
146
 
147
147
  if output
148
148
  unless output.parent.directory?
@@ -4,7 +4,7 @@ module RBS
4
4
  module Inline
5
5
  module NodeUtils
6
6
  # @rbs node: Prism::Node
7
- # @rbs returns TypeName?
7
+ # @rbs return: TypeName?
8
8
  def type_name(node)
9
9
  case node
10
10
  when Prism::ConstantReadNode
@@ -5,13 +5,18 @@
5
5
  module RBS
6
6
  module Inline
7
7
  class Parser < Prism::Visitor
8
+ # @rbs! type with_members = AST::Declarations::ModuleDecl
9
+ # | AST::Declarations::ClassDecl
10
+ # | AST::Declarations::SingletonClassDecl
11
+ # | AST::Declarations::BlockDecl
12
+
8
13
  # The top level declarations
9
14
  #
10
- attr_reader :decls #:: Array[AST::Declarations::t]
15
+ attr_reader :decls #: Array[AST::Declarations::t]
11
16
 
12
17
  # The surrounding declarations
13
18
  #
14
- attr_reader :surrounding_decls #:: Array[AST::Declarations::ModuleDecl | AST::Declarations::ClassDecl]
19
+ attr_reader :surrounding_decls #: Array[with_members]
15
20
 
16
21
  # ParsingResult associated with the line number at the end
17
22
  #
@@ -23,24 +28,35 @@ module RBS
23
28
  # > [!IMPORTANT]
24
29
  # > The values will be removed during parsing.
25
30
  #
26
- attr_reader :comments #:: Hash[Integer, AnnotationParser::ParsingResult]
31
+ attr_reader :comments #: Hash[Integer, AnnotationParser::ParsingResult]
27
32
 
28
33
  # The current visibility applied to single `def` node
29
34
  #
30
35
  # Assuming it's directly inside `private` or `public` calls.
31
36
  # `nil` when the `def` node is not inside `private` or `public` calls.
32
37
  #
33
- attr_reader :current_visibility #:: RBS::AST::Members::visibility?
38
+ attr_reader :current_visibility #: RBS::AST::Members::visibility?
34
39
 
35
- def initialize() #:: void
40
+ def initialize() #: void
36
41
  @decls = []
37
42
  @surrounding_decls = []
38
43
  @comments = {}
39
44
  end
40
45
 
46
+ # Parses the given Prism result to a three tuple
47
+ #
48
+ # Returns a three tuple of:
49
+ #
50
+ # 1. An array of `use` directives
51
+ # 2. An array of declarations
52
+ # 3. An array of RBS declarations given as `@rbs!` annotation at top-level
53
+ #
54
+ # Note that only RBS declarations are allowed in the top-level `@rbs!` annotations.
55
+ # RBS *members* are ignored in the array.
56
+ #
41
57
  # @rbs result: ParseResult
42
58
  # @rbs opt_in: bool -- `true` for *opt-out* mode, `false` for *opt-in* mode.
43
- # @rbs returns [Array[AST::Annotations::Use], Array[AST::Declarations::t]]?
59
+ # @rbs return: [Array[AST::Annotations::Use], Array[AST::Declarations::t], Array[RBS::AST::Declarations::t]]?
44
60
  def self.parse(result, opt_in:)
45
61
  instance = Parser.new()
46
62
 
@@ -61,7 +77,7 @@ module RBS
61
77
 
62
78
  uses = [] #: Array[AST::Annotations::Use]
63
79
  annots.each do |annot|
64
- annot.annotations.each do |annotation|
80
+ annot.each_annotation do |annotation|
65
81
  if annotation.is_a?(AST::Annotations::Use)
66
82
  uses << annotation
67
83
  end
@@ -70,24 +86,44 @@ module RBS
70
86
 
71
87
  instance.visit(result.value)
72
88
 
89
+ rbs_embeddeds = [] #: Array[AST::Members::RBSEmbedded]
90
+
91
+ instance.comments.each_value do |comment|
92
+ comment.each_annotation do |annotation|
93
+ if annotation.is_a?(AST::Annotations::Embedded)
94
+ rbs_embeddeds << AST::Members::RBSEmbedded.new(comment, annotation)
95
+ end
96
+ end
97
+ end
98
+
99
+ rbs_decls = rbs_embeddeds.flat_map do |embedded|
100
+ if (members = embedded.members).is_a?(Array)
101
+ members.select do |member|
102
+ member.is_a?(RBS::AST::Declarations::Base)
103
+ end
104
+ else
105
+ []
106
+ end #: Array[RBS::AST::Declarations::t]
107
+ end
108
+
73
109
  [
74
110
  uses,
75
- instance.decls
111
+ instance.decls,
112
+ rbs_decls
76
113
  ]
77
114
  end
78
115
 
79
- # @rbs returns AST::Declarations::ModuleDecl | AST::Declarations::ClassDecl | nil
116
+ # @rbs return: with_members?
80
117
  def current_class_module_decl
81
118
  surrounding_decls.last
82
119
  end
83
120
 
84
- # @rbs returns AST::Declarations::ModuleDecl | AST::Declarations::ClassDecl
121
+ # @rbs return: with_members
85
122
  def current_class_module_decl!
86
123
  current_class_module_decl or raise
87
124
  end
88
125
 
89
- #:: (AST::Declarations::ModuleDecl | AST::Declarations::ClassDecl | AST::Declarations::SingletonClassDecl) { () -> void } -> void
90
- #:: (AST::Declarations::ConstantDecl) -> void
126
+ #: (with_members) { () -> void } -> void
91
127
  def push_class_module_decl(decl)
92
128
  if current = current_class_module_decl
93
129
  current.members << decl
@@ -96,7 +132,7 @@ module RBS
96
132
  end
97
133
 
98
134
  if block_given?
99
- surrounding_decls.push(_ = decl)
135
+ surrounding_decls.push(decl)
100
136
  begin
101
137
  yield
102
138
  ensure
@@ -114,11 +150,11 @@ module RBS
114
150
  # @rbs members: Array[AST::Members::t | AST::Declarations::t] --
115
151
  # The destination.
116
152
  # The method doesn't insert declarations, but have it to please type checker.
117
- def load_inner_annotations(start_line, end_line, members) #:: void
153
+ def load_inner_annotations(start_line, end_line, members) #: void
118
154
  comments = inner_annotations(start_line, end_line)
119
155
 
120
156
  comments.each do |comment|
121
- comment.annotations.each do |annotation|
157
+ comment.each_annotation do |annotation|
122
158
  case annotation
123
159
  when AST::Annotations::IvarType
124
160
  members << AST::Members::RBSIvar.new(comment, annotation)
@@ -133,51 +169,53 @@ module RBS
133
169
 
134
170
  # @rbs override
135
171
  def visit_class_node(node)
136
- return if ignored_node?(node)
172
+ process_nesting_node(node) do
173
+ visit node.constant_path
174
+ visit node.superclass
137
175
 
138
- visit node.constant_path
139
- visit node.superclass
176
+ associated_comment = comments.delete(node.location.start_line - 1)
177
+ if node.superclass
178
+ app_comment = application_annotation(node.superclass)
179
+ end
140
180
 
141
- associated_comment = comments.delete(node.location.start_line - 1)
142
- if node.superclass
143
- app_comment = application_annotation(node.superclass)
144
- end
181
+ class_decl = AST::Declarations::ClassDecl.new(node, associated_comment, app_comment)
145
182
 
146
- class_decl = AST::Declarations::ClassDecl.new(node, associated_comment, app_comment)
183
+ push_class_module_decl(class_decl) do
184
+ visit node.body
185
+ end
147
186
 
148
- push_class_module_decl(class_decl) do
149
- visit node.body
187
+ load_inner_annotations(node.location.start_line, node.location.end_line, class_decl.members)
150
188
  end
151
-
152
- load_inner_annotations(node.location.start_line, node.location.end_line, class_decl.members)
153
189
  end
154
190
 
155
191
  # @rbs override
156
192
  def visit_singleton_class_node(node)
157
- return if ignored_node?(node)
193
+ process_nesting_node(node) do
194
+ associated_comment = comments.delete(node.location.start_line - 1)
195
+ singleton_decl = AST::Declarations::SingletonClassDecl.new(node, associated_comment)
158
196
 
159
- associated_comment = comments.delete(node.location.start_line - 1)
160
- singleton_decl = AST::Declarations::SingletonClassDecl.new(node, associated_comment)
197
+ push_class_module_decl(singleton_decl) do
198
+ visit node.body
199
+ end
161
200
 
162
- push_class_module_decl(singleton_decl) do
163
- visit node.body
201
+ load_inner_annotations(node.location.start_line, node.location.end_line, singleton_decl.members)
164
202
  end
165
203
  end
166
204
 
167
205
  # @rbs override
168
206
  def visit_module_node(node)
169
- return if ignored_node?(node)
207
+ process_nesting_node(node) do
208
+ visit node.constant_path
170
209
 
171
- visit node.constant_path
210
+ associated_comment = comments.delete(node.location.start_line - 1)
172
211
 
173
- associated_comment = comments.delete(node.location.start_line - 1)
212
+ module_decl = AST::Declarations::ModuleDecl.new(node, associated_comment)
213
+ push_class_module_decl(module_decl) do
214
+ visit node.body
215
+ end
174
216
 
175
- module_decl = AST::Declarations::ModuleDecl.new(node, associated_comment)
176
- push_class_module_decl(module_decl) do
177
- visit node.body
217
+ load_inner_annotations(node.location.start_line, node.location.end_line, module_decl.members)
178
218
  end
179
-
180
- load_inner_annotations(node.location.start_line, node.location.end_line, module_decl.members)
181
219
  end
182
220
 
183
221
  # Returns an array of annotations from comments that is located between start_line and end_line
@@ -191,7 +229,7 @@ module RBS
191
229
  #
192
230
  # @rbs start_line: Integer
193
231
  # @rbs end_line: Integer
194
- def inner_annotations(start_line, end_line) #:: Array[AnnotationParser::ParsingResult]
232
+ def inner_annotations(start_line, end_line) #: Array[AnnotationParser::ParsingResult]
195
233
  annotations = comments.each_value.select do |annotation|
196
234
  range = annotation.line_range
197
235
  start_line < range.begin && range.end < end_line
@@ -204,25 +242,27 @@ module RBS
204
242
 
205
243
  # @rbs override
206
244
  def visit_def_node(node)
207
- return if ignored_node?(node)
208
- return unless current_class_module_decl
245
+ process_nesting_node(node) do
246
+ return unless current_class_module_decl
209
247
 
210
- current_decl = current_class_module_decl!
248
+ current_decl = current_class_module_decl!
211
249
 
212
- if node.location
213
- associated_comment = comments.delete(node.location.start_line - 1)
214
- end
250
+ if node.location
251
+ associated_comment = comments.delete(node.location.start_line - 1)
252
+ end
215
253
 
216
- assertion = assertion_annotation(node.rparen_loc || node&.parameters&.location || node.name_loc)
254
+ assertion = assertion_annotation(node.rparen_loc || node&.parameters&.location || node.name_loc)
217
255
 
218
- current_decl.members << AST::Members::RubyDef.new(node, associated_comment, current_visibility, assertion)
256
+ current_decl.members << AST::Members::RubyDef.new(node, associated_comment, current_visibility, assertion)
219
257
 
220
- super
258
+ super
259
+ end
221
260
  end
222
261
 
223
262
  # @rbs override
224
263
  def visit_alias_method_node(node)
225
264
  return if ignored_node?(node)
265
+ return unless current_class_module_decl
226
266
 
227
267
  if node.location
228
268
  comment = comments.delete(node.location.start_line - 1)
@@ -256,9 +296,9 @@ module RBS
256
296
  end
257
297
  if assertion_comment && comment_line
258
298
  comments.delete(comment_line)
259
- assertion = assertion_comment.annotations.find do |annotation|
260
- annotation.is_a?(AST::Annotations::Assertion)
261
- end #: AST::Annotations::Assertion?
299
+ assertion = assertion_comment.each_annotation.find do |annotation|
300
+ annotation.is_a?(AST::Annotations::TypeAssertion)
301
+ end #: AST::Annotations::TypeAssertion?
262
302
  end
263
303
 
264
304
  current_class_module_decl!.members << AST::Members::RubyAttr.new(node, comment, assertion)
@@ -296,8 +336,8 @@ module RBS
296
336
  end
297
337
 
298
338
  # @rbs new_visibility: RBS::AST::Members::visibility?
299
- # @rbs block: ^() -> void
300
- # @rbs returns void
339
+ # @rbs &block: () -> void
340
+ # @rbs return: void
301
341
  def push_visibility(new_visibility, &block)
302
342
  old_visibility = current_visibility
303
343
 
@@ -309,11 +349,21 @@ module RBS
309
349
  end
310
350
  end
311
351
 
352
+ # @rbs [A] (Node) { () -> A } -> A?
353
+ def process_nesting_node(node)
354
+ yield unless ignored_node?(node)
355
+ ensure
356
+ # Delete all inner annotations
357
+ inner_annotations(node.location.start_line, node.location.end_line)
358
+ comments.delete(node.location.start_line)
359
+ comments.delete(node.location.end_line)
360
+ end
361
+
312
362
  # @rbs node: Node
313
- # @rbs returns bool
363
+ # @rbs return: bool
314
364
  def ignored_node?(node)
315
365
  if comment = comments.fetch(node.location.start_line - 1, nil)
316
- comment.annotations.any? { _1.is_a?(AST::Annotations::Skip) }
366
+ comment.each_annotation.any? { _1.is_a?(AST::Annotations::Skip) }
317
367
  else
318
368
  false
319
369
  end
@@ -324,7 +374,7 @@ module RBS
324
374
  # The application annotation is removed from `comments`.
325
375
  #
326
376
  # @rbs node: Node
327
- # @rbs returns AST::Annotations::Application?
377
+ # @rbs return: AST::Annotations::Application?
328
378
  def application_annotation(node)
329
379
  comment_line, app_comment = comments.find do |_, comment|
330
380
  comment.line_range.begin == node.location.end_line
@@ -332,18 +382,18 @@ module RBS
332
382
 
333
383
  if app_comment && comment_line
334
384
  comments.delete(comment_line)
335
- app_comment.annotations.find do |annotation|
385
+ app_comment.each_annotation.find do |annotation|
336
386
  annotation.is_a?(AST::Annotations::Application)
337
387
  end #: AST::Annotations::Application?
338
388
  end
339
389
  end
340
390
 
341
- # Fetch Assertion annotation which is associated to `node`
391
+ # Fetch TypeAssertion annotation which is associated to `node`
342
392
  #
343
393
  # The assertion annotation is removed from `comments`.
344
394
  #
345
395
  # @rbs node: Node | Location
346
- # @rbs returns AST::Annotations::Assertion?
396
+ # @rbs return: AST::Annotations::TypeAssertion?
347
397
  def assertion_annotation(node)
348
398
  if node.is_a?(Prism::Location)
349
399
  location = node
@@ -356,9 +406,9 @@ module RBS
356
406
 
357
407
  if app_comment && comment_line
358
408
  comments.delete(comment_line)
359
- app_comment.annotations.find do |annotation|
360
- annotation.is_a?(AST::Annotations::Assertion)
361
- end #: AST::Annotations::Assertion?
409
+ app_comment.each_annotation.find do |annotation|
410
+ annotation.is_a?(AST::Annotations::TypeAssertion)
411
+ end #: AST::Annotations::TypeAssertion?
362
412
  end
363
413
  end
364
414
 
@@ -367,10 +417,58 @@ module RBS
367
417
  return if ignored_node?(node)
368
418
 
369
419
  comment = comments.delete(node.location.start_line - 1)
370
- assertion = assertion_annotation(node)
371
420
 
372
- decl = AST::Declarations::ConstantDecl.new(node, comment, assertion)
373
- push_class_module_decl(decl)
421
+ case
422
+ when data_node = AST::Declarations::DataAssignDecl.data_define?(node)
423
+ type_decls = {} #: Hash[Integer, AST::Annotations::TypeAssertion]
424
+
425
+ inner_annotations(node.location.start_line, node.location.end_line).flat_map do |comment|
426
+ comment.each_annotation do |annotation|
427
+ if annotation.is_a?(AST::Annotations::TypeAssertion)
428
+ start_line = annotation.source.comments[0].location.start_line
429
+ type_decls[start_line] = annotation
430
+ end
431
+ end
432
+ end
433
+
434
+ decl = AST::Declarations::DataAssignDecl.new(node, data_node, comment, type_decls)
435
+ when struct_node = AST::Declarations::StructAssignDecl.struct_new?(node)
436
+ type_decls = {} #: Hash[Integer, AST::Annotations::TypeAssertion]
437
+
438
+ inner_annotations(node.location.start_line, node.location.end_line).flat_map do |comment|
439
+ comment.each_annotation do |annotation|
440
+ if annotation.is_a?(AST::Annotations::TypeAssertion)
441
+ start_line = annotation.source.comments[0].location.start_line
442
+ type_decls[start_line] = annotation
443
+ end
444
+ end
445
+ end
446
+
447
+ decl = AST::Declarations::StructAssignDecl.new(node, struct_node, comment, type_decls)
448
+ else
449
+ assertion = assertion_annotation(node)
450
+ decl = AST::Declarations::ConstantDecl.new(node, comment, assertion)
451
+ end
452
+
453
+ if current = current_class_module_decl
454
+ current.members << decl
455
+ else
456
+ decls << decl
457
+ end
458
+ end
459
+
460
+ # @rbs override
461
+ def visit_block_node(node)
462
+ process_nesting_node(node) do
463
+ comment = comments.delete(node.location.start_line - 1)
464
+ block = AST::Declarations::BlockDecl.new(node, comment)
465
+
466
+ push_class_module_decl(block) do
467
+ super
468
+ end
469
+
470
+ load_inner_annotations(node.location.start_line, node.location.end_line, block.members)
471
+ end
374
472
  end
375
473
  end
376
474
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RBS
4
4
  module Inline
5
- VERSION = "0.4.0"
5
+ VERSION = "0.6.0"
6
6
  end
7
7
  end