sord 4.0.0 → 5.0.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.
- 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
|