rbs 0.3.1 → 0.8.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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +7 -1
  3. data/.gitignore +1 -1
  4. data/CHANGELOG.md +39 -0
  5. data/COPYING +1 -1
  6. data/Gemfile +16 -2
  7. data/README.md +87 -48
  8. data/Rakefile +54 -22
  9. data/bin/rbs-prof +9 -0
  10. data/bin/run_in_md.rb +49 -0
  11. data/bin/test_runner.rb +0 -2
  12. data/docs/sigs.md +6 -6
  13. data/docs/stdlib.md +3 -5
  14. data/docs/syntax.md +6 -3
  15. data/goodcheck.yml +65 -0
  16. data/lib/rbs.rb +3 -0
  17. data/lib/rbs/ast/declarations.rb +115 -14
  18. data/lib/rbs/ast/members.rb +41 -17
  19. data/lib/rbs/cli.rb +315 -122
  20. data/lib/rbs/constant.rb +4 -4
  21. data/lib/rbs/constant_table.rb +51 -45
  22. data/lib/rbs/definition.rb +175 -59
  23. data/lib/rbs/definition_builder.rb +802 -604
  24. data/lib/rbs/environment.rb +352 -210
  25. data/lib/rbs/environment_walker.rb +14 -23
  26. data/lib/rbs/errors.rb +184 -3
  27. data/lib/rbs/factory.rb +14 -0
  28. data/lib/rbs/parser.y +95 -27
  29. data/lib/rbs/prototype/rb.rb +119 -117
  30. data/lib/rbs/prototype/rbi.rb +5 -3
  31. data/lib/rbs/prototype/runtime.rb +34 -7
  32. data/lib/rbs/substitution.rb +12 -1
  33. data/lib/rbs/test.rb +82 -3
  34. data/lib/rbs/test/errors.rb +5 -1
  35. data/lib/rbs/test/hook.rb +133 -259
  36. data/lib/rbs/test/observer.rb +17 -0
  37. data/lib/rbs/test/setup.rb +35 -19
  38. data/lib/rbs/test/setup_helper.rb +29 -0
  39. data/lib/rbs/test/spy.rb +0 -321
  40. data/lib/rbs/test/tester.rb +116 -0
  41. data/lib/rbs/test/type_check.rb +43 -7
  42. data/lib/rbs/type_name_resolver.rb +58 -0
  43. data/lib/rbs/types.rb +94 -2
  44. data/lib/rbs/validator.rb +51 -0
  45. data/lib/rbs/variance_calculator.rb +12 -2
  46. data/lib/rbs/version.rb +1 -1
  47. data/lib/rbs/writer.rb +127 -91
  48. data/rbs.gemspec +0 -9
  49. data/schema/annotation.json +14 -0
  50. data/schema/comment.json +26 -0
  51. data/schema/decls.json +353 -0
  52. data/schema/function.json +87 -0
  53. data/schema/location.json +56 -0
  54. data/schema/members.json +248 -0
  55. data/schema/methodType.json +44 -0
  56. data/schema/types.json +299 -0
  57. data/stdlib/benchmark/benchmark.rbs +151 -151
  58. data/stdlib/builtin/encoding.rbs +2 -0
  59. data/stdlib/builtin/enumerable.rbs +4 -4
  60. data/stdlib/builtin/enumerator.rbs +3 -1
  61. data/stdlib/builtin/fiber.rbs +5 -1
  62. data/stdlib/builtin/file.rbs +0 -3
  63. data/stdlib/builtin/io.rbs +4 -4
  64. data/stdlib/builtin/proc.rbs +1 -2
  65. data/stdlib/builtin/symbol.rbs +1 -1
  66. data/stdlib/builtin/thread.rbs +2 -2
  67. data/stdlib/csv/csv.rbs +4 -6
  68. data/stdlib/fiber/fiber.rbs +117 -0
  69. data/stdlib/json/json.rbs +1 -1
  70. data/stdlib/logger/formatter.rbs +23 -0
  71. data/stdlib/logger/log_device.rbs +39 -0
  72. data/stdlib/logger/logger.rbs +507 -0
  73. data/stdlib/logger/period.rbs +7 -0
  74. data/stdlib/logger/severity.rbs +8 -0
  75. data/stdlib/mutex_m/mutex_m.rbs +77 -0
  76. data/stdlib/pathname/pathname.rbs +6 -6
  77. data/stdlib/prime/integer-extension.rbs +1 -1
  78. data/stdlib/prime/prime.rbs +44 -44
  79. data/stdlib/pty/pty.rbs +159 -0
  80. data/stdlib/tmpdir/tmpdir.rbs +1 -1
  81. metadata +28 -116
  82. data/lib/rbs/test/test_helper.rb +0 -183
@@ -36,18 +36,18 @@ module RBS
36
36
  @stderr = stderr
37
37
  end
38
38
 
39
- COMMANDS = [:ast, :list, :ancestors, :methods, :method, :validate, :constant, :paths, :prototype, :vendor, :version, :parse]
39
+ COMMANDS = [:ast, :list, :ancestors, :methods, :method, :validate, :constant, :paths, :prototype, :vendor, :parse]
40
40
 
41
41
  def library_parse(opts, options:)
42
- opts.on("-r LIBRARY") do |lib|
42
+ opts.on("-r LIBRARY", "Load RBS files of the library") do |lib|
43
43
  options.libs << lib
44
44
  end
45
45
 
46
- opts.on("-I DIR") do |dir|
46
+ opts.on("-I DIR", "Load RBS files from the directory") do |dir|
47
47
  options.dirs << dir
48
48
  end
49
49
 
50
- opts.on("--no-stdlib") do
50
+ opts.on("--no-stdlib", "Skip loading standard library signatures") do
51
51
  options.no_stdlib = true
52
52
  end
53
53
 
@@ -55,33 +55,44 @@ module RBS
55
55
  end
56
56
 
57
57
  def parse_logging_options(opts)
58
- opts.on("--log-level=LEVEL", "Specify log level (defaults to `warn`)") do |level|
58
+ opts.on("--log-level LEVEL", "Specify log level (defaults to `warn`)") do |level|
59
59
  RBS.logger_level = level
60
60
  end
61
61
 
62
- opts.on("--log-output=OUTPUT", "Specify the file to output log (defaults to stderr)") do |output|
62
+ opts.on("--log-output OUTPUT", "Specify the file to output log (defaults to stderr)") do |output|
63
63
  RBS.logger_output = File.open(output, "a")
64
64
  end
65
65
 
66
66
  opts
67
67
  end
68
68
 
69
+ def has_parser?
70
+ defined?(RubyVM::AbstractSyntaxTree)
71
+ end
72
+
69
73
  def run(args)
70
74
  options = LibraryOptions.new
71
75
 
72
76
  opts = OptionParser.new
73
77
  opts.banner = <<~USAGE
74
- Usage: rbs [options] COMMAND
75
- Available commands: #{COMMANDS.join(", ")}
78
+ Usage: rbs [options...] [command...]
79
+
80
+ Available commands: #{COMMANDS.join(", ")}, version, help.
81
+
82
+ Options:
76
83
  USAGE
77
84
  library_parse(opts, options: options)
78
85
  parse_logging_options(opts)
86
+ opts.version = RBS::VERSION
79
87
 
80
88
  opts.order!(args)
81
89
 
82
90
  command = args.shift&.to_sym
83
91
 
84
- if COMMANDS.include?(command)
92
+ case command
93
+ when :version
94
+ stdout.puts opts.ver
95
+ when *COMMANDS
85
96
  __send__ :"run_#{command}", args, options
86
97
  else
87
98
  stdout.puts opts.help
@@ -89,101 +100,151 @@ module RBS
89
100
  end
90
101
 
91
102
  def run_ast(args, options)
92
- loader = EnvironmentLoader.new()
103
+ OptionParser.new do |opts|
104
+ opts.banner = <<EOB
105
+ Usage: rbs ast [patterns...]
106
+
107
+ Print JSON AST of loaded environment.
108
+ You can specify patterns to filter declarations with the file names.
109
+
110
+ Examples:
93
111
 
112
+ $ rbs ast
113
+ $ rbs ast 'basic_object.rbs'
114
+ $ rbs -I ./sig ast ./sig
115
+ $ rbs -I ./sig ast '*/models/*.rbs'
116
+ EOB
117
+ end.order!(args)
118
+
119
+ patterns = args.map do |arg|
120
+ path = Pathname(arg)
121
+ if path.exist?
122
+ # Pathname means a directory or a file
123
+ path
124
+ else
125
+ # String means a `fnmatch` pattern
126
+ arg
127
+ end
128
+ end
129
+
130
+ loader = EnvironmentLoader.new()
94
131
  options.setup(loader)
95
132
 
96
- env = Environment.new()
97
- loader.load(env: env)
133
+ env = Environment.from_loader(loader).resolve_type_names
98
134
 
99
- stdout.print JSON.generate(env.declarations)
135
+ decls = env.declarations.select do |decl|
136
+ name = decl.location.buffer.name
137
+
138
+ patterns.empty? || patterns.any? do |pat|
139
+ case pat
140
+ when Pathname
141
+ Pathname(name).ascend.any? {|p| p == pat }
142
+ when String
143
+ name.end_with?(pat) || File.fnmatch(pat, name, File::FNM_EXTGLOB)
144
+ end
145
+ end
146
+ end
147
+
148
+ stdout.print JSON.generate(decls)
100
149
  stdout.flush
101
150
  end
102
151
 
103
152
  def run_list(args, options)
104
- list = []
153
+ list = Set[]
105
154
 
106
155
  OptionParser.new do |opts|
107
- opts.on("--class") { list << :class }
108
- opts.on("--module") { list << :module }
109
- opts.on("--interface") { list << :interface }
156
+ opts.banner = <<EOB
157
+ Usage: rbs list [options...]
158
+
159
+ List classes, modules, and interfaces.
160
+
161
+ Examples:
162
+
163
+ $ rbs list
164
+ $ rbs list --class --module --interface
165
+
166
+ Options:
167
+ EOB
168
+ opts.on("--class", "List classes") { list << :class }
169
+ opts.on("--module", "List modules") { list << :module }
170
+ opts.on("--interface", "List interfaces") { list << :interface }
110
171
  end.order!(args)
111
172
 
112
- list.push(:class, :module, :interface) if list.empty?
173
+ list.merge([:class, :module, :interface]) if list.empty?
113
174
 
114
175
  loader = EnvironmentLoader.new()
115
-
116
176
  options.setup(loader)
117
177
 
118
- env = Environment.new()
119
- loader.load(env: env)
178
+ env = Environment.from_loader(loader).resolve_type_names
120
179
 
121
- env.each_decl.sort_by {|name,| name.to_s }.each do |type_name, decl|
122
- case decl
123
- when AST::Declarations::Class
124
- if list.include?(:class)
125
- stdout.puts "#{type_name} (class)"
126
- end
127
- when AST::Declarations::Module
128
- if list.include?(:module)
129
- stdout.puts "#{type_name} (module)"
130
- end
131
- when AST::Declarations::Interface
132
- if list.include?(:interface)
133
- stdout.puts "#{type_name} (interface)"
180
+ if list.include?(:class) || list.include?(:module)
181
+ env.class_decls.each do |name, entry|
182
+ case entry
183
+ when Environment::ModuleEntry
184
+ if list.include?(:module)
185
+ stdout.puts "#{name} (module)"
186
+ end
187
+ when Environment::ClassEntry
188
+ if list.include?(:class)
189
+ stdout.puts "#{name} (class)"
190
+ end
134
191
  end
135
192
  end
136
193
  end
194
+
195
+ if list.include?(:interface)
196
+ env.interface_decls.each do |name, entry|
197
+ stdout.puts "#{name} (interface)"
198
+ end
199
+ end
137
200
  end
138
201
 
139
202
  def run_ancestors(args, options)
140
203
  kind = :instance
141
204
 
142
205
  OptionParser.new do |opts|
143
- opts.on("--instance") { kind = :instance }
144
- opts.on("--singleton") { kind = :singleton }
206
+ opts.banner = <<EOU
207
+ Usage: rbs ancestors [options...] [type_name]
208
+
209
+ Show ancestors of the given class or module.
210
+
211
+ Examples:
212
+
213
+ $ rbs ancestors --instance String
214
+ $ rbs ancestors --singleton Array
215
+
216
+ Options:
217
+ EOU
218
+ opts.on("--instance", "Ancestors of instance of the given type_name (default)") { kind = :instance }
219
+ opts.on("--singleton", "Ancestors of singleton of the given type_name") { kind = :singleton }
145
220
  end.order!(args)
146
221
 
147
222
  loader = EnvironmentLoader.new()
148
-
149
223
  options.setup(loader)
150
224
 
151
- env = Environment.new()
152
- loader.load(env: env)
225
+ env = Environment.from_loader(loader).resolve_type_names
153
226
 
154
227
  builder = DefinitionBuilder.new(env: env)
155
228
  type_name = parse_type_name(args[0]).absolute!
156
229
 
157
- if env.class?(type_name)
158
- ancestor = case kind
159
- when :instance
160
- decl = env.find_class(type_name)
161
- Definition::Ancestor::Instance.new(name: type_name,
162
- args: Types::Variable.build(decl.type_params.each.map(&:name)))
163
- when :singleton
164
- Definition::Ancestor::Singleton.new(name: type_name)
165
- end
230
+ if env.class_decls.key?(type_name)
231
+ ancestors = case kind
232
+ when :instance
233
+ builder.instance_ancestors(type_name)
234
+ when :singleton
235
+ builder.singleton_ancestors(type_name)
236
+ end
166
237
 
167
- ancestors = builder.build_ancestors(ancestor)
168
-
169
- ancestors.each do |ancestor|
238
+ ancestors.ancestors.each do |ancestor|
170
239
  case ancestor
171
240
  when Definition::Ancestor::Singleton
172
241
  stdout.puts "singleton(#{ancestor.name})"
173
- when Definition::Ancestor::ExtensionSingleton
174
- stdout.puts "singleton(#{ancestor.name} (#{ancestor.extension_name}))"
175
242
  when Definition::Ancestor::Instance
176
243
  if ancestor.args.empty?
177
244
  stdout.puts ancestor.name.to_s
178
245
  else
179
246
  stdout.puts "#{ancestor.name}[#{ancestor.args.join(", ")}]"
180
247
  end
181
- when Definition::Ancestor::ExtensionInstance
182
- if ancestor.args.empty?
183
- stdout.puts "#{ancestor.name} (#{ancestor.extension_name})"
184
- else
185
- stdout.puts "#{ancestor.name}[#{ancestor.args.join(", ")}] (#{ancestor.extension_name})"
186
- end
187
248
  end
188
249
  end
189
250
  else
@@ -196,10 +257,21 @@ module RBS
196
257
  inherit = true
197
258
 
198
259
  OptionParser.new do |opts|
199
- opts.on("--instance") { kind = :instance }
200
- opts.on("--singleton") { kind = :singleton }
201
- opts.on("--inherit") { inherit = true }
202
- opts.on("--no-inherit") { inherit = false }
260
+ opts.banner = <<EOU
261
+ Usage: rbs methods [options...] [type_name]
262
+
263
+ Show methods defined in the class or module.
264
+
265
+ Examples:
266
+
267
+ $ rbs methods --instance Kernel
268
+ $ rbs methods --singleton --no-inherit String
269
+
270
+ Options:
271
+ EOU
272
+ opts.on("--instance", "Show instance methods (default)") { kind = :instance }
273
+ opts.on("--singleton", "Show singleton methods") { kind = :singleton }
274
+ opts.on("--[no-]inherit", "Show methods defined in super class and mixed modules too") {|v| inherit = v }
203
275
  end.order!(args)
204
276
 
205
277
  unless args.size == 1
@@ -208,16 +280,14 @@ module RBS
208
280
  end
209
281
 
210
282
  loader = EnvironmentLoader.new()
211
-
212
283
  options.setup(loader)
213
284
 
214
- env = Environment.new()
215
- loader.load(env: env)
285
+ env = Environment.from_loader(loader).resolve_type_names
216
286
 
217
287
  builder = DefinitionBuilder.new(env: env)
218
288
  type_name = parse_type_name(args[0]).absolute!
219
289
 
220
- if env.class?(type_name)
290
+ if env.class_decls.key?(type_name)
221
291
  definition = case kind
222
292
  when :instance
223
293
  builder.build_instance(type_name)
@@ -227,7 +297,7 @@ module RBS
227
297
 
228
298
  definition.methods.keys.sort.each do |name|
229
299
  method = definition.methods[name]
230
- if inherit || method.implemented_in == definition.declaration
300
+ if inherit || method.implemented_in == type_name
231
301
  stdout.puts "#{name} (#{method.accessibility})"
232
302
  end
233
303
  end
@@ -240,8 +310,20 @@ module RBS
240
310
  kind = :instance
241
311
 
242
312
  OptionParser.new do |opts|
243
- opts.on("--instance") { kind = :instance }
244
- opts.on("--singleton") { kind = :singleton }
313
+ opts.banner = <<EOU
314
+ Usage: rbs method [options...] [type_name] [method_name]
315
+
316
+ Show the information of the method specified by type_name and method_name.
317
+
318
+ Examples:
319
+
320
+ $ rbs method --instance Kernel puts
321
+ $ rbs method --singleton String try_convert
322
+
323
+ Options:
324
+ EOU
325
+ opts.on("--instance", "Show an instance method (default)") { kind = :instance }
326
+ opts.on("--singleton", "Show a singleton method") { kind = :singleton }
245
327
  end.order!(args)
246
328
 
247
329
  unless args.size == 2
@@ -250,17 +332,15 @@ module RBS
250
332
  end
251
333
 
252
334
  loader = EnvironmentLoader.new()
253
-
254
335
  options.setup(loader)
255
336
 
256
- env = Environment.new()
257
- loader.load(env: env)
337
+ env = Environment.from_loader(loader).resolve_type_names
258
338
 
259
339
  builder = DefinitionBuilder.new(env: env)
260
340
  type_name = parse_type_name(args[0]).absolute!
261
341
  method_name = args[1].to_sym
262
342
 
263
- unless env.class?(type_name)
343
+ unless env.class_decls.key?(type_name)
264
344
  stdout.puts "Cannot find class: #{type_name}"
265
345
  return
266
346
  end
@@ -280,8 +360,8 @@ module RBS
280
360
  end
281
361
 
282
362
  stdout.puts "#{type_name}#{kind == :instance ? "#" : "."}#{method_name}"
283
- stdout.puts " defined_in: #{method.defined_in&.name&.absolute!}"
284
- stdout.puts " implementation: #{method.implemented_in.name.absolute!}"
363
+ stdout.puts " defined_in: #{method.defined_in}"
364
+ stdout.puts " implementation: #{method.implemented_in}"
285
365
  stdout.puts " accessibility: #{method.accessibility}"
286
366
  stdout.puts " types:"
287
367
  separator = " "
@@ -292,46 +372,57 @@ module RBS
292
372
  end
293
373
 
294
374
  def run_validate(args, options)
375
+ OptionParser.new do |opts|
376
+ opts.banner = <<EOU
377
+ Usage: rbs validate
378
+
379
+ Validate RBS files. It ensures the type names in RBS files are present and the type applications have correct arity.
380
+
381
+ Examples:
382
+
383
+ $ rbs validate
384
+ EOU
385
+ end.parse!(args)
386
+
295
387
  loader = EnvironmentLoader.new()
296
388
 
297
389
  options.setup(loader)
298
390
 
299
- env = Environment.new()
300
- loader.load(env: env)
391
+ env = Environment.from_loader(loader).resolve_type_names
301
392
 
302
393
  builder = DefinitionBuilder.new(env: env)
394
+ validator = Validator.new(env: env, resolver: TypeNameResolver.from_env(env))
303
395
 
304
- env.each_decl do |name, decl|
305
- case decl
306
- when AST::Declarations::Class, AST::Declarations::Module
307
- stdout.puts "#{Location.to_string decl.location}:\tValidating class/module definition: `#{name}`..."
308
- builder.build_instance(decl.name.absolute!).each_type do |type|
309
- env.validate type, namespace: Namespace.root
310
- end
311
- builder.build_singleton(decl.name.absolute!).each_type do |type|
312
- env.validate type, namespace: Namespace.root
313
- end
314
- when AST::Declarations::Interface
315
- stdout.puts "#{Location.to_string decl.location}:\tValidating interface: `#{name}`..."
316
- builder.build_interface(decl.name.absolute!, decl).each_type do |type|
317
- env.validate type, namespace: Namespace.root
318
- end
396
+ env.class_decls.each_key do |name|
397
+ stdout.puts "Validating class/module definition: `#{name}`..."
398
+ builder.build_instance(name).each_type do |type|
399
+ validator.validate_type type, context: [Namespace.root]
400
+ end
401
+ builder.build_singleton(name).each_type do |type|
402
+ validator.validate_type type, context: [Namespace.root]
319
403
  end
320
404
  end
321
405
 
322
- env.each_constant do |name, const|
323
- stdout.puts "#{Location.to_string const.location}:\tValidating constant: `#{name}`..."
324
- env.validate const.type, namespace: name.namespace
406
+ env.interface_decls.each_key do |name|
407
+ stdout.puts "Validating interface: `#{name}`..."
408
+ builder.build_interface(name).each_type do |type|
409
+ validator.validate_type type, context: [Namespace.root]
410
+ end
325
411
  end
326
412
 
327
- env.each_global do |name, global|
328
- stdout.puts "#{Location.to_string global.location}:\tValidating global: `#{name}`..."
329
- env.validate global.type, namespace: Namespace.root
413
+ env.constant_decls.each do |name, const|
414
+ stdout.puts "Validating constant: `#{name}`..."
415
+ validator.validate_type const.decl.type, context: const.context
330
416
  end
331
417
 
332
- env.each_alias do |name, decl|
333
- stdout.puts "#{Location.to_string decl.location}:\tValidating alias: `#{name}`..."
334
- env.validate decl.type, namespace: name.namespace
418
+ env.global_decls.each do |name, global|
419
+ stdout.puts "Validating global: `#{name}`..."
420
+ validator.validate_type global.decl.type, context: [Namespace.root]
421
+ end
422
+
423
+ env.alias_decls.each do |name, decl|
424
+ stdout.puts "Validating alias: `#{name}`..."
425
+ validator.validate_type decl.decl.type, context: decl.context
335
426
  end
336
427
  end
337
428
 
@@ -339,7 +430,20 @@ module RBS
339
430
  context = nil
340
431
 
341
432
  OptionParser.new do |opts|
342
- opts.on("--context CONTEXT") {|c| context = c }
433
+ opts.banner = <<EOU
434
+ Usage: rbs constant [options...] [name]
435
+
436
+ Resolve constant based on RBS.
437
+
438
+ Examples:
439
+
440
+ $ rbs constant ::Object
441
+ $ rbs constant UTF_8
442
+ $ rbs constant --context=::Encoding UTF_8
443
+
444
+ Options:
445
+ EOU
446
+ opts.on("--context CONTEXT", "Name of the module where the constant resolution starts") {|c| context = c }
343
447
  end.order!(args)
344
448
 
345
449
  unless args.size == 1
@@ -351,8 +455,7 @@ module RBS
351
455
 
352
456
  options.setup(loader)
353
457
 
354
- env = Environment.new()
355
- loader.load(env: env)
458
+ env = Environment.from_loader(loader).resolve_type_names
356
459
 
357
460
  builder = DefinitionBuilder.new(env: env)
358
461
  table = ConstantTable.new(builder: builder)
@@ -371,11 +474,20 @@ module RBS
371
474
  end
372
475
  end
373
476
 
374
- def run_version(args, options)
375
- stdout.puts "ruby-signature #{VERSION}"
376
- end
377
-
378
477
  def run_paths(args, options)
478
+ OptionParser.new do |opts|
479
+ opts.banner = <<EOU
480
+ Usage: rbs paths
481
+
482
+ Show paths to directories where the RBS files are loaded from.
483
+
484
+ Examples:
485
+
486
+ $ rbs paths
487
+ $ tbs -r set paths
488
+ EOU
489
+ end.parse!(args)
490
+
379
491
  loader = EnvironmentLoader.new()
380
492
 
381
493
  options.setup(loader)
@@ -423,16 +535,30 @@ module RBS
423
535
  owners_included = []
424
536
 
425
537
  OptionParser.new do |opts|
426
- opts.on("--require LIB") do |lib|
538
+ opts.banner = <<EOU
539
+ Usage: rbs prototype runtime [options...] [pattern...]
540
+
541
+ Generate RBS prototype based on runtime introspection.
542
+ It loads Ruby code specified in [options] and generates RBS prototypes for classes matches to [pattern].
543
+
544
+ Examples:
545
+
546
+ $ rbs prototype runtime String
547
+ $ rbs prototype runtime --require set Set
548
+ $ rbs prototype runtime -R lib/rbs RBS::*
549
+
550
+ Options:
551
+ EOU
552
+ opts.on("-r", "--require LIB", "Load library using `require`") do |lib|
427
553
  require_libs << lib
428
554
  end
429
- opts.on("--require-relative LIB") do |lib|
555
+ opts.on("-R", "--require-relative LIB", "Load library using `require_relative`") do |lib|
430
556
  relative_libs << lib
431
557
  end
432
- opts.on("--merge") do
558
+ opts.on("--merge", "Merge generated prototype RBS with existing RBS") do
433
559
  merge = true
434
560
  end
435
- opts.on("--method-owner CLASS") do |klass|
561
+ opts.on("--method-owner CLASS", "Generate method prototypes if the owner of the method is [CLASS]") do |klass|
436
562
  owners_included << klass
437
563
  end
438
564
  end.parse!(args)
@@ -441,28 +567,72 @@ module RBS
441
567
 
442
568
  options.setup(loader)
443
569
 
444
- env = Environment.new()
445
- loader.load(env: env)
570
+ env = Environment.from_loader(loader).resolve_type_names
446
571
 
447
572
  require_libs.each do |lib|
448
573
  require(lib)
449
574
  end
450
575
 
451
576
  relative_libs.each do |lib|
452
- require(lib)
577
+ eval("require_relative(lib)", binding, "rbs")
453
578
  end
454
579
 
455
580
  decls = Prototype::Runtime.new(patterns: args, env: env, merge: merge, owners_included: owners_included).decls
456
581
  else
457
- stdout.puts "Supported formats: rbi, rb, runtime"
582
+ stdout.puts <<EOU
583
+ Usage: rbs prototype [generator...] [args...]
584
+
585
+ Generate prototype of RBS files.
586
+ Supported generators are rb, rbi, runtime.
587
+
588
+ Examples:
589
+
590
+ $ rbs prototype rb foo.rb
591
+ $ rbs prototype rbi foo.rbi
592
+ $ rbs prototype runtime String
593
+ EOU
458
594
  exit 1
459
595
  end
460
596
 
461
- writer = Writer.new(out: stdout)
462
- writer.write decls
597
+ if decls
598
+ writer = Writer.new(out: stdout)
599
+ writer.write decls
600
+ else
601
+ exit 1
602
+ end
463
603
  end
464
604
 
465
605
  def run_prototype_file(format, args)
606
+ availability = unless has_parser?
607
+ "\n** This command does not work on this interpreter (#{RUBY_ENGINE}) **\n"
608
+ end
609
+
610
+ opts = OptionParser.new
611
+ opts.banner = <<EOU
612
+ Usage: rbs prototype #{format} [options...] [files...]
613
+ #{availability}
614
+ Generate RBS prototype from source code.
615
+ It parses specified Ruby code and and generates RBS prototypes.
616
+
617
+ It only works on MRI because it parses Ruby code with `RubyVM::AbstractSyntaxTree`.
618
+
619
+ Examples:
620
+
621
+ $ rbs prototype rb lib/foo.rb
622
+ $ rbs prototype rbi sorbet/rbi/foo.rbi
623
+ EOU
624
+ opts.parse!(args)
625
+
626
+ unless has_parser?
627
+ stdout.puts "Not supported on this interpreter (#{RUBY_ENGINE})."
628
+ exit 1
629
+ end
630
+
631
+ if args.empty?
632
+ stdout.puts opts
633
+ return nil
634
+ end
635
+
466
636
  parser = case format
467
637
  when "rbi"
468
638
  Prototype::RBI.new()
@@ -483,9 +653,19 @@ module RBS
483
653
  vendor_dir = Pathname("vendor/sigs")
484
654
 
485
655
  OptionParser.new do |opts|
486
- opts.banner = <<~EOB
487
- Usage: rbs vendor [options] GEMS...
488
- Vendor signatures in the project directory.
656
+ opts.banner = <<-EOB
657
+ Usage: rbs vendor [options...] [gems...]
658
+
659
+ Vendor signatures in the project directory.
660
+ This command ignores the RBS loading global options, `-r` and `-I`.
661
+
662
+ Examples:
663
+
664
+ $ rbs vendor
665
+ $ rbs vendor --vendor-dir=sig
666
+ $ rbs vendor --no-stdlib
667
+
668
+ Options:
489
669
  EOB
490
670
 
491
671
  opts.on("--[no-]clean", "Clean vendor directory (default: no)") do |v|
@@ -528,6 +708,18 @@ module RBS
528
708
  end
529
709
 
530
710
  def run_parse(args, options)
711
+ OptionParser.new do |opts|
712
+ opts.banner = <<-EOB
713
+ Usage: rbs parse [files...]
714
+
715
+ Parse given RBS files and print syntax errors.
716
+
717
+ Examples:
718
+
719
+ $ rbs parse sig/app/models.rbs sig/app/controllers.rbs
720
+ EOB
721
+ end.parse!(args)
722
+
531
723
  loader = EnvironmentLoader.new()
532
724
 
533
725
  syntax_error = false
@@ -545,6 +737,7 @@ module RBS
545
737
  syntax_error = true
546
738
  end
547
739
  end
740
+
548
741
  exit 1 if syntax_error
549
742
  end
550
743