rbs 0.4.0 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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