rbs 0.4.0 → 0.9.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 (71) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +7 -1
  3. data/.gitignore +1 -1
  4. data/CHANGELOG.md +35 -0
  5. data/Gemfile +14 -0
  6. data/README.md +86 -47
  7. data/Rakefile +53 -21
  8. data/bin/rbs-prof +9 -0
  9. data/bin/run_in_md.rb +49 -0
  10. data/docs/stdlib.md +0 -2
  11. data/docs/syntax.md +6 -3
  12. data/goodcheck.yml +65 -0
  13. data/lib/rbs.rb +3 -0
  14. data/lib/rbs/ast/comment.rb +6 -0
  15. data/lib/rbs/ast/declarations.rb +106 -13
  16. data/lib/rbs/ast/members.rb +41 -17
  17. data/lib/rbs/cli.rb +317 -121
  18. data/lib/rbs/constant.rb +4 -4
  19. data/lib/rbs/constant_table.rb +51 -45
  20. data/lib/rbs/definition.rb +175 -59
  21. data/lib/rbs/definition_builder.rb +814 -604
  22. data/lib/rbs/environment.rb +352 -210
  23. data/lib/rbs/environment_walker.rb +14 -23
  24. data/lib/rbs/errors.rb +184 -3
  25. data/lib/rbs/factory.rb +14 -0
  26. data/lib/rbs/location.rb +15 -0
  27. data/lib/rbs/parser.y +100 -34
  28. data/lib/rbs/prototype/rb.rb +101 -113
  29. data/lib/rbs/prototype/rbi.rb +5 -3
  30. data/lib/rbs/prototype/runtime.rb +11 -7
  31. data/lib/rbs/substitution.rb +12 -1
  32. data/lib/rbs/test.rb +82 -3
  33. data/lib/rbs/test/errors.rb +5 -1
  34. data/lib/rbs/test/hook.rb +133 -259
  35. data/lib/rbs/test/observer.rb +17 -0
  36. data/lib/rbs/test/setup.rb +35 -19
  37. data/lib/rbs/test/setup_helper.rb +29 -0
  38. data/lib/rbs/test/spy.rb +0 -321
  39. data/lib/rbs/test/tester.rb +116 -0
  40. data/lib/rbs/test/type_check.rb +43 -7
  41. data/lib/rbs/type_name_resolver.rb +58 -0
  42. data/lib/rbs/types.rb +94 -2
  43. data/lib/rbs/validator.rb +55 -0
  44. data/lib/rbs/variance_calculator.rb +12 -2
  45. data/lib/rbs/version.rb +1 -1
  46. data/lib/rbs/writer.rb +127 -91
  47. data/rbs.gemspec +0 -10
  48. data/schema/decls.json +36 -10
  49. data/schema/members.json +3 -0
  50. data/stdlib/benchmark/benchmark.rbs +151 -151
  51. data/stdlib/builtin/enumerable.rbs +3 -3
  52. data/stdlib/builtin/file.rbs +0 -3
  53. data/stdlib/builtin/io.rbs +4 -4
  54. data/stdlib/builtin/proc.rbs +1 -2
  55. data/stdlib/builtin/thread.rbs +2 -2
  56. data/stdlib/csv/csv.rbs +4 -6
  57. data/stdlib/fiber/fiber.rbs +1 -1
  58. data/stdlib/json/json.rbs +7 -1
  59. data/stdlib/logger/formatter.rbs +23 -0
  60. data/stdlib/logger/log_device.rbs +39 -0
  61. data/stdlib/logger/logger.rbs +507 -0
  62. data/stdlib/logger/period.rbs +7 -0
  63. data/stdlib/logger/severity.rbs +8 -0
  64. data/stdlib/mutex_m/mutex_m.rbs +77 -0
  65. data/stdlib/pathname/pathname.rbs +6 -6
  66. data/stdlib/prime/integer-extension.rbs +1 -1
  67. data/stdlib/prime/prime.rbs +44 -44
  68. data/stdlib/pty/pty.rbs +159 -0
  69. data/stdlib/tmpdir/tmpdir.rbs +1 -1
  70. metadata +19 -130
  71. 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,60 @@ 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
416
+ builder.ensure_namespace!(name.namespace, location: const.decl.location)
330
417
  end
331
418
 
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
419
+ env.global_decls.each do |name, global|
420
+ stdout.puts "Validating global: `#{name}`..."
421
+ validator.validate_type global.decl.type, context: [Namespace.root]
422
+ end
423
+
424
+ env.alias_decls.each do |name, decl|
425
+ stdout.puts "Validating alias: `#{name}`..."
426
+ builder.expand_alias(name).tap do |type|
427
+ validator.validate_type type, context: [Namespace.root]
428
+ end
335
429
  end
336
430
  end
337
431
 
@@ -339,7 +433,20 @@ module RBS
339
433
  context = nil
340
434
 
341
435
  OptionParser.new do |opts|
342
- opts.on("--context CONTEXT") {|c| context = c }
436
+ opts.banner = <<EOU
437
+ Usage: rbs constant [options...] [name]
438
+
439
+ Resolve constant based on RBS.
440
+
441
+ Examples:
442
+
443
+ $ rbs constant ::Object
444
+ $ rbs constant UTF_8
445
+ $ rbs constant --context=::Encoding UTF_8
446
+
447
+ Options:
448
+ EOU
449
+ opts.on("--context CONTEXT", "Name of the module where the constant resolution starts") {|c| context = c }
343
450
  end.order!(args)
344
451
 
345
452
  unless args.size == 1
@@ -351,8 +458,7 @@ module RBS
351
458
 
352
459
  options.setup(loader)
353
460
 
354
- env = Environment.new()
355
- loader.load(env: env)
461
+ env = Environment.from_loader(loader).resolve_type_names
356
462
 
357
463
  builder = DefinitionBuilder.new(env: env)
358
464
  table = ConstantTable.new(builder: builder)
@@ -371,11 +477,20 @@ module RBS
371
477
  end
372
478
  end
373
479
 
374
- def run_version(args, options)
375
- stdout.puts "rbs #{VERSION}"
376
- end
377
-
378
480
  def run_paths(args, options)
481
+ OptionParser.new do |opts|
482
+ opts.banner = <<EOU
483
+ Usage: rbs paths
484
+
485
+ Show paths to directories where the RBS files are loaded from.
486
+
487
+ Examples:
488
+
489
+ $ rbs paths
490
+ $ tbs -r set paths
491
+ EOU
492
+ end.parse!(args)
493
+
379
494
  loader = EnvironmentLoader.new()
380
495
 
381
496
  options.setup(loader)
@@ -423,16 +538,30 @@ module RBS
423
538
  owners_included = []
424
539
 
425
540
  OptionParser.new do |opts|
426
- opts.on("--require LIB") do |lib|
541
+ opts.banner = <<EOU
542
+ Usage: rbs prototype runtime [options...] [pattern...]
543
+
544
+ Generate RBS prototype based on runtime introspection.
545
+ It loads Ruby code specified in [options] and generates RBS prototypes for classes matches to [pattern].
546
+
547
+ Examples:
548
+
549
+ $ rbs prototype runtime String
550
+ $ rbs prototype runtime --require set Set
551
+ $ rbs prototype runtime -R lib/rbs RBS::*
552
+
553
+ Options:
554
+ EOU
555
+ opts.on("-r", "--require LIB", "Load library using `require`") do |lib|
427
556
  require_libs << lib
428
557
  end
429
- opts.on("--require-relative LIB") do |lib|
558
+ opts.on("-R", "--require-relative LIB", "Load library using `require_relative`") do |lib|
430
559
  relative_libs << lib
431
560
  end
432
- opts.on("--merge") do
561
+ opts.on("--merge", "Merge generated prototype RBS with existing RBS") do
433
562
  merge = true
434
563
  end
435
- opts.on("--method-owner CLASS") do |klass|
564
+ opts.on("--method-owner CLASS", "Generate method prototypes if the owner of the method is [CLASS]") do |klass|
436
565
  owners_included << klass
437
566
  end
438
567
  end.parse!(args)
@@ -441,8 +570,7 @@ module RBS
441
570
 
442
571
  options.setup(loader)
443
572
 
444
- env = Environment.new()
445
- loader.load(env: env)
573
+ env = Environment.from_loader(loader).resolve_type_names
446
574
 
447
575
  require_libs.each do |lib|
448
576
  require(lib)
@@ -454,15 +582,60 @@ module RBS
454
582
 
455
583
  decls = Prototype::Runtime.new(patterns: args, env: env, merge: merge, owners_included: owners_included).decls
456
584
  else
457
- stdout.puts "Supported formats: rbi, rb, runtime"
585
+ stdout.puts <<EOU
586
+ Usage: rbs prototype [generator...] [args...]
587
+
588
+ Generate prototype of RBS files.
589
+ Supported generators are rb, rbi, runtime.
590
+
591
+ Examples:
592
+
593
+ $ rbs prototype rb foo.rb
594
+ $ rbs prototype rbi foo.rbi
595
+ $ rbs prototype runtime String
596
+ EOU
458
597
  exit 1
459
598
  end
460
599
 
461
- writer = Writer.new(out: stdout)
462
- writer.write decls
600
+ if decls
601
+ writer = Writer.new(out: stdout)
602
+ writer.write decls
603
+ else
604
+ exit 1
605
+ end
463
606
  end
464
607
 
465
608
  def run_prototype_file(format, args)
609
+ availability = unless has_parser?
610
+ "\n** This command does not work on this interpreter (#{RUBY_ENGINE}) **\n"
611
+ end
612
+
613
+ opts = OptionParser.new
614
+ opts.banner = <<EOU
615
+ Usage: rbs prototype #{format} [options...] [files...]
616
+ #{availability}
617
+ Generate RBS prototype from source code.
618
+ It parses specified Ruby code and and generates RBS prototypes.
619
+
620
+ It only works on MRI because it parses Ruby code with `RubyVM::AbstractSyntaxTree`.
621
+
622
+ Examples:
623
+
624
+ $ rbs prototype rb lib/foo.rb
625
+ $ rbs prototype rbi sorbet/rbi/foo.rbi
626
+ EOU
627
+ opts.parse!(args)
628
+
629
+ unless has_parser?
630
+ stdout.puts "Not supported on this interpreter (#{RUBY_ENGINE})."
631
+ exit 1
632
+ end
633
+
634
+ if args.empty?
635
+ stdout.puts opts
636
+ return nil
637
+ end
638
+
466
639
  parser = case format
467
640
  when "rbi"
468
641
  Prototype::RBI.new()
@@ -483,9 +656,19 @@ module RBS
483
656
  vendor_dir = Pathname("vendor/sigs")
484
657
 
485
658
  OptionParser.new do |opts|
486
- opts.banner = <<~EOB
487
- Usage: rbs vendor [options] GEMS...
488
- Vendor signatures in the project directory.
659
+ opts.banner = <<-EOB
660
+ Usage: rbs vendor [options...] [gems...]
661
+
662
+ Vendor signatures in the project directory.
663
+ This command ignores the RBS loading global options, `-r` and `-I`.
664
+
665
+ Examples:
666
+
667
+ $ rbs vendor
668
+ $ rbs vendor --vendor-dir=sig
669
+ $ rbs vendor --no-stdlib
670
+
671
+ Options:
489
672
  EOB
490
673
 
491
674
  opts.on("--[no-]clean", "Clean vendor directory (default: no)") do |v|
@@ -528,6 +711,18 @@ module RBS
528
711
  end
529
712
 
530
713
  def run_parse(args, options)
714
+ OptionParser.new do |opts|
715
+ opts.banner = <<-EOB
716
+ Usage: rbs parse [files...]
717
+
718
+ Parse given RBS files and print syntax errors.
719
+
720
+ Examples:
721
+
722
+ $ rbs parse sig/app/models.rbs sig/app/controllers.rbs
723
+ EOB
724
+ end.parse!(args)
725
+
531
726
  loader = EnvironmentLoader.new()
532
727
 
533
728
  syntax_error = false
@@ -545,6 +740,7 @@ module RBS
545
740
  syntax_error = true
546
741
  end
547
742
  end
743
+
548
744
  exit 1 if syntax_error
549
745
  end
550
746