rbs 0.4.0 → 0.5.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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +7 -1
  3. data/.gitignore +1 -1
  4. data/CHANGELOG.md +7 -0
  5. data/Gemfile +12 -0
  6. data/README.md +86 -47
  7. data/Rakefile +54 -21
  8. data/bin/rbs-prof +9 -0
  9. data/bin/run_in_md.rb +49 -0
  10. data/lib/rbs.rb +2 -0
  11. data/lib/rbs/ast/declarations.rb +62 -7
  12. data/lib/rbs/ast/members.rb +41 -17
  13. data/lib/rbs/cli.rb +299 -121
  14. data/lib/rbs/constant.rb +4 -4
  15. data/lib/rbs/constant_table.rb +50 -44
  16. data/lib/rbs/definition.rb +175 -59
  17. data/lib/rbs/definition_builder.rb +647 -603
  18. data/lib/rbs/environment.rb +338 -209
  19. data/lib/rbs/environment_walker.rb +14 -23
  20. data/lib/rbs/errors.rb +141 -3
  21. data/lib/rbs/parser.y +14 -9
  22. data/lib/rbs/prototype/rb.rb +100 -112
  23. data/lib/rbs/prototype/rbi.rb +4 -2
  24. data/lib/rbs/prototype/runtime.rb +10 -6
  25. data/lib/rbs/substitution.rb +8 -1
  26. data/lib/rbs/test/hook.rb +2 -2
  27. data/lib/rbs/test/setup.rb +3 -1
  28. data/lib/rbs/test/test_helper.rb +2 -5
  29. data/lib/rbs/test/type_check.rb +1 -2
  30. data/lib/rbs/type_name_resolver.rb +58 -0
  31. data/lib/rbs/types.rb +94 -2
  32. data/lib/rbs/validator.rb +51 -0
  33. data/lib/rbs/variance_calculator.rb +12 -2
  34. data/lib/rbs/version.rb +1 -1
  35. data/lib/rbs/writer.rb +125 -89
  36. data/rbs.gemspec +0 -10
  37. data/schema/decls.json +15 -0
  38. data/schema/members.json +3 -0
  39. data/stdlib/benchmark/benchmark.rbs +151 -151
  40. data/stdlib/builtin/enumerable.rbs +1 -1
  41. data/stdlib/builtin/file.rbs +0 -3
  42. data/stdlib/builtin/io.rbs +4 -4
  43. data/stdlib/builtin/thread.rbs +2 -2
  44. data/stdlib/csv/csv.rbs +4 -6
  45. data/stdlib/fiber/fiber.rbs +1 -1
  46. data/stdlib/json/json.rbs +1 -1
  47. data/stdlib/mutex_m/mutex_m.rbs +77 -0
  48. data/stdlib/pathname/pathname.rbs +6 -6
  49. data/stdlib/prime/integer-extension.rbs +1 -1
  50. data/stdlib/prime/prime.rbs +44 -44
  51. data/stdlib/tmpdir/tmpdir.rbs +1 -1
  52. metadata +8 -129
@@ -1,7 +1,10 @@
1
1
  module RBS
2
2
  module AST
3
3
  module Members
4
- class MethodDefinition
4
+ class Base
5
+ end
6
+
7
+ class MethodDefinition < Base
5
8
  attr_reader :name
6
9
  attr_reader :kind
7
10
  attr_reader :types
@@ -9,8 +12,9 @@ module RBS
9
12
  attr_reader :location
10
13
  attr_reader :comment
11
14
  attr_reader :attributes
15
+ attr_reader :overload
12
16
 
13
- def initialize(name:, kind:, types:, annotations:, location:, comment:, attributes:)
17
+ def initialize(name:, kind:, types:, annotations:, location:, comment:, attributes:, overload:)
14
18
  @name = name
15
19
  @kind = kind
16
20
  @types = types
@@ -18,6 +22,7 @@ module RBS
18
22
  @location = location
19
23
  @comment = comment
20
24
  @attributes = attributes
25
+ @overload = overload
21
26
  end
22
27
 
23
28
  def ==(other)
@@ -25,13 +30,14 @@ module RBS
25
30
  other.name == name &&
26
31
  other.kind == kind &&
27
32
  other.types == types &&
28
- other.attributes == attributes
33
+ other.attributes == attributes &&
34
+ other.overload == overload
29
35
  end
30
36
 
31
37
  alias eql? ==
32
38
 
33
39
  def hash
34
- self.class.hash ^ name.hash ^ kind.hash ^ types.hash ^ attributes.hash
40
+ self.class.hash ^ name.hash ^ kind.hash ^ types.hash ^ attributes.hash ^ overload.hash
35
41
  end
36
42
 
37
43
  def instance?
@@ -42,6 +48,23 @@ module RBS
42
48
  kind == :singleton || kind == :singleton_instance
43
49
  end
44
50
 
51
+ def overload?
52
+ overload
53
+ end
54
+
55
+ def update(name: self.name, kind: self.kind, types: self.types, annotations: self.annotations, location: self.location, comment: self.comment, attributes: self.attributes, overload: self.overload)
56
+ self.class.new(
57
+ name: name,
58
+ kind: kind,
59
+ types: types,
60
+ annotations: annotations,
61
+ location: location,
62
+ comment: comment,
63
+ attributes: attributes,
64
+ overload: overload
65
+ )
66
+ end
67
+
45
68
  def to_json(*a)
46
69
  {
47
70
  member: :method_definition,
@@ -50,7 +73,8 @@ module RBS
50
73
  annotations: annotations,
51
74
  location: location,
52
75
  comment: comment,
53
- attributes: attributes
76
+ attributes: attributes,
77
+ overload: overload
54
78
  }.to_json(*a)
55
79
  end
56
80
  end
@@ -79,7 +103,7 @@ module RBS
79
103
  end
80
104
  end
81
105
 
82
- class InstanceVariable
106
+ class InstanceVariable < Base
83
107
  include Var
84
108
 
85
109
  def to_json(*a)
@@ -93,7 +117,7 @@ module RBS
93
117
  end
94
118
  end
95
119
 
96
- class ClassInstanceVariable
120
+ class ClassInstanceVariable < Base
97
121
  include Var
98
122
 
99
123
  def to_json(*a)
@@ -107,7 +131,7 @@ module RBS
107
131
  end
108
132
  end
109
133
 
110
- class ClassVariable
134
+ class ClassVariable < Base
111
135
  include Var
112
136
 
113
137
  def to_json(*a)
@@ -149,7 +173,7 @@ module RBS
149
173
  end
150
174
  end
151
175
 
152
- class Include
176
+ class Include < Base
153
177
  include Mixin
154
178
 
155
179
  def to_json(*a)
@@ -164,7 +188,7 @@ module RBS
164
188
  end
165
189
  end
166
190
 
167
- class Extend
191
+ class Extend < Base
168
192
  include Mixin
169
193
 
170
194
  def to_json(*a)
@@ -179,7 +203,7 @@ module RBS
179
203
  end
180
204
  end
181
205
 
182
- class Prepend
206
+ class Prepend < Base
183
207
  include Mixin
184
208
 
185
209
  def to_json(*a)
@@ -225,7 +249,7 @@ module RBS
225
249
  end
226
250
  end
227
251
 
228
- class AttrReader
252
+ class AttrReader < Base
229
253
  include Attribute
230
254
 
231
255
  def to_json(*a)
@@ -241,7 +265,7 @@ module RBS
241
265
  end
242
266
  end
243
267
 
244
- class AttrAccessor
268
+ class AttrAccessor < Base
245
269
  include Attribute
246
270
 
247
271
  def to_json(*a)
@@ -257,7 +281,7 @@ module RBS
257
281
  end
258
282
  end
259
283
 
260
- class AttrWriter
284
+ class AttrWriter < Base
261
285
  include Attribute
262
286
 
263
287
  def to_json(*a)
@@ -291,7 +315,7 @@ module RBS
291
315
  end
292
316
  end
293
317
 
294
- class Public
318
+ class Public < Base
295
319
  include LocationOnly
296
320
 
297
321
  def to_json(*a)
@@ -299,7 +323,7 @@ module RBS
299
323
  end
300
324
  end
301
325
 
302
- class Private
326
+ class Private < Base
303
327
  include LocationOnly
304
328
 
305
329
  def to_json(*a)
@@ -307,7 +331,7 @@ module RBS
307
331
  end
308
332
  end
309
333
 
310
- class Alias
334
+ class Alias < Base
311
335
  attr_reader :new_name
312
336
  attr_reader :old_name
313
337
  attr_reader :kind
@@ -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,11 +55,11 @@ 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
 
@@ -71,17 +71,24 @@ module RBS
71
71
 
72
72
  opts = OptionParser.new
73
73
  opts.banner = <<~USAGE
74
- Usage: rbs [options] COMMAND
75
- Available commands: #{COMMANDS.join(", ")}
74
+ Usage: rbs [options...] [command...]
75
+
76
+ Available commands: #{COMMANDS.join(", ")}, version, help.
77
+
78
+ Options:
76
79
  USAGE
77
80
  library_parse(opts, options: options)
78
81
  parse_logging_options(opts)
82
+ opts.version = RBS::VERSION
79
83
 
80
84
  opts.order!(args)
81
85
 
82
86
  command = args.shift&.to_sym
83
87
 
84
- if COMMANDS.include?(command)
88
+ case command
89
+ when :version
90
+ stdout.puts opts.ver
91
+ when *COMMANDS
85
92
  __send__ :"run_#{command}", args, options
86
93
  else
87
94
  stdout.puts opts.help
@@ -89,101 +96,151 @@ module RBS
89
96
  end
90
97
 
91
98
  def run_ast(args, options)
92
- loader = EnvironmentLoader.new()
99
+ OptionParser.new do |opts|
100
+ opts.banner = <<EOB
101
+ Usage: rbs ast [patterns...]
102
+
103
+ Print JSON AST of loaded environment.
104
+ You can specify patterns to filter declarations with the file names.
105
+
106
+ Examples:
107
+
108
+ $ rbs ast
109
+ $ rbs ast 'basic_object.rbs'
110
+ $ rbs -I ./sig ast ./sig
111
+ $ rbs -I ./sig ast '*/models/*.rbs'
112
+ EOB
113
+ end.order!(args)
93
114
 
115
+ patterns = args.map do |arg|
116
+ path = Pathname(arg)
117
+ if path.exist?
118
+ # Pathname means a directory or a file
119
+ path
120
+ else
121
+ # String means a `fnmatch` pattern
122
+ arg
123
+ end
124
+ end
125
+
126
+ loader = EnvironmentLoader.new()
94
127
  options.setup(loader)
95
128
 
96
- env = Environment.new()
97
- loader.load(env: env)
129
+ env = Environment.from_loader(loader).resolve_type_names
130
+
131
+ decls = env.declarations.select do |decl|
132
+ name = decl.location.buffer.name
98
133
 
99
- stdout.print JSON.generate(env.declarations)
134
+ patterns.empty? || patterns.any? do |pat|
135
+ case pat
136
+ when Pathname
137
+ Pathname(name).ascend.any? {|p| p == pat }
138
+ when String
139
+ name.end_with?(pat) || File.fnmatch(pat, name, File::FNM_EXTGLOB)
140
+ end
141
+ end
142
+ end
143
+
144
+ stdout.print JSON.generate(decls)
100
145
  stdout.flush
101
146
  end
102
147
 
103
148
  def run_list(args, options)
104
- list = []
149
+ list = Set[]
105
150
 
106
151
  OptionParser.new do |opts|
107
- opts.on("--class") { list << :class }
108
- opts.on("--module") { list << :module }
109
- opts.on("--interface") { list << :interface }
152
+ opts.banner = <<EOB
153
+ Usage: rbs list [options...]
154
+
155
+ List classes, modules, and interfaces.
156
+
157
+ Examples:
158
+
159
+ $ rbs list
160
+ $ rbs list --class --module --interface
161
+
162
+ Options:
163
+ EOB
164
+ opts.on("--class", "List classes") { list << :class }
165
+ opts.on("--module", "List modules") { list << :module }
166
+ opts.on("--interface", "List interfaces") { list << :interface }
110
167
  end.order!(args)
111
168
 
112
- list.push(:class, :module, :interface) if list.empty?
169
+ list.merge([:class, :module, :interface]) if list.empty?
113
170
 
114
171
  loader = EnvironmentLoader.new()
115
-
116
172
  options.setup(loader)
117
173
 
118
- env = Environment.new()
119
- loader.load(env: env)
174
+ env = Environment.from_loader(loader).resolve_type_names
120
175
 
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)"
176
+ if list.include?(:class) || list.include?(:module)
177
+ env.class_decls.each do |name, entry|
178
+ case entry
179
+ when Environment::ModuleEntry
180
+ if list.include?(:module)
181
+ stdout.puts "#{name} (module)"
182
+ end
183
+ when Environment::ClassEntry
184
+ if list.include?(:class)
185
+ stdout.puts "#{name} (class)"
186
+ end
134
187
  end
135
188
  end
136
189
  end
190
+
191
+ if list.include?(:interface)
192
+ env.interface_decls.each do |name, entry|
193
+ stdout.puts "#{name} (interface)"
194
+ end
195
+ end
137
196
  end
138
197
 
139
198
  def run_ancestors(args, options)
140
199
  kind = :instance
141
200
 
142
201
  OptionParser.new do |opts|
143
- opts.on("--instance") { kind = :instance }
144
- opts.on("--singleton") { kind = :singleton }
202
+ opts.banner = <<EOU
203
+ Usage: rbs ancestors [options...] [type_name]
204
+
205
+ Show ancestors of the given class or module.
206
+
207
+ Examples:
208
+
209
+ $ rbs ancestors --instance String
210
+ $ rbs ancestors --singleton Array
211
+
212
+ Options:
213
+ EOU
214
+ opts.on("--instance", "Ancestors of instance of the given type_name (default)") { kind = :instance }
215
+ opts.on("--singleton", "Ancestors of singleton of the given type_name") { kind = :singleton }
145
216
  end.order!(args)
146
217
 
147
218
  loader = EnvironmentLoader.new()
148
-
149
219
  options.setup(loader)
150
220
 
151
- env = Environment.new()
152
- loader.load(env: env)
221
+ env = Environment.from_loader(loader).resolve_type_names
153
222
 
154
223
  builder = DefinitionBuilder.new(env: env)
155
224
  type_name = parse_type_name(args[0]).absolute!
156
225
 
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
166
-
167
- ancestors = builder.build_ancestors(ancestor)
226
+ if env.class_decls.key?(type_name)
227
+ ancestors = case kind
228
+ when :instance
229
+ builder.instance_ancestors(type_name)
230
+ when :singleton
231
+ builder.singleton_ancestors(type_name)
232
+ end
168
233
 
169
- ancestors.each do |ancestor|
234
+ ancestors.ancestors.each do |ancestor|
170
235
  case ancestor
171
236
  when Definition::Ancestor::Singleton
172
237
  stdout.puts "singleton(#{ancestor.name})"
173
- when Definition::Ancestor::ExtensionSingleton
174
- stdout.puts "singleton(#{ancestor.name} (#{ancestor.extension_name}))"
175
238
  when Definition::Ancestor::Instance
176
239
  if ancestor.args.empty?
177
240
  stdout.puts ancestor.name.to_s
178
241
  else
179
242
  stdout.puts "#{ancestor.name}[#{ancestor.args.join(", ")}]"
180
243
  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
244
  end
188
245
  end
189
246
  else
@@ -196,10 +253,21 @@ module RBS
196
253
  inherit = true
197
254
 
198
255
  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 }
256
+ opts.banner = <<EOU
257
+ Usage: rbs methods [options...] [type_name]
258
+
259
+ Show methods defined in the class or module.
260
+
261
+ Examples:
262
+
263
+ $ rbs methods --instance Kernel
264
+ $ rbs methods --singleton --no-inherit String
265
+
266
+ Options:
267
+ EOU
268
+ opts.on("--instance", "Show instance methods (default)") { kind = :instance }
269
+ opts.on("--singleton", "Show singleton methods") { kind = :singleton }
270
+ opts.on("--[no-]inherit", "Show methods defined in super class and mixed modules too") {|v| inherit = v }
203
271
  end.order!(args)
204
272
 
205
273
  unless args.size == 1
@@ -208,16 +276,14 @@ module RBS
208
276
  end
209
277
 
210
278
  loader = EnvironmentLoader.new()
211
-
212
279
  options.setup(loader)
213
280
 
214
- env = Environment.new()
215
- loader.load(env: env)
281
+ env = Environment.from_loader(loader).resolve_type_names
216
282
 
217
283
  builder = DefinitionBuilder.new(env: env)
218
284
  type_name = parse_type_name(args[0]).absolute!
219
285
 
220
- if env.class?(type_name)
286
+ if env.class_decls.key?(type_name)
221
287
  definition = case kind
222
288
  when :instance
223
289
  builder.build_instance(type_name)
@@ -227,7 +293,7 @@ module RBS
227
293
 
228
294
  definition.methods.keys.sort.each do |name|
229
295
  method = definition.methods[name]
230
- if inherit || method.implemented_in == definition.declaration
296
+ if inherit || method.implemented_in == type_name
231
297
  stdout.puts "#{name} (#{method.accessibility})"
232
298
  end
233
299
  end
@@ -240,8 +306,20 @@ module RBS
240
306
  kind = :instance
241
307
 
242
308
  OptionParser.new do |opts|
243
- opts.on("--instance") { kind = :instance }
244
- opts.on("--singleton") { kind = :singleton }
309
+ opts.banner = <<EOU
310
+ Usage: rbs method [options...] [type_name] [method_name]
311
+
312
+ Show the information of the method specified by type_name and method_name.
313
+
314
+ Examples:
315
+
316
+ $ rbs method --instance Kernel puts
317
+ $ rbs method --singleton String try_convert
318
+
319
+ Options:
320
+ EOU
321
+ opts.on("--instance", "Show an instance method (default)") { kind = :instance }
322
+ opts.on("--singleton", "Show a singleton method") { kind = :singleton }
245
323
  end.order!(args)
246
324
 
247
325
  unless args.size == 2
@@ -250,17 +328,15 @@ module RBS
250
328
  end
251
329
 
252
330
  loader = EnvironmentLoader.new()
253
-
254
331
  options.setup(loader)
255
332
 
256
- env = Environment.new()
257
- loader.load(env: env)
333
+ env = Environment.from_loader(loader).resolve_type_names
258
334
 
259
335
  builder = DefinitionBuilder.new(env: env)
260
336
  type_name = parse_type_name(args[0]).absolute!
261
337
  method_name = args[1].to_sym
262
338
 
263
- unless env.class?(type_name)
339
+ unless env.class_decls.key?(type_name)
264
340
  stdout.puts "Cannot find class: #{type_name}"
265
341
  return
266
342
  end
@@ -280,8 +356,8 @@ module RBS
280
356
  end
281
357
 
282
358
  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!}"
359
+ stdout.puts " defined_in: #{method.defined_in}"
360
+ stdout.puts " implementation: #{method.implemented_in}"
285
361
  stdout.puts " accessibility: #{method.accessibility}"
286
362
  stdout.puts " types:"
287
363
  separator = " "
@@ -292,46 +368,57 @@ module RBS
292
368
  end
293
369
 
294
370
  def run_validate(args, options)
371
+ OptionParser.new do |opts|
372
+ opts.banner = <<EOU
373
+ Usage: rbs validate
374
+
375
+ Validate RBS files. It ensures the type names in RBS files are present and the type applications have correct arity.
376
+
377
+ Examples:
378
+
379
+ $ rbs validate
380
+ EOU
381
+ end.parse!(args)
382
+
295
383
  loader = EnvironmentLoader.new()
296
384
 
297
385
  options.setup(loader)
298
386
 
299
- env = Environment.new()
300
- loader.load(env: env)
387
+ env = Environment.from_loader(loader).resolve_type_names
301
388
 
302
389
  builder = DefinitionBuilder.new(env: env)
390
+ validator = Validator.new(env: env, resolver: TypeNameResolver.from_env(env))
303
391
 
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
392
+ env.class_decls.each_key do |name|
393
+ stdout.puts "Validating class/module definition: `#{name}`..."
394
+ builder.build_instance(name).each_type do |type|
395
+ validator.validate_type type, context: [Namespace.root]
396
+ end
397
+ builder.build_singleton(name).each_type do |type|
398
+ validator.validate_type type, context: [Namespace.root]
319
399
  end
320
400
  end
321
401
 
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
402
+ env.interface_decls.each_key do |name|
403
+ stdout.puts "Validating interface: `#{name}`..."
404
+ builder.build_interface(name).each_type do |type|
405
+ validator.validate_type type, context: [Namespace.root]
406
+ end
325
407
  end
326
408
 
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
409
+ env.constant_decls.each do |name, const|
410
+ stdout.puts "Validating constant: `#{name}`..."
411
+ validator.validate_type const.decl.type, context: const.context
330
412
  end
331
413
 
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
414
+ env.global_decls.each do |name, global|
415
+ stdout.puts "Validating global: `#{name}`..."
416
+ validator.validate_type global.decl.type, context: [Namespace.root]
417
+ end
418
+
419
+ env.alias_decls.each do |name, decl|
420
+ stdout.puts "Validating alias: `#{name}`..."
421
+ validator.validate_type decl.decl.type, context: decl.context
335
422
  end
336
423
  end
337
424
 
@@ -339,7 +426,20 @@ module RBS
339
426
  context = nil
340
427
 
341
428
  OptionParser.new do |opts|
342
- opts.on("--context CONTEXT") {|c| context = c }
429
+ opts.banner = <<EOU
430
+ Usage: rbs constant [options...] [name]
431
+
432
+ Resolve constant based on RBS.
433
+
434
+ Examples:
435
+
436
+ $ rbs constant ::Object
437
+ $ rbs constant UTF_8
438
+ $ rbs constant --context=::Encoding UTF_8
439
+
440
+ Options:
441
+ EOU
442
+ opts.on("--context CONTEXT", "Name of the module where the constant resolution starts") {|c| context = c }
343
443
  end.order!(args)
344
444
 
345
445
  unless args.size == 1
@@ -351,8 +451,7 @@ module RBS
351
451
 
352
452
  options.setup(loader)
353
453
 
354
- env = Environment.new()
355
- loader.load(env: env)
454
+ env = Environment.from_loader(loader).resolve_type_names
356
455
 
357
456
  builder = DefinitionBuilder.new(env: env)
358
457
  table = ConstantTable.new(builder: builder)
@@ -371,11 +470,20 @@ module RBS
371
470
  end
372
471
  end
373
472
 
374
- def run_version(args, options)
375
- stdout.puts "rbs #{VERSION}"
376
- end
377
-
378
473
  def run_paths(args, options)
474
+ OptionParser.new do |opts|
475
+ opts.banner = <<EOU
476
+ Usage: rbs paths
477
+
478
+ Show paths to directories where the RBS files are loaded from.
479
+
480
+ Examples:
481
+
482
+ $ rbs paths
483
+ $ tbs -r set paths
484
+ EOU
485
+ end.parse!(args)
486
+
379
487
  loader = EnvironmentLoader.new()
380
488
 
381
489
  options.setup(loader)
@@ -423,16 +531,30 @@ module RBS
423
531
  owners_included = []
424
532
 
425
533
  OptionParser.new do |opts|
426
- opts.on("--require LIB") do |lib|
534
+ opts.banner = <<EOU
535
+ Usage: rbs prototype runtime [options...] [pattern...]
536
+
537
+ Generate RBS prototype based on runtime introspection.
538
+ It loads Ruby code specified in [options] and generates RBS prototypes for classes matches to [pattern].
539
+
540
+ Examples:
541
+
542
+ $ rbs prototype runtime String
543
+ $ rbs prototype runtime --require set Set
544
+ $ rbs prototype runtime -R lib/rbs RBS::*
545
+
546
+ Options:
547
+ EOU
548
+ opts.on("-r", "--require LIB", "Load library using `require`") do |lib|
427
549
  require_libs << lib
428
550
  end
429
- opts.on("--require-relative LIB") do |lib|
551
+ opts.on("-R", "--require-relative LIB", "Load library using `require_relative`") do |lib|
430
552
  relative_libs << lib
431
553
  end
432
- opts.on("--merge") do
554
+ opts.on("--merge", "Merge generated prototype RBS with existing RBS") do
433
555
  merge = true
434
556
  end
435
- opts.on("--method-owner CLASS") do |klass|
557
+ opts.on("--method-owner CLASS", "Generate method prototypes if the owner of the method is [CLASS]") do |klass|
436
558
  owners_included << klass
437
559
  end
438
560
  end.parse!(args)
@@ -441,8 +563,7 @@ module RBS
441
563
 
442
564
  options.setup(loader)
443
565
 
444
- env = Environment.new()
445
- loader.load(env: env)
566
+ env = Environment.from_loader(loader).resolve_type_names
446
567
 
447
568
  require_libs.each do |lib|
448
569
  require(lib)
@@ -454,15 +575,49 @@ module RBS
454
575
 
455
576
  decls = Prototype::Runtime.new(patterns: args, env: env, merge: merge, owners_included: owners_included).decls
456
577
  else
457
- stdout.puts "Supported formats: rbi, rb, runtime"
578
+ stdout.puts <<EOU
579
+ Usage: rbs prototype [generator...] [args...]
580
+
581
+ Generate prototype of RBS files.
582
+ Supported generators are rb, rbi, runtime.
583
+
584
+ Examples:
585
+
586
+ $ rbs prototype rb foo.rb
587
+ $ rbs prototype rbi foo.rbi
588
+ $ rbs prototype runtime String
589
+ EOU
458
590
  exit 1
459
591
  end
460
592
 
461
- writer = Writer.new(out: stdout)
462
- writer.write decls
593
+ if decls
594
+ writer = Writer.new(out: stdout)
595
+ writer.write decls
596
+ else
597
+ exit 1
598
+ end
463
599
  end
464
600
 
465
601
  def run_prototype_file(format, args)
602
+ opts = OptionParser.new
603
+ opts.banner = <<EOU
604
+ Usage: rbs prototype #{format} [options...] [files...]
605
+
606
+ Generate RBS prototype from source code.
607
+ It parses specified Ruby code and and generates RBS prototypes.
608
+
609
+ Examples:
610
+
611
+ $ rbs prototype rb lib/foo.rb
612
+ $ rbs prototype rbi sorbet/rbi/foo.rbi
613
+ EOU
614
+ opts.parse!(args)
615
+
616
+ if args.empty?
617
+ stdout.puts opts
618
+ return nil
619
+ end
620
+
466
621
  parser = case format
467
622
  when "rbi"
468
623
  Prototype::RBI.new()
@@ -483,9 +638,19 @@ module RBS
483
638
  vendor_dir = Pathname("vendor/sigs")
484
639
 
485
640
  OptionParser.new do |opts|
486
- opts.banner = <<~EOB
487
- Usage: rbs vendor [options] GEMS...
488
- Vendor signatures in the project directory.
641
+ opts.banner = <<-EOB
642
+ Usage: rbs vendor [options...] [gems...]
643
+
644
+ Vendor signatures in the project directory.
645
+ This command ignores the RBS loading global options, `-r` and `-I`.
646
+
647
+ Examples:
648
+
649
+ $ rbs vendor
650
+ $ rbs vendor --vendor-dir=sig
651
+ $ rbs vendor --no-stdlib
652
+
653
+ Options:
489
654
  EOB
490
655
 
491
656
  opts.on("--[no-]clean", "Clean vendor directory (default: no)") do |v|
@@ -528,6 +693,18 @@ module RBS
528
693
  end
529
694
 
530
695
  def run_parse(args, options)
696
+ OptionParser.new do |opts|
697
+ opts.banner = <<-EOB
698
+ Usage: rbs parse [files...]
699
+
700
+ Parse given RBS files and print syntax errors.
701
+
702
+ Examples:
703
+
704
+ $ rbs parse sig/app/models.rbs sig/app/controllers.rbs
705
+ EOB
706
+ end.parse!(args)
707
+
531
708
  loader = EnvironmentLoader.new()
532
709
 
533
710
  syntax_error = false
@@ -545,6 +722,7 @@ module RBS
545
722
  syntax_error = true
546
723
  end
547
724
  end
725
+
548
726
  exit 1 if syntax_error
549
727
  end
550
728