sord 4.0.0 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +3 -3
- data/CHANGELOG.md +25 -0
- data/README.md +2 -0
- data/exe/sord +5 -1
- data/lib/sord/generator.rb +57 -24
- data/lib/sord/parlour_plugin.rb +21 -5
- data/lib/sord/resolver.rb +65 -8
- data/lib/sord/type_converter.rb +102 -24
- data/lib/sord/version.rb +1 -1
- data/rbi/sord.rbi +147 -40
- data/sord.gemspec +1 -0
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 12567f3b935a14271aedb28b62acfcfef3cb1e3bfb80fd294732947490dfb765
|
4
|
+
data.tar.gz: c40c2790500508fd2e575ab01e471cf7fe0e90f0c6d255285c7474433b65952c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85435a86928840302557121d0deeafde94a0b70905da93403c642a303ec773f5f4503b316beed31709052839a597e671df9ac554410d4c169843e859aebf361d
|
7
|
+
data.tar.gz: 77795ca64b509b9c7e7da210fac12d2b27bb8fe9e952ab61dc1d87c92189a1d0e457a4546bce2cebac4230a643f96072559c39bd477fc7e345819c98dd0b3bdd
|
data/.github/workflows/ruby.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -3,6 +3,31 @@ All notable changes to this project will be documented in this file.
|
|
3
3
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
5
5
|
|
6
|
+
## [5.0.0] - 2022-10-06
|
7
|
+
### Added
|
8
|
+
- If a derived class does not provide tags for a method, but it is overridden from a base class
|
9
|
+
which does, then the base class' documentation will be used to generate types for the derived
|
10
|
+
method.
|
11
|
+
- When generating RBS, if a duck type matches one of RBS' built-in interfaces, this type will be
|
12
|
+
generated instead. (For example, `#to_s` will generate the type `_ToS`.)
|
13
|
+
- Added the `--hide-private` flag, which will omit outputting items with private visibility.
|
14
|
+
- To improve resolution, types for gems are now loaded from the RBS collection.
|
15
|
+
- If you are using custom YARD tags, Sord can now be made aware of these by passing the `--tags`
|
16
|
+
option.
|
17
|
+
|
18
|
+
### Changed
|
19
|
+
- **Breaking change**: Support for versions of Ruby prior to 2.7 has been dropped.
|
20
|
+
- When Sord runs YARD automatically, it no longer generates HTML documentation, since this isn't
|
21
|
+
necessary for Sord's analysis. If you were relying on this as part of your workflow, then this
|
22
|
+
could be a **breaking change**.
|
23
|
+
|
24
|
+
### Fixed
|
25
|
+
- Duck-typed methods ending with `?` or `!`, and operator methods like `#[]=`, are now correctly
|
26
|
+
recognised as duck types.
|
27
|
+
- Fixed an exception when referring to built-in types with root namespace (`::Array<Foo>`) syntax.
|
28
|
+
- `@yieldparams` without a parameter name no longer cause an exception, and instead use default
|
29
|
+
names of the pattern: `arg0`, `arg1`, ...
|
30
|
+
|
6
31
|
## [4.0.0] - 2022-07-19
|
7
32
|
### Added
|
8
33
|
- Constants are now assigned types when generating RBS, using `@return`.
|
data/README.md
CHANGED
@@ -85,6 +85,8 @@ Sord also takes some flags to alter the generated file:
|
|
85
85
|
(You cannot specify both `--include-messages` and `--exclude-messages`.)
|
86
86
|
- `--exclude-untyped`: Exclude methods and attributes with untyped return
|
87
87
|
values.
|
88
|
+
- `--tags TAGS`: Provide a list of comma-separated tags as understood by the
|
89
|
+
`yard` command. E.g. `--tags 'mytag:My Description,mytag2:My New Description'
|
88
90
|
|
89
91
|
## Example
|
90
92
|
|
data/exe/sord
CHANGED
@@ -22,8 +22,10 @@ command :gen do |c|
|
|
22
22
|
c.option '--include-messages STRING', String, 'Whitelists a comma-separated string of log message types'
|
23
23
|
c.option '--keep-original-comments', 'Retains original YARD comments rather than converting them to Markdown'
|
24
24
|
c.option '--skip-constants', 'Excludes constants from generated file'
|
25
|
+
c.option '--hide-private', 'Exclude any object marked with @!visibility private'
|
25
26
|
c.option '--use-original-initialize-return', 'Uses the specified return type for #initialize rather than void'
|
26
27
|
c.option '--exclude-untyped', 'Exclude methods and attributes with untyped return values'
|
28
|
+
c.option '--tags TAGS', Array, 'Tag parameters for the YARD command'
|
27
29
|
|
28
30
|
c.action do |args, options|
|
29
31
|
options.default(
|
@@ -34,12 +36,14 @@ command :gen do |c|
|
|
34
36
|
break_params: 4,
|
35
37
|
replace_errors_with_untyped: false,
|
36
38
|
replace_unresolved_with_untyped: false,
|
39
|
+
hide_private: false,
|
37
40
|
exclude_messages: nil,
|
38
41
|
include_messages: nil,
|
39
42
|
keep_original_comments: false,
|
40
43
|
skip_constants: false,
|
41
44
|
use_original_initialize_return: false,
|
42
45
|
exclude_untyped: false,
|
46
|
+
tags: [],
|
43
47
|
)
|
44
48
|
|
45
49
|
if args.length != 1
|
@@ -86,7 +90,7 @@ command :gen do |c|
|
|
86
90
|
|
87
91
|
plugin.parlour = klass.new(break_params: plugin_options[:break_params])
|
88
92
|
plugin.generate(plugin.parlour.root)
|
89
|
-
|
93
|
+
|
90
94
|
File.write(args.first, plugin.parlour.send(generator_method))
|
91
95
|
end
|
92
96
|
end
|
data/lib/sord/generator.rb
CHANGED
@@ -28,6 +28,7 @@ module Sord
|
|
28
28
|
# @option options [Integer] break_params
|
29
29
|
# @option options [Boolean] replace_errors_with_untyped
|
30
30
|
# @option options [Boolean] replace_unresolved_with_untyped
|
31
|
+
# @option options [Boolean] hide_private
|
31
32
|
# @option options [Boolean] comments
|
32
33
|
# @option options [Parlour::Generator] generator
|
33
34
|
# @option options [Parlour::TypedObject] root
|
@@ -53,18 +54,25 @@ module Sord
|
|
53
54
|
@replace_errors_with_untyped = options[:replace_errors_with_untyped]
|
54
55
|
@replace_unresolved_with_untyped = options[:replace_unresolved_with_untyped]
|
55
56
|
@keep_original_comments = options[:keep_original_comments]
|
57
|
+
@hide_private = options[:hide_private]
|
56
58
|
@skip_constants = options[:skip_constants]
|
57
59
|
@use_original_initialize_return = options[:use_original_initialize_return]
|
58
60
|
@exclude_untyped = options[:exclude_untyped]
|
59
61
|
|
62
|
+
@type_converter_config = TypeConverter::Configuration.new(
|
63
|
+
output_language: @mode,
|
64
|
+
replace_errors_with_untyped: @replace_errors_with_untyped,
|
65
|
+
replace_unresolved_with_untyped: @replace_unresolved_with_untyped,
|
66
|
+
)
|
67
|
+
|
60
68
|
# Hook the logger so that messages are added as comments
|
61
69
|
Logging.add_hook do |type, msg, item, **opts|
|
62
70
|
# Hack: the "exclude untyped" log message needs to print somewhere, but
|
63
71
|
# if there's no future object for that comment to associate with, it'll
|
64
72
|
# never be printed!
|
65
|
-
# Instead, add an arbitrary containing the comment
|
73
|
+
# Instead, add an arbitrary containing the comment
|
66
74
|
if opts[:immediate]
|
67
|
-
@current_object.create_arbitrary(code: "# sord #{type} - #{msg}")
|
75
|
+
@current_object.create_arbitrary(code: "# sord #{type} - #{msg}")
|
68
76
|
else
|
69
77
|
@current_object.add_comment_to_next_child("sord #{type} - #{msg}")
|
70
78
|
end
|
@@ -120,6 +128,7 @@ module Sord
|
|
120
128
|
inserted_constant_names = Set.new
|
121
129
|
|
122
130
|
item.constants(included: false).each do |constant|
|
131
|
+
next if @hide_private && constant.visibility == :private
|
123
132
|
# Take a constant (like "A::B::CONSTANT"), split it on each '::', and
|
124
133
|
# set the constant name to the last string in the array.
|
125
134
|
constant_name = constant.to_s.split('::').last
|
@@ -144,8 +153,7 @@ module Sord
|
|
144
153
|
TypeConverter.yard_to_parlour(
|
145
154
|
return_tags.map(&:types).flatten,
|
146
155
|
constant,
|
147
|
-
@
|
148
|
-
@replace_unresolved_with_untyped
|
156
|
+
@type_converter_config,
|
149
157
|
)
|
150
158
|
end
|
151
159
|
@current_object.create_constant(constant_name, type: returns) do |c|
|
@@ -246,12 +254,33 @@ module Sord
|
|
246
254
|
end
|
247
255
|
end
|
248
256
|
|
257
|
+
# @param method [YARD::CodeObjects::MethodObject]
|
258
|
+
# @param tag_name [String]
|
259
|
+
# @return [Array<YARD::Tags::Tag>]
|
260
|
+
def method_tags(method, tag_name)
|
261
|
+
tags = method.tags(tag_name)
|
262
|
+
return tags if tags.any? || method.overridden_method.nil?
|
263
|
+
|
264
|
+
method.overridden_method.tags(tag_name)
|
265
|
+
end
|
266
|
+
|
267
|
+
# @param method [YARD::CodeObjects::MethodObject]
|
268
|
+
# @param tag_name [String]
|
269
|
+
# @return [YARD::Tags::Tag, nil]
|
270
|
+
def method_tag(method, tag_name)
|
271
|
+
tag = method.tag(tag_name)
|
272
|
+
return tag if tag || method.overridden_method.nil?
|
273
|
+
|
274
|
+
method.overridden_method.tag(tag_name)
|
275
|
+
end
|
276
|
+
|
249
277
|
# Given a YARD NamespaceObject, add lines defining its methods and their
|
250
278
|
# signatures to the current file.
|
251
279
|
# @param [YARD::CodeObjects::NamespaceObject] item
|
252
280
|
# @return [void]
|
253
281
|
def add_methods(item)
|
254
282
|
item.meths(inherited: false).each do |meth|
|
283
|
+
next if @hide_private && meth.visibility == :private
|
255
284
|
count_method
|
256
285
|
|
257
286
|
# If the method is an alias, skip it so we don't define it as a
|
@@ -267,17 +296,18 @@ module Sord
|
|
267
296
|
|
268
297
|
# Sort parameters
|
269
298
|
meth.parameters.reverse.sort! { |pair1, pair2| sort_params(pair1, pair2) }
|
299
|
+
|
270
300
|
# This is better than iterating over YARD's "@param" tags directly
|
271
301
|
# because it includes parameters without documentation
|
272
302
|
# (The gsubs allow for better splat-argument compatibility)
|
273
303
|
parameter_names_and_defaults_to_tags = meth.parameters.map do |name, default|
|
274
|
-
[[name, fix_default_if_unary_minus(default)], meth
|
304
|
+
[[name, fix_default_if_unary_minus(default)], method_tags(meth, 'param')
|
275
305
|
.find { |p| p.name&.gsub('*', '')&.gsub(':', '') == name.gsub('*', '').gsub(':', '') }]
|
276
306
|
end.to_h
|
277
307
|
|
278
308
|
# Add block param if there is no named param but YARD tags are present
|
279
309
|
if !parameter_names_and_defaults_to_tags.any? { |((name, _), _)| name.start_with? '&' } \
|
280
|
-
&& (meth
|
310
|
+
&& (method_tags(meth, 'yieldparam').any? || method_tag(meth, 'yieldreturn'))
|
281
311
|
parameter_names_and_defaults_to_tags[['&blk']] = nil
|
282
312
|
end
|
283
313
|
|
@@ -285,22 +315,22 @@ module Sord
|
|
285
315
|
name = name_and_default.first
|
286
316
|
|
287
317
|
if tag
|
288
|
-
TypeConverter.yard_to_parlour(tag.types, meth, @
|
318
|
+
TypeConverter.yard_to_parlour(tag.types, meth, @type_converter_config)
|
289
319
|
elsif name.start_with? '&'
|
290
320
|
# Find yieldparams and yieldreturn
|
291
|
-
yieldparams = meth
|
292
|
-
yieldreturn = meth
|
321
|
+
yieldparams = method_tags(meth, 'yieldparam')
|
322
|
+
yieldreturn = method_tag(meth, 'yieldreturn')&.types
|
293
323
|
yieldreturn = nil if yieldreturn&.length == 1 &&
|
294
324
|
yieldreturn&.first&.downcase == 'void'
|
295
325
|
|
296
326
|
# Create strings
|
297
|
-
params = yieldparams.map do |param|
|
327
|
+
params = yieldparams.map.with_index do |param, i|
|
298
328
|
Parlour::Types::Proc::Parameter.new(
|
299
|
-
param.name
|
300
|
-
TypeConverter.yard_to_parlour(param.types, meth, @
|
329
|
+
param.name&.gsub('*', '') || "arg#{i}",
|
330
|
+
TypeConverter.yard_to_parlour(param.types, meth, @type_converter_config)
|
301
331
|
)
|
302
332
|
end
|
303
|
-
returns = TypeConverter.yard_to_parlour(yieldreturn, meth, @
|
333
|
+
returns = TypeConverter.yard_to_parlour(yieldreturn, meth, @type_converter_config)
|
304
334
|
|
305
335
|
# Create proc types, if possible
|
306
336
|
if yieldparams.empty? && yieldreturn.nil?
|
@@ -315,24 +345,24 @@ module Sord
|
|
315
345
|
|
316
346
|
unless getter
|
317
347
|
if parameter_names_and_defaults_to_tags.length == 1 \
|
318
|
-
&& meth
|
319
|
-
&& meth
|
348
|
+
&& method_tags(meth, 'param').length == 1 \
|
349
|
+
&& method_tag(meth, 'param').types
|
320
350
|
|
321
351
|
Logging.infer("argument name in single @param inferred as #{parameter_names_and_defaults_to_tags.first.first.first.inspect}", meth)
|
322
|
-
next TypeConverter.yard_to_parlour(meth
|
352
|
+
next TypeConverter.yard_to_parlour(method_tag(meth, 'param').types, meth, @type_converter_config)
|
323
353
|
else
|
324
354
|
Logging.omit("no YARD type given for #{name.inspect}, using untyped", meth)
|
325
355
|
next Parlour::Types::Untyped.new
|
326
356
|
end
|
327
357
|
end
|
328
358
|
|
329
|
-
return_types = getter
|
359
|
+
return_types = method_tags(getter, 'return').flat_map(&:types)
|
330
360
|
unless return_types.any?
|
331
361
|
Logging.omit("no YARD type given for #{name.inspect}, using untyped", meth)
|
332
362
|
next Parlour::Types::Untyped.new
|
333
363
|
end
|
334
364
|
inferred_type = TypeConverter.yard_to_parlour(
|
335
|
-
return_types, meth, @
|
365
|
+
return_types, meth, @type_converter_config)
|
336
366
|
|
337
367
|
Logging.infer("inferred type of parameter #{name.inspect} as #{inferred_type.describe} using getter's return type", meth)
|
338
368
|
inferred_type
|
@@ -340,11 +370,11 @@ module Sord
|
|
340
370
|
# Is this the only argument, and was a @param specified without an
|
341
371
|
# argument name? If so, infer it
|
342
372
|
if parameter_names_and_defaults_to_tags.length == 1 \
|
343
|
-
&& meth
|
344
|
-
&& meth
|
373
|
+
&& method_tags(meth, 'param').length == 1 \
|
374
|
+
&& method_tag(meth, 'param').types
|
345
375
|
|
346
376
|
Logging.infer("argument name in single @param inferred as #{parameter_names_and_defaults_to_tags.first.first.first.inspect}", meth)
|
347
|
-
TypeConverter.yard_to_parlour(meth
|
377
|
+
TypeConverter.yard_to_parlour(method_tag(meth, 'param').types, meth, @type_converter_config)
|
348
378
|
else
|
349
379
|
Logging.omit("no YARD type given for #{name.inspect}, using untyped", meth)
|
350
380
|
Parlour::Types::Untyped.new
|
@@ -352,7 +382,7 @@ module Sord
|
|
352
382
|
end
|
353
383
|
end
|
354
384
|
|
355
|
-
return_tags = meth
|
385
|
+
return_tags = method_tags(meth, 'return')
|
356
386
|
returns = if meth.name == :initialize && !@use_original_initialize_return
|
357
387
|
nil
|
358
388
|
elsif return_tags.length == 0
|
@@ -361,7 +391,7 @@ module Sord
|
|
361
391
|
elsif return_tags.length == 1 && %w{void nil}.include?(return_tags&.first&.types&.first&.downcase)
|
362
392
|
nil
|
363
393
|
else
|
364
|
-
TypeConverter.yard_to_parlour(meth
|
394
|
+
TypeConverter.yard_to_parlour(method_tag(meth, 'return').types, meth, @type_converter_config)
|
365
395
|
end
|
366
396
|
|
367
397
|
rbs_block = nil
|
@@ -447,10 +477,12 @@ module Sord
|
|
447
477
|
# Get all given types
|
448
478
|
yard_types = []
|
449
479
|
if reader
|
480
|
+
next if @hide_private && reader.visibility == :private
|
450
481
|
yard_types += reader.tags('return').flat_map(&:types).compact.reject { |x| x.downcase == 'void' } +
|
451
482
|
reader.tags('param').flat_map(&:types)
|
452
483
|
end
|
453
484
|
if writer
|
485
|
+
next if @hide_private && writer.visibility == :private
|
454
486
|
yard_types += writer.tags('return').flat_map(&:types).compact.reject { |x| x.downcase == 'void' } +
|
455
487
|
writer.tags('param').flat_map(&:types)
|
456
488
|
end
|
@@ -465,7 +497,7 @@ module Sord
|
|
465
497
|
parlour_type = Parlour::Types::Untyped.new
|
466
498
|
else
|
467
499
|
parlour_type = TypeConverter.yard_to_parlour(
|
468
|
-
yard_types, reader || writer, @
|
500
|
+
yard_types, reader || writer, @type_converter_config)
|
469
501
|
end
|
470
502
|
|
471
503
|
# Generate attribute
|
@@ -538,6 +570,7 @@ module Sord
|
|
538
570
|
# @param [YARD::CodeObjects::NamespaceObject] item
|
539
571
|
# @return [void]
|
540
572
|
def add_namespace(item)
|
573
|
+
return if @hide_private && item.visibility == :private
|
541
574
|
count_namespace
|
542
575
|
|
543
576
|
superclass = nil
|
data/lib/sord/parlour_plugin.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# typed: true
|
2
2
|
require 'parlour'
|
3
|
+
require 'yard/tags/library'
|
3
4
|
|
4
5
|
module Sord
|
5
6
|
class ParlourPlugin < Parlour::Plugin
|
@@ -42,17 +43,21 @@ module Sord
|
|
42
43
|
Sord::Logging.error('No output format given; please specify --rbi or --rbs')
|
43
44
|
exit 1
|
44
45
|
end
|
45
|
-
|
46
|
+
|
46
47
|
if (options[:rbi] && options[:rbs])
|
47
48
|
Sord::Logging.error('You cannot specify both --rbi and --rbs; please use only one')
|
48
49
|
exit 1
|
49
50
|
end
|
50
|
-
|
51
|
+
|
51
52
|
if options[:regenerate]
|
52
53
|
begin
|
53
54
|
Sord::Logging.info('Running YARD...')
|
54
55
|
Sord::ParlourPlugin.with_clean_env do
|
55
|
-
|
56
|
+
tag_param = ''
|
57
|
+
options[:tags]&.each do |tag|
|
58
|
+
tag_param += "--tag #{tag} "
|
59
|
+
end
|
60
|
+
system("bundle exec yard #{tag_param} --no-output")
|
56
61
|
end
|
57
62
|
rescue Errno::ENOENT
|
58
63
|
Sord::Logging.error('The YARD tool could not be found on your PATH.')
|
@@ -63,15 +68,26 @@ module Sord
|
|
63
68
|
end
|
64
69
|
|
65
70
|
options[:mode] = \
|
66
|
-
if options[:rbi] then :rbi elsif options[:rbs] then :rbs end
|
71
|
+
if options[:rbi] then :rbi elsif options[:rbs] then :rbs end
|
67
72
|
options[:parlour] = @parlour
|
68
73
|
options[:root] = root
|
69
74
|
|
75
|
+
add_custom_tags
|
76
|
+
|
70
77
|
Sord::Generator.new(options).run
|
71
78
|
|
72
79
|
true
|
73
80
|
end
|
74
81
|
|
82
|
+
def add_custom_tags
|
83
|
+
return if options[:tags].empty?
|
84
|
+
|
85
|
+
options[:tags].each do |tag|
|
86
|
+
name, description = tag.split(':')
|
87
|
+
YARD::Tags::Library.define_tag(description, name)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
75
91
|
def self.with_clean_env &block
|
76
92
|
meth = if Bundler.respond_to?(:with_unbundled_env)
|
77
93
|
:with_unbundled_env
|
@@ -81,4 +97,4 @@ module Sord
|
|
81
97
|
Bundler.send meth, &block
|
82
98
|
end
|
83
99
|
end
|
84
|
-
end
|
100
|
+
end
|
data/lib/sord/resolver.rb
CHANGED
@@ -1,23 +1,80 @@
|
|
1
1
|
# typed: false
|
2
2
|
require 'stringio'
|
3
|
+
require 'rbs'
|
4
|
+
require 'rbs/cli'
|
3
5
|
|
4
6
|
module Sord
|
5
7
|
module Resolver
|
6
8
|
# @return [void]
|
7
9
|
def self.prepare
|
10
|
+
return @names_to_paths if @names_to_paths
|
11
|
+
|
12
|
+
gem_objects = {}
|
13
|
+
load_gem_objects(gem_objects)
|
14
|
+
|
8
15
|
# Construct a hash of class names to full paths
|
9
|
-
|
16
|
+
@names_to_paths = YARD::Registry.all(:class)
|
10
17
|
.group_by(&:name)
|
11
|
-
.map { |k, v| [k.to_s, v.map(&:path)] }
|
18
|
+
.map { |k, v| [k.to_s, v.map(&:path).to_set] }
|
12
19
|
.to_h
|
13
|
-
.merge(builtin_classes.map { |x| [x, [x]] }.to_h)
|
14
|
-
|
20
|
+
.merge(builtin_classes.map { |x| [x, Set.new([x])] }.to_h) { |_k, a, b| a.union(b) }
|
21
|
+
.merge(gem_objects) { |_k, a, b| a.union(b) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.load_gem_objects(hash)
|
25
|
+
all_decls = []
|
26
|
+
begin
|
27
|
+
RBS::CLI::LibraryOptions.new.loader.load(env: all_decls)
|
28
|
+
rescue RBS::Collection::Config::CollectionNotAvailable
|
29
|
+
Sord::Logging.warn("Could not load RBS collection - run rbs collection install for dependencies")
|
30
|
+
end
|
31
|
+
add_rbs_objects_to_paths(all_decls, hash)
|
32
|
+
|
33
|
+
gem_paths = Bundler.load.specs.map(&:full_gem_path)
|
34
|
+
gem_paths.each do |path|
|
35
|
+
if File.exists?("#{path}/rbi")
|
36
|
+
Dir["#{path}/rbi/**/*.rbi"].each do |sigfile|
|
37
|
+
tree = Parlour::TypeLoader.load_file(sigfile)
|
38
|
+
add_rbi_objects_to_paths(tree.children, hash)
|
39
|
+
end
|
15
40
|
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.add_rbs_objects_to_paths(all_decls, names_to_paths, path=[])
|
45
|
+
klasses = [
|
46
|
+
RBS::AST::Declarations::Module,
|
47
|
+
RBS::AST::Declarations::Class,
|
48
|
+
RBS::AST::Declarations::Constant
|
49
|
+
]
|
50
|
+
all_decls.each do |decl|
|
51
|
+
next unless klasses.include?(decl.class)
|
52
|
+
name = decl.name.to_s
|
53
|
+
new_path = path + [name]
|
54
|
+
names_to_paths[name] ||= Set.new
|
55
|
+
names_to_paths[name] << new_path.join('::')
|
56
|
+
add_rbs_objects_to_paths(decl.members, names_to_paths, new_path) if decl.respond_to?(:members)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.add_rbi_objects_to_paths(nodes, names_to_paths, path=[])
|
61
|
+
klasses = [
|
62
|
+
Parlour::RbiGenerator::Constant,
|
63
|
+
Parlour::RbiGenerator::ModuleNamespace,
|
64
|
+
Parlour::RbiGenerator::ClassNamespace
|
65
|
+
]
|
66
|
+
nodes.each do |node|
|
67
|
+
next unless klasses.include?(node.class)
|
68
|
+
new_path = path + [node.name]
|
69
|
+
names_to_paths[node.name] ||= Set.new
|
70
|
+
names_to_paths[node.name] << new_path.join('::')
|
71
|
+
add_rbi_objects_to_paths(node.children, names_to_paths, new_path) if node.respond_to?(:children)
|
72
|
+
end
|
16
73
|
end
|
17
74
|
|
18
75
|
# @return [void]
|
19
76
|
def self.clear
|
20
|
-
|
77
|
+
@names_to_paths = nil
|
21
78
|
end
|
22
79
|
|
23
80
|
# @param [String] name
|
@@ -28,7 +85,7 @@ module Sord
|
|
28
85
|
# If the name starts with ::, then we've been given an explicit path from root - just use that
|
29
86
|
return [name] if name.start_with?('::')
|
30
87
|
|
31
|
-
(
|
88
|
+
(@names_to_paths[name.split('::').last] || [])
|
32
89
|
.select { |x| x.end_with?(name) }
|
33
90
|
end
|
34
91
|
|
@@ -43,9 +100,9 @@ module Sord
|
|
43
100
|
# This prints some deprecation warnings, so suppress them
|
44
101
|
prev_stderr = $stderr
|
45
102
|
$stderr = StringIO.new
|
46
|
-
|
103
|
+
|
47
104
|
major = RUBY_VERSION.split('.').first.to_i
|
48
|
-
sorted_set_removed = major >= 3
|
105
|
+
sorted_set_removed = major >= 3
|
49
106
|
|
50
107
|
Object.constants
|
51
108
|
.reject { |x| sorted_set_removed && x == :SortedSet }
|
data/lib/sord/type_converter.rb
CHANGED
@@ -21,10 +21,15 @@ module Sord
|
|
21
21
|
GENERIC_TYPE_REGEX =
|
22
22
|
/(#{SIMPLE_TYPE_REGEX})\s*[<{]\s*(.*)\s*[>}]/
|
23
23
|
|
24
|
+
# Matches valid method names.
|
25
|
+
# From: https://stackoverflow.com/a/4379197/2626000
|
26
|
+
METHOD_NAME_REGEX =
|
27
|
+
/(?:[a-z_]\w*[?!=]?|\[\]=?|<<|>>|\*\*|[!~+\*\/%&^|-]|[<>]=?|<=>|={2,3}|![=~]|=~)/i
|
28
|
+
|
24
29
|
# Match duck types which require the object implement one or more methods,
|
25
30
|
# like '#foo', '#foo & #bar', '#foo&#bar&#baz', and '#foo&#bar&#baz&#foo_bar'.
|
26
31
|
DUCK_TYPE_REGEX =
|
27
|
-
|
32
|
+
/^\##{METHOD_NAME_REGEX}(?:\s*\&\s*\##{METHOD_NAME_REGEX})*$/
|
28
33
|
|
29
34
|
# A regular expression which matches ordered lists in the format of
|
30
35
|
# either "Array(String, Symbol)" or "(String, Symbol)".
|
@@ -90,17 +95,35 @@ module Sord
|
|
90
95
|
result
|
91
96
|
end
|
92
97
|
|
98
|
+
# Configuration for how the type converter should work in particular cases.
|
99
|
+
class Configuration
|
100
|
+
def initialize(replace_errors_with_untyped:, replace_unresolved_with_untyped:, output_language:)
|
101
|
+
@output_language = output_language
|
102
|
+
@replace_errors_with_untyped = replace_errors_with_untyped
|
103
|
+
@replace_unresolved_with_untyped = replace_unresolved_with_untyped
|
104
|
+
end
|
105
|
+
|
106
|
+
# The language which the generated types will be converted to - one of
|
107
|
+
# `:rbi` or `:rbs`.
|
108
|
+
attr_accessor :output_language
|
109
|
+
|
110
|
+
# @return [Boolean] If true, T.untyped is used instead of SORD_ERROR_
|
111
|
+
# constants for unknown types.
|
112
|
+
attr_accessor :replace_errors_with_untyped
|
113
|
+
|
114
|
+
# @param [Boolean] replace_unresolved_with_untyped If true, T.untyped is
|
115
|
+
# used when Sord is unable to resolve a constant.
|
116
|
+
attr_accessor :replace_unresolved_with_untyped
|
117
|
+
end
|
118
|
+
|
93
119
|
# Converts a YARD type into a Parlour type.
|
94
120
|
# @param [Boolean, Array, String] yard The YARD type.
|
95
121
|
# @param [YARD::CodeObjects::Base] item The CodeObject which the YARD type
|
96
122
|
# is associated with. This is used for logging and can be nil, but this
|
97
123
|
# will lead to less informative log messages.
|
98
|
-
# @param [
|
99
|
-
# instead of SORD_ERROR_ constants for unknown types.
|
100
|
-
# @param [Boolean] replace_unresolved_with_untyped If true, T.untyped is used
|
101
|
-
# when Sord is unable to resolve a constant.
|
124
|
+
# @param [Configuration] config The generation configuration.
|
102
125
|
# @return [Parlour::Types::Type]
|
103
|
-
def self.yard_to_parlour(yard, item
|
126
|
+
def self.yard_to_parlour(yard, item, config)
|
104
127
|
case yard
|
105
128
|
when nil # Type not specified
|
106
129
|
Parlour::Types::Untyped.new
|
@@ -113,7 +136,7 @@ module Sord
|
|
113
136
|
# selection of any of the types
|
114
137
|
types = yard
|
115
138
|
.reject { |x| x == 'nil' }
|
116
|
-
.map { |x| yard_to_parlour(x, item,
|
139
|
+
.map { |x| yard_to_parlour(x, item, config) }
|
117
140
|
.uniq(&:hash)
|
118
141
|
result = types.length == 1 \
|
119
142
|
? types.first
|
@@ -142,7 +165,7 @@ module Sord
|
|
142
165
|
unless yard == new_path
|
143
166
|
Parlour::Types::Raw.new(new_path)
|
144
167
|
else
|
145
|
-
if replace_unresolved_with_untyped
|
168
|
+
if config.replace_unresolved_with_untyped
|
146
169
|
Logging.warn("#{yard} wasn't able to be resolved to a constant in this project, replaced with untyped", item)
|
147
170
|
Parlour::Types::Untyped.new
|
148
171
|
else
|
@@ -154,33 +177,43 @@ module Sord
|
|
154
177
|
Parlour::Types::Raw.new(yard)
|
155
178
|
end
|
156
179
|
when DUCK_TYPE_REGEX
|
157
|
-
|
158
|
-
|
180
|
+
if config.output_language == :rbs && (type = duck_type_to_rbs_type(yard))
|
181
|
+
Logging.duck("#{yard} looks like a duck type with an equivalent RBS interface, replacing with #{type.generate_rbs}", item)
|
182
|
+
type
|
183
|
+
else
|
184
|
+
Logging.duck("#{yard} looks like a duck type, replacing with untyped", item)
|
185
|
+
Parlour::Types::Untyped.new
|
186
|
+
end
|
159
187
|
when /^#{GENERIC_TYPE_REGEX}$/
|
160
188
|
generic_type = $1
|
161
189
|
type_parameters = $2
|
162
190
|
|
191
|
+
# If we don't do this, `const_defined?` will resolve "::Array" as the actual Ruby `Array`
|
192
|
+
# type, not `Parlour::Types::Array`!
|
193
|
+
relative_generic_type = generic_type.start_with?('::') \
|
194
|
+
? generic_type[2..-1] : generic_type
|
195
|
+
|
163
196
|
parameters = split_type_parameters(type_parameters)
|
164
|
-
.map { |x| yard_to_parlour(x, item,
|
165
|
-
if SINGLE_ARG_GENERIC_TYPES.include?(
|
166
|
-
Parlour::Types.const_get(
|
167
|
-
elsif
|
197
|
+
.map { |x| yard_to_parlour(x, item, config) }
|
198
|
+
if SINGLE_ARG_GENERIC_TYPES.include?(relative_generic_type) && parameters.length > 1
|
199
|
+
Parlour::Types.const_get(relative_generic_type).new(Parlour::Types::Union.new(parameters))
|
200
|
+
elsif relative_generic_type == 'Class' && parameters.length == 1
|
168
201
|
Parlour::Types::Class.new(parameters.first)
|
169
|
-
elsif
|
202
|
+
elsif relative_generic_type == 'Hash'
|
170
203
|
if parameters.length == 2
|
171
204
|
Parlour::Types::Hash.new(*parameters)
|
172
205
|
else
|
173
|
-
handle_sord_error(parameters.map(&:describe).join, "Invalid hash, must have exactly two types: #{yard.inspect}.", item, replace_errors_with_untyped)
|
206
|
+
handle_sord_error(parameters.map(&:describe).join, "Invalid hash, must have exactly two types: #{yard.inspect}.", item, config.replace_errors_with_untyped)
|
174
207
|
end
|
175
208
|
else
|
176
|
-
if Parlour::Types.
|
209
|
+
if Parlour::Types.constants.include?(relative_generic_type.to_sym)
|
177
210
|
# This generic is built in to parlour, but sord doesn't
|
178
211
|
# explicitly know about it.
|
179
|
-
Parlour::Types.const_get(
|
212
|
+
Parlour::Types.const_get(relative_generic_type).new(*parameters)
|
180
213
|
else
|
181
214
|
# This is a user defined generic
|
182
215
|
Parlour::Types::Generic.new(
|
183
|
-
yard_to_parlour(generic_type),
|
216
|
+
yard_to_parlour(generic_type, nil, config),
|
184
217
|
parameters
|
185
218
|
)
|
186
219
|
end
|
@@ -190,22 +223,22 @@ module Sord
|
|
190
223
|
when ORDERED_LIST_REGEX
|
191
224
|
type_parameters = $1
|
192
225
|
parameters = split_type_parameters(type_parameters)
|
193
|
-
.map { |x| yard_to_parlour(x, item,
|
226
|
+
.map { |x| yard_to_parlour(x, item, config) }
|
194
227
|
Parlour::Types::Tuple.new(parameters)
|
195
228
|
when SHORTHAND_HASH_SYNTAX
|
196
229
|
type_parameters = $1
|
197
230
|
parameters = split_type_parameters(type_parameters)
|
198
|
-
.map { |x| yard_to_parlour(x, item,
|
231
|
+
.map { |x| yard_to_parlour(x, item, config) }
|
199
232
|
# Return a warning about an invalid hash when it has more or less than two elements.
|
200
233
|
if parameters.length == 2
|
201
234
|
Parlour::Types::Hash.new(*parameters)
|
202
235
|
else
|
203
|
-
handle_sord_error(parameters.map(&:describe).join, "Invalid hash, must have exactly two types: #{yard.inspect}.", item, replace_errors_with_untyped)
|
236
|
+
handle_sord_error(parameters.map(&:describe).join, "Invalid hash, must have exactly two types: #{yard.inspect}.", item, config.replace_errors_with_untyped)
|
204
237
|
end
|
205
238
|
when SHORTHAND_ARRAY_SYNTAX
|
206
239
|
type_parameters = $1
|
207
240
|
parameters = split_type_parameters(type_parameters)
|
208
|
-
.map { |x| yard_to_parlour(x, item,
|
241
|
+
.map { |x| yard_to_parlour(x, item, config) }
|
209
242
|
parameters.one? \
|
210
243
|
? Parlour::Types::Array.new(parameters.first)
|
211
244
|
: Parlour::Types::Array.new(Parlour::Types::Union.new(parameters))
|
@@ -215,7 +248,7 @@ module Sord
|
|
215
248
|
return Parlour::Types::Raw.new(from_yaml.class.to_s) \
|
216
249
|
if [Symbol, Float, Integer].include?(from_yaml.class)
|
217
250
|
|
218
|
-
return handle_sord_error(yard.to_s, "#{yard.inspect} does not appear to be a type", item, replace_errors_with_untyped)
|
251
|
+
return handle_sord_error(yard.to_s, "#{yard.inspect} does not appear to be a type", item, config.replace_errors_with_untyped)
|
219
252
|
end
|
220
253
|
end
|
221
254
|
|
@@ -233,5 +266,50 @@ module Sord
|
|
233
266
|
? Parlour::Types::Untyped.new
|
234
267
|
: Parlour::Types::Raw.new("SORD_ERROR_#{name.gsub(/[^0-9A-Za-z_]/i, '')}")
|
235
268
|
end
|
269
|
+
|
270
|
+
# Taken from: https://github.com/ruby/rbs/blob/master/core/builtin.rbs
|
271
|
+
# When the latest commit was: 6c847d1
|
272
|
+
#
|
273
|
+
# Interfaces which use generic arguments have those arguments as `untyped`, since I'm not aware
|
274
|
+
# of any standard way that these are specified.
|
275
|
+
DUCK_TYPES_TO_RBS_TYPE_NAMES = {
|
276
|
+
# Concrete
|
277
|
+
"#to_i" => "_ToI",
|
278
|
+
"#to_int" => "_ToInt",
|
279
|
+
"#to_r" => "_ToR",
|
280
|
+
"#to_s" => "_ToS",
|
281
|
+
"#to_str" => "_ToStr",
|
282
|
+
"#to_proc" => "_ToProc",
|
283
|
+
"#to_path" => "_ToPath",
|
284
|
+
"#read" => "_Reader",
|
285
|
+
"#readpartial" => "_ReaderPartial",
|
286
|
+
"#write" => "_Writer",
|
287
|
+
"#rewind" => "_Rewindable",
|
288
|
+
"#to_io" => "_ToIO",
|
289
|
+
"#exception" => "_Exception",
|
290
|
+
|
291
|
+
# Generic - these will be put in a `Types::Raw`, so writing RBS syntax is a little devious,
|
292
|
+
# but by their nature we know they'll only be used in an RBS file, so it's probably fine
|
293
|
+
"#to_hash" => "_ToHash[untyped, untyped]",
|
294
|
+
"#each" => "_Each[untyped]",
|
295
|
+
}
|
296
|
+
|
297
|
+
# Given a YARD duck type string, attempts to convert it to one of a list of pre-defined RBS
|
298
|
+
# built-in interfaces.
|
299
|
+
#
|
300
|
+
# For example, the common duck type `#to_s` has a built-in RBS equivalent `_ToS`.
|
301
|
+
#
|
302
|
+
# If no such interface exists, returns `nil`.
|
303
|
+
#
|
304
|
+
# @param [String] type
|
305
|
+
# @return [Parlour::Types::Type, nil]
|
306
|
+
def self.duck_type_to_rbs_type(type)
|
307
|
+
type_name = DUCK_TYPES_TO_RBS_TYPE_NAMES[type]
|
308
|
+
if !type_name.nil?
|
309
|
+
Parlour::Types::Raw.new(type_name)
|
310
|
+
else
|
311
|
+
nil
|
312
|
+
end
|
313
|
+
end
|
236
314
|
end
|
237
315
|
end
|
data/lib/sord/version.rb
CHANGED
data/rbi/sord.rbi
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# typed: strong
|
2
|
-
|
2
|
+
# typed: strong
|
3
3
|
module Sord
|
4
|
-
VERSION = T.let('
|
4
|
+
VERSION = T.let('5.0.0', T.untyped)
|
5
5
|
|
6
6
|
# Handles writing logs to stdout and any other classes which request them.
|
7
7
|
module Logging
|
@@ -42,6 +42,7 @@ module Sord
|
|
42
42
|
def self.valid_types?(value); end
|
43
43
|
|
44
44
|
# sord warn - YARD::CodeObjects::Base wasn't able to be resolved to a constant in this project
|
45
|
+
# sord omit - no YARD type given for "**opts", using untyped
|
45
46
|
# A generic log message writer which is called by all other specific logging
|
46
47
|
# methods. This shouldn't be called outside of the Logging class itself.
|
47
48
|
#
|
@@ -57,32 +58,36 @@ module Sord
|
|
57
58
|
kind: Symbol,
|
58
59
|
header: String,
|
59
60
|
msg: String,
|
60
|
-
item: YARD::CodeObjects::Base
|
61
|
+
item: YARD::CodeObjects::Base,
|
62
|
+
opts: T.untyped
|
61
63
|
).void
|
62
64
|
end
|
63
|
-
def self.generic(kind, header, msg, item); end
|
65
|
+
def self.generic(kind, header, msg, item, **opts); end
|
64
66
|
|
65
67
|
# sord warn - YARD::CodeObjects::Base wasn't able to be resolved to a constant in this project
|
68
|
+
# sord omit - no YARD type given for "**opts", using untyped
|
66
69
|
# Print a warning message. This should be used for things which require the
|
67
70
|
# user's attention but do not prevent the process from stopping.
|
68
71
|
#
|
69
72
|
# _@param_ `msg` — The log message to write.
|
70
73
|
#
|
71
74
|
# _@param_ `item` — The CodeObject which this log is associated with, if any. This is shown before the log message if it is specified.
|
72
|
-
sig { params(msg: String, item: T.nilable(YARD::CodeObjects::Base)).void }
|
73
|
-
def self.warn(msg, item = nil); end
|
75
|
+
sig { params(msg: String, item: T.nilable(YARD::CodeObjects::Base), opts: T.untyped).void }
|
76
|
+
def self.warn(msg, item = nil, **opts); end
|
74
77
|
|
75
78
|
# sord warn - YARD::CodeObjects::Base wasn't able to be resolved to a constant in this project
|
79
|
+
# sord omit - no YARD type given for "**opts", using untyped
|
76
80
|
# Print an info message. This should be used for generic informational
|
77
81
|
# messages which the user doesn't need to act on.
|
78
82
|
#
|
79
83
|
# _@param_ `msg` — The log message to write.
|
80
84
|
#
|
81
85
|
# _@param_ `item` — The CodeObject which this log is associated with, if any. This is shown before the log message if it is specified.
|
82
|
-
sig { params(msg: String, item: T.nilable(YARD::CodeObjects::Base)).void }
|
83
|
-
def self.info(msg, item = nil); end
|
86
|
+
sig { params(msg: String, item: T.nilable(YARD::CodeObjects::Base), opts: T.untyped).void }
|
87
|
+
def self.info(msg, item = nil, **opts); end
|
84
88
|
|
85
89
|
# sord warn - YARD::CodeObjects::Base wasn't able to be resolved to a constant in this project
|
90
|
+
# sord omit - no YARD type given for "**opts", using untyped
|
86
91
|
# Print a duck-typing message. This should be used when the YARD
|
87
92
|
# documentation contains duck typing, which isn't supported by Sorbet, so
|
88
93
|
# it is substituted for something different.
|
@@ -90,20 +95,22 @@ module Sord
|
|
90
95
|
# _@param_ `msg` — The log message to write.
|
91
96
|
#
|
92
97
|
# _@param_ `item` — The CodeObject which this log is associated with, if any. This is shown before the log message if it is specified.
|
93
|
-
sig { params(msg: String, item: T.nilable(YARD::CodeObjects::Base)).void }
|
94
|
-
def self.duck(msg, item = nil); end
|
98
|
+
sig { params(msg: String, item: T.nilable(YARD::CodeObjects::Base), opts: T.untyped).void }
|
99
|
+
def self.duck(msg, item = nil, **opts); end
|
95
100
|
|
96
101
|
# sord warn - YARD::CodeObjects::Base wasn't able to be resolved to a constant in this project
|
102
|
+
# sord omit - no YARD type given for "**opts", using untyped
|
97
103
|
# Print an error message. This should be used for things which require the
|
98
104
|
# current process to stop.
|
99
105
|
#
|
100
106
|
# _@param_ `msg` — The log message to write.
|
101
107
|
#
|
102
108
|
# _@param_ `item` — The CodeObject which this log is associated with, if any. This is shown before the log message if it is specified.
|
103
|
-
sig { params(msg: String, item: T.nilable(YARD::CodeObjects::Base)).void }
|
104
|
-
def self.error(msg, item = nil); end
|
109
|
+
sig { params(msg: String, item: T.nilable(YARD::CodeObjects::Base), opts: T.untyped).void }
|
110
|
+
def self.error(msg, item = nil, **opts); end
|
105
111
|
|
106
112
|
# sord warn - YARD::CodeObjects::Base wasn't able to be resolved to a constant in this project
|
113
|
+
# sord omit - no YARD type given for "**opts", using untyped
|
107
114
|
# Print an infer message. This should be used when the user should be told
|
108
115
|
# that some information has been filled in or guessed for them, and that
|
109
116
|
# information is likely correct.
|
@@ -111,10 +118,11 @@ module Sord
|
|
111
118
|
# _@param_ `msg` — The log message to write.
|
112
119
|
#
|
113
120
|
# _@param_ `item` — The CodeObject which this log is associated with, if any. This is shown before the log message if it is specified.
|
114
|
-
sig { params(msg: String, item: T.nilable(YARD::CodeObjects::Base)).void }
|
115
|
-
def self.infer(msg, item = nil); end
|
121
|
+
sig { params(msg: String, item: T.nilable(YARD::CodeObjects::Base), opts: T.untyped).void }
|
122
|
+
def self.infer(msg, item = nil, **opts); end
|
116
123
|
|
117
124
|
# sord warn - YARD::CodeObjects::Base wasn't able to be resolved to a constant in this project
|
125
|
+
# sord omit - no YARD type given for "**opts", using untyped
|
118
126
|
# Print an omit message. This should be used as a special type of warning
|
119
127
|
# to alert the user that there is some information missing, but this
|
120
128
|
# information is not critical to the completion of the process.
|
@@ -122,20 +130,22 @@ module Sord
|
|
122
130
|
# _@param_ `msg` — The log message to write.
|
123
131
|
#
|
124
132
|
# _@param_ `item` — The CodeObject which this log is associated with, if any. This is shown before the log message if it is specified.
|
125
|
-
sig { params(msg: String, item: T.nilable(YARD::CodeObjects::Base)).void }
|
126
|
-
def self.omit(msg, item = nil); end
|
133
|
+
sig { params(msg: String, item: T.nilable(YARD::CodeObjects::Base), opts: T.untyped).void }
|
134
|
+
def self.omit(msg, item = nil, **opts); end
|
127
135
|
|
128
136
|
# sord warn - YARD::CodeObjects::Base wasn't able to be resolved to a constant in this project
|
137
|
+
# sord omit - no YARD type given for "**opts", using untyped
|
129
138
|
# Print a done message. This should be used when a process completes
|
130
139
|
# successfully.
|
131
140
|
#
|
132
141
|
# _@param_ `msg` — The log message to write.
|
133
142
|
#
|
134
143
|
# _@param_ `item` — The CodeObject which this log is associated with, if any. This is shown before the log message if it is specified.
|
135
|
-
sig { params(msg: String, item: T.nilable(YARD::CodeObjects::Base)).void }
|
136
|
-
def self.done(msg, item = nil); end
|
144
|
+
sig { params(msg: String, item: T.nilable(YARD::CodeObjects::Base), opts: T.untyped).void }
|
145
|
+
def self.done(msg, item = nil, **opts); end
|
137
146
|
|
138
147
|
# sord warn - YARD::CodeObjects::Base wasn't able to be resolved to a constant in this project
|
148
|
+
# sord omit - no YARD type given for "**opts", using untyped
|
139
149
|
# Invokes all registered hooks on the logger.
|
140
150
|
#
|
141
151
|
# _@param_ `kind` — The kind of log message this is.
|
@@ -143,8 +153,15 @@ module Sord
|
|
143
153
|
# _@param_ `msg` — The log message to write.
|
144
154
|
#
|
145
155
|
# _@param_ `item` — The CodeObject which this log is associated with, if any. This is shown before the log message if it is specified.
|
146
|
-
sig
|
147
|
-
|
156
|
+
sig do
|
157
|
+
params(
|
158
|
+
kind: Symbol,
|
159
|
+
msg: String,
|
160
|
+
item: YARD::CodeObjects::Base,
|
161
|
+
opts: T.untyped
|
162
|
+
).void
|
163
|
+
end
|
164
|
+
def self.invoke_hooks(kind, msg, item, **opts); end
|
148
165
|
|
149
166
|
# sord warn - YARD::CodeObjects::Base wasn't able to be resolved to a constant in this project
|
150
167
|
# Adds a hook to the logger.
|
@@ -156,6 +173,25 @@ module Sord
|
|
156
173
|
sig { void }
|
157
174
|
def self.prepare; end
|
158
175
|
|
176
|
+
# sord omit - no YARD type given for "hash", using untyped
|
177
|
+
# sord omit - no YARD return type given, using untyped
|
178
|
+
sig { params(hash: T.untyped).returns(T.untyped) }
|
179
|
+
def self.load_gem_objects(hash); end
|
180
|
+
|
181
|
+
# sord omit - no YARD type given for "all_decls", using untyped
|
182
|
+
# sord omit - no YARD type given for "names_to_paths", using untyped
|
183
|
+
# sord omit - no YARD type given for "path", using untyped
|
184
|
+
# sord omit - no YARD return type given, using untyped
|
185
|
+
sig { params(all_decls: T.untyped, names_to_paths: T.untyped, path: T.untyped).returns(T.untyped) }
|
186
|
+
def self.add_rbs_objects_to_paths(all_decls, names_to_paths, path = []); end
|
187
|
+
|
188
|
+
# sord omit - no YARD type given for "nodes", using untyped
|
189
|
+
# sord omit - no YARD type given for "names_to_paths", using untyped
|
190
|
+
# sord omit - no YARD type given for "path", using untyped
|
191
|
+
# sord omit - no YARD return type given, using untyped
|
192
|
+
sig { params(nodes: T.untyped, names_to_paths: T.untyped, path: T.untyped).returns(T.untyped) }
|
193
|
+
def self.add_rbi_objects_to_paths(nodes, names_to_paths, path = []); end
|
194
|
+
|
159
195
|
sig { void }
|
160
196
|
def self.clear; end
|
161
197
|
|
@@ -182,7 +218,7 @@ module Sord
|
|
182
218
|
class Generator
|
183
219
|
VALID_MODES = T.let([:rbi, :rbs], T.untyped)
|
184
220
|
|
185
|
-
# _@return_ — The number of objects this generator has processed so
|
221
|
+
# _@return_ — The number of objects this generator has processed so
|
186
222
|
# far.
|
187
223
|
sig { returns(Integer) }
|
188
224
|
def object_count; end
|
@@ -217,7 +253,6 @@ module Sord
|
|
217
253
|
def add_constants(item); end
|
218
254
|
|
219
255
|
# sord warn - YARD::CodeObjects::NamespaceObject wasn't able to be resolved to a constant in this project
|
220
|
-
# sord warn - Parlour::TypedObject wasn't able to be resolved to a constant in this project
|
221
256
|
# Adds comments to an object based on a docstring.
|
222
257
|
#
|
223
258
|
# _@param_ `item`
|
@@ -226,6 +261,22 @@ module Sord
|
|
226
261
|
sig { params(item: YARD::CodeObjects::NamespaceObject, typed_object: Parlour::TypedObject).void }
|
227
262
|
def add_comments(item, typed_object); end
|
228
263
|
|
264
|
+
# sord warn - YARD::CodeObjects::MethodObject wasn't able to be resolved to a constant in this project
|
265
|
+
# sord warn - YARD::Tags::Tag wasn't able to be resolved to a constant in this project
|
266
|
+
# _@param_ `method`
|
267
|
+
#
|
268
|
+
# _@param_ `tag_name`
|
269
|
+
sig { params(method: YARD::CodeObjects::MethodObject, tag_name: String).returns(T::Array[YARD::Tags::Tag]) }
|
270
|
+
def method_tags(method, tag_name); end
|
271
|
+
|
272
|
+
# sord warn - YARD::CodeObjects::MethodObject wasn't able to be resolved to a constant in this project
|
273
|
+
# sord warn - YARD::Tags::Tag wasn't able to be resolved to a constant in this project
|
274
|
+
# _@param_ `method`
|
275
|
+
#
|
276
|
+
# _@param_ `tag_name`
|
277
|
+
sig { params(method: YARD::CodeObjects::MethodObject, tag_name: String).returns(T.nilable(YARD::Tags::Tag)) }
|
278
|
+
def method_tag(method, tag_name); end
|
279
|
+
|
229
280
|
# sord warn - YARD::CodeObjects::NamespaceObject wasn't able to be resolved to a constant in this project
|
230
281
|
# Given a YARD NamespaceObject, add lines defining its methods and their
|
231
282
|
# signatures to the current file.
|
@@ -250,7 +301,7 @@ module Sord
|
|
250
301
|
sig { params(item: YARD::CodeObjects::NamespaceObject).void }
|
251
302
|
def add_namespace(item); end
|
252
303
|
|
253
|
-
# Populates the generator with the contents of the YARD registry. You
|
304
|
+
# Populates the generator with the contents of the YARD registry. You
|
254
305
|
# must load the YARD registry first!
|
255
306
|
sig { void }
|
256
307
|
def populate; end
|
@@ -279,8 +330,15 @@ module Sord
|
|
279
330
|
sig { params(pair1: T::Array[T.untyped], pair2: T::Array[T.untyped]).returns(T.untyped) }
|
280
331
|
def sort_params(pair1, pair2); end
|
281
332
|
|
333
|
+
# Removes the last character of a default parameter value if it begins with
|
334
|
+
# '-', working around a bug in YARD. (See lsegal/yard #894)
|
335
|
+
#
|
336
|
+
# _@param_ `default`
|
337
|
+
sig { params(default: String).returns(T.nilable(String)) }
|
338
|
+
def fix_default_if_unary_minus(default); end
|
339
|
+
|
282
340
|
# sord warn - YARD::CodeObjects::Base wasn't able to be resolved to a constant in this project
|
283
|
-
# _@return_ — The
|
341
|
+
# _@return_ — The
|
284
342
|
# errors encountered by by the generator. Each element is of the form
|
285
343
|
# [message, item, line].
|
286
344
|
sig { returns(T::Array[[String, YARD::CodeObjects::Base, Integer]]) }
|
@@ -297,6 +355,10 @@ module Sord
|
|
297
355
|
sig { params(root: T.untyped).returns(T.untyped) }
|
298
356
|
def generate(root); end
|
299
357
|
|
358
|
+
# sord omit - no YARD return type given, using untyped
|
359
|
+
sig { returns(T.untyped) }
|
360
|
+
def add_custom_tags; end
|
361
|
+
|
300
362
|
# sord omit - no YARD return type given, using untyped
|
301
363
|
sig { params(block: T.untyped).returns(T.untyped) }
|
302
364
|
def self.with_clean_env(&block); end
|
@@ -315,11 +377,33 @@ module Sord
|
|
315
377
|
module TypeConverter
|
316
378
|
SIMPLE_TYPE_REGEX = T.let(/(?:\:\:)?[a-zA-Z_][\w]*(?:\:\:[a-zA-Z_][\w]*)*/, T.untyped)
|
317
379
|
GENERIC_TYPE_REGEX = T.let(/(#{SIMPLE_TYPE_REGEX})\s*[<{]\s*(.*)\s*[>}]/, T.untyped)
|
318
|
-
|
380
|
+
METHOD_NAME_REGEX = T.let(/(?:[a-z_]\w*[?!=]?|\[\]=?|<<|>>|\*\*|[!~+\*\/%&^|-]|[<>]=?|<=>|={2,3}|![=~]|=~)/i, T.untyped)
|
381
|
+
DUCK_TYPE_REGEX = T.let(/^\##{METHOD_NAME_REGEX}(?:\s*\&\s*\##{METHOD_NAME_REGEX})*$/, T.untyped)
|
319
382
|
ORDERED_LIST_REGEX = T.let(/^(?:Array|)\((.*)\s*\)$/, T.untyped)
|
320
383
|
SHORTHAND_HASH_SYNTAX = T.let(/^{\s*(.*)\s*}$/, T.untyped)
|
321
384
|
SHORTHAND_ARRAY_SYNTAX = T.let(/^<\s*(.*)\s*>$/, T.untyped)
|
322
385
|
SINGLE_ARG_GENERIC_TYPES = T.let(%w{Array Set Enumerable Enumerator Range}, T.untyped)
|
386
|
+
DUCK_TYPES_TO_RBS_TYPE_NAMES = T.let({
|
387
|
+
# Concrete
|
388
|
+
"#to_i" => "_ToI",
|
389
|
+
"#to_int" => "_ToInt",
|
390
|
+
"#to_r" => "_ToR",
|
391
|
+
"#to_s" => "_ToS",
|
392
|
+
"#to_str" => "_ToStr",
|
393
|
+
"#to_proc" => "_ToProc",
|
394
|
+
"#to_path" => "_ToPath",
|
395
|
+
"#read" => "_Reader",
|
396
|
+
"#readpartial" => "_ReaderPartial",
|
397
|
+
"#write" => "_Writer",
|
398
|
+
"#rewind" => "_Rewindable",
|
399
|
+
"#to_io" => "_ToIO",
|
400
|
+
"#exception" => "_Exception",
|
401
|
+
|
402
|
+
# Generic - these will be put in a `Types::Raw`, so writing RBS syntax is a little devious,
|
403
|
+
# but by their nature we know they'll only be used in an RBS file, so it's probably fine
|
404
|
+
"#to_hash" => "_ToHash[untyped, untyped]",
|
405
|
+
"#each" => "_Each[untyped]",
|
406
|
+
}, T.untyped)
|
323
407
|
|
324
408
|
# Given a string of YARD type parameters (without angle brackets), splits
|
325
409
|
# the string into an array of each type parameter.
|
@@ -331,29 +415,17 @@ module Sord
|
|
331
415
|
def self.split_type_parameters(params); end
|
332
416
|
|
333
417
|
# sord warn - YARD::CodeObjects::Base wasn't able to be resolved to a constant in this project
|
334
|
-
# sord warn - Parlour::Types::Type wasn't able to be resolved to a constant in this project
|
335
418
|
# Converts a YARD type into a Parlour type.
|
336
419
|
#
|
337
420
|
# _@param_ `yard` — The YARD type.
|
338
421
|
#
|
339
422
|
# _@param_ `item` — The CodeObject which the YARD type is associated with. This is used for logging and can be nil, but this will lead to less informative log messages.
|
340
423
|
#
|
341
|
-
# _@param_ `
|
342
|
-
|
343
|
-
|
344
|
-
sig do
|
345
|
-
params(
|
346
|
-
yard: T.any(T::Boolean, T::Array[T.untyped], String),
|
347
|
-
item: T.nilable(YARD::CodeObjects::Base),
|
348
|
-
replace_errors_with_untyped: T::Boolean,
|
349
|
-
replace_unresolved_with_untyped: T::Boolean
|
350
|
-
).returns(Parlour::Types::Type)
|
351
|
-
end
|
352
|
-
def self.yard_to_parlour(yard, item = nil, replace_errors_with_untyped = false, replace_unresolved_with_untyped = false); end
|
424
|
+
# _@param_ `config` — The generation configuration.
|
425
|
+
sig { params(yard: T.any(T::Boolean, T::Array[T.untyped], String), item: YARD::CodeObjects::Base, config: Configuration).returns(Parlour::Types::Type) }
|
426
|
+
def self.yard_to_parlour(yard, item, config); end
|
353
427
|
|
354
|
-
# sord warn - Parlour::Types::Type wasn't able to be resolved to a constant in this project
|
355
428
|
# sord warn - YARD::CodeObjects::Base wasn't able to be resolved to a constant in this project
|
356
|
-
# sord warn - Parlour::Types::Type wasn't able to be resolved to a constant in this project
|
357
429
|
# Handles SORD_ERRORs.
|
358
430
|
#
|
359
431
|
# _@param_ `name`
|
@@ -372,5 +444,40 @@ module Sord
|
|
372
444
|
).returns(Parlour::Types::Type)
|
373
445
|
end
|
374
446
|
def self.handle_sord_error(name, log_warning, item, replace_errors_with_untyped); end
|
447
|
+
|
448
|
+
# Given a YARD duck type string, attempts to convert it to one of a list of pre-defined RBS
|
449
|
+
# built-in interfaces.
|
450
|
+
#
|
451
|
+
# For example, the common duck type `#to_s` has a built-in RBS equivalent `_ToS`.
|
452
|
+
#
|
453
|
+
# If no such interface exists, returns `nil`.
|
454
|
+
#
|
455
|
+
# _@param_ `type`
|
456
|
+
sig { params(type: String).returns(T.nilable(Parlour::Types::Type)) }
|
457
|
+
def self.duck_type_to_rbs_type(type); end
|
458
|
+
|
459
|
+
# Configuration for how the type converter should work in particular cases.
|
460
|
+
class Configuration
|
461
|
+
# sord omit - no YARD type given for "replace_errors_with_untyped:", using untyped
|
462
|
+
# sord omit - no YARD type given for "replace_unresolved_with_untyped:", using untyped
|
463
|
+
# sord omit - no YARD type given for "output_language:", using untyped
|
464
|
+
sig { params(replace_errors_with_untyped: T.untyped, replace_unresolved_with_untyped: T.untyped, output_language: T.untyped).void }
|
465
|
+
def initialize(replace_errors_with_untyped:, replace_unresolved_with_untyped:, output_language:); end
|
466
|
+
|
467
|
+
# sord omit - no YARD type given for :output_language, using untyped
|
468
|
+
# The language which the generated types will be converted to - one of
|
469
|
+
# `:rbi` or `:rbs`.
|
470
|
+
sig { returns(T.untyped) }
|
471
|
+
attr_accessor :output_language
|
472
|
+
|
473
|
+
# _@return_ — If true, T.untyped is used instead of SORD_ERROR_
|
474
|
+
# constants for unknown types.
|
475
|
+
sig { returns(T::Boolean) }
|
476
|
+
attr_accessor :replace_errors_with_untyped
|
477
|
+
|
478
|
+
# _@param_ `replace_unresolved_with_untyped` — If true, T.untyped is used when Sord is unable to resolve a constant.
|
479
|
+
sig { returns(T::Boolean) }
|
480
|
+
attr_accessor :replace_unresolved_with_untyped
|
481
|
+
end
|
375
482
|
end
|
376
483
|
end
|
data/sord.gemspec
CHANGED
@@ -26,6 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_dependency 'sorbet-runtime'
|
27
27
|
spec.add_dependency 'commander', '~> 4.5'
|
28
28
|
spec.add_dependency 'parlour', '~> 5.0'
|
29
|
+
spec.add_dependency 'rbs', '~> 2.0'
|
29
30
|
|
30
31
|
spec.add_development_dependency "bundler", "~> 2.0"
|
31
32
|
spec.add_development_dependency "rake", "~> 10.0"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sord
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 5.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Aaron Christiansen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-10-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: yard
|
@@ -66,6 +66,20 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '5.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rbs
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '2.0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '2.0'
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: bundler
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|