parlour 2.0.0 → 5.0.0.beta.1
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/ISSUE_TEMPLATE/bug-report.md +0 -0
- data/.github/ISSUE_TEMPLATE/feature-request.md +0 -0
- data/.gitignore +1 -1
- data/.parlour +5 -0
- data/.rspec +0 -0
- data/.travis.yml +3 -3
- data/CHANGELOG.md +64 -0
- data/CODE_OF_CONDUCT.md +0 -0
- data/Gemfile +0 -0
- data/LICENSE.txt +0 -0
- data/README.md +233 -19
- data/Rakefile +0 -0
- data/exe/parlour +109 -4
- data/lib/parlour.rb +29 -1
- data/lib/parlour/conflict_resolver.rb +75 -27
- data/lib/parlour/conversion/converter.rb +34 -0
- data/lib/parlour/conversion/rbi_to_rbs.rb +223 -0
- data/lib/parlour/debugging.rb +0 -0
- data/lib/parlour/detached_rbi_generator.rb +1 -6
- data/lib/parlour/detached_rbs_generator.rb +25 -0
- data/lib/parlour/generator.rb +34 -0
- data/lib/parlour/kernel_hack.rb +0 -0
- data/lib/parlour/options.rb +71 -0
- data/lib/parlour/parse_error.rb +0 -0
- data/lib/parlour/plugin.rb +1 -1
- data/lib/parlour/rbi_generator.rb +24 -37
- data/lib/parlour/rbi_generator/arbitrary.rb +5 -2
- data/lib/parlour/rbi_generator/attribute.rb +14 -5
- data/lib/parlour/rbi_generator/class_namespace.rb +8 -3
- data/lib/parlour/rbi_generator/constant.rb +28 -8
- data/lib/parlour/rbi_generator/enum_class_namespace.rb +32 -5
- data/lib/parlour/rbi_generator/extend.rb +5 -2
- data/lib/parlour/rbi_generator/include.rb +5 -2
- data/lib/parlour/rbi_generator/method.rb +15 -10
- data/lib/parlour/rbi_generator/module_namespace.rb +7 -2
- data/lib/parlour/rbi_generator/namespace.rb +115 -27
- data/lib/parlour/rbi_generator/parameter.rb +13 -7
- data/lib/parlour/rbi_generator/rbi_object.rb +19 -78
- data/lib/parlour/rbi_generator/struct_class_namespace.rb +110 -0
- data/lib/parlour/rbi_generator/struct_prop.rb +139 -0
- data/lib/parlour/rbi_generator/type_alias.rb +101 -0
- data/lib/parlour/rbs_generator.rb +24 -0
- data/lib/parlour/rbs_generator/arbitrary.rb +92 -0
- data/lib/parlour/rbs_generator/attribute.rb +82 -0
- data/lib/parlour/rbs_generator/block.rb +49 -0
- data/lib/parlour/rbs_generator/class_namespace.rb +106 -0
- data/lib/parlour/rbs_generator/constant.rb +95 -0
- data/lib/parlour/rbs_generator/extend.rb +92 -0
- data/lib/parlour/rbs_generator/include.rb +92 -0
- data/lib/parlour/rbs_generator/interface_namespace.rb +34 -0
- data/lib/parlour/rbs_generator/method.rb +146 -0
- data/lib/parlour/rbs_generator/method_signature.rb +104 -0
- data/lib/parlour/rbs_generator/module_namespace.rb +35 -0
- data/lib/parlour/rbs_generator/namespace.rb +627 -0
- data/lib/parlour/rbs_generator/parameter.rb +145 -0
- data/lib/parlour/rbs_generator/rbs_object.rb +78 -0
- data/lib/parlour/rbs_generator/type_alias.rb +96 -0
- data/lib/parlour/type_loader.rb +30 -10
- data/lib/parlour/type_parser.rb +440 -43
- data/lib/parlour/typed_object.rb +87 -0
- data/lib/parlour/types.rb +445 -0
- data/lib/parlour/version.rb +1 -1
- data/parlour.gemspec +2 -2
- data/plugin_examples/foobar_plugin.rb +0 -0
- data/rbi/parlour.rbi +1799 -0
- metadata +42 -15
- data/lib/parlour/rbi_generator/options.rb +0 -74
data/exe/parlour
CHANGED
@@ -16,15 +16,67 @@ command :run do |c|
|
|
16
16
|
c.description = 'Generates an RBI file from your .parlour file'
|
17
17
|
|
18
18
|
c.action do |args, options|
|
19
|
-
|
19
|
+
working_dir = Dir.pwd
|
20
|
+
config_filename = File.join(working_dir, '.parlour')
|
20
21
|
|
21
|
-
|
22
|
+
if File.exists?(config_filename)
|
23
|
+
configuration = keys_to_symbols(YAML.load_file(config_filename))
|
24
|
+
else
|
25
|
+
configuration = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
# Output file
|
29
|
+
if configuration[:output_file].is_a?(String)
|
30
|
+
assumed_format = \
|
31
|
+
if configuration[:output_file].end_with?('.rbi')
|
32
|
+
:rbi
|
33
|
+
elsif configuration[:output_file].end_with?('.rbs')
|
34
|
+
:rbs
|
35
|
+
else
|
36
|
+
raise 'invalid output file; please specify an RBI or RBS file'
|
37
|
+
end
|
38
|
+
|
39
|
+
unless $VERBOSE.nil?
|
40
|
+
print Rainbow("Parlour warning: ").yellow.dark.bold
|
41
|
+
print Rainbow("CLI: ").magenta.bright.bold
|
42
|
+
puts "Specifying output_file in .parlour as a string is deprecated."
|
43
|
+
puts "For now, generating an #{assumed_format.to_s.upcase} file based on the file extension."
|
44
|
+
puts "Please update your .parlour to use the new form:"
|
45
|
+
puts " output_file:"
|
46
|
+
puts " #{assumed_format}: #{configuration[:output_file]}"
|
47
|
+
end
|
48
|
+
configuration[:output_file] = {
|
49
|
+
assumed_format => configuration[:output_file]
|
50
|
+
}
|
51
|
+
end
|
52
|
+
configuration[:output_file] ||= {
|
53
|
+
rbi: "rbi/#{File.basename(working_dir)}.rbi"
|
54
|
+
}
|
22
55
|
|
23
56
|
# Style defaults
|
24
57
|
configuration[:style] ||= {}
|
25
58
|
configuration[:style][:tab_size] ||= 2
|
26
59
|
configuration[:style][:break_params] ||= 4
|
27
60
|
|
61
|
+
# Parser defaults, set explicitly to false to not run parser
|
62
|
+
if configuration[:parser] != false
|
63
|
+
configuration[:parser] ||= {}
|
64
|
+
|
65
|
+
# Input/Output defaults
|
66
|
+
configuration[:parser][:root] ||= '.'
|
67
|
+
|
68
|
+
# Included/Excluded path defaults
|
69
|
+
configuration[:parser][:included_paths] ||= ['lib']
|
70
|
+
configuration[:parser][:excluded_paths] ||= ['sorbet', 'spec']
|
71
|
+
|
72
|
+
# Defaults can be overridden but we always want to exclude the output file
|
73
|
+
configuration[:parser][:excluded_paths] << configuration[:output_file][:rbi]
|
74
|
+
end
|
75
|
+
|
76
|
+
# Included/Excluded module defaults
|
77
|
+
configuration[:included_modules] ||= []
|
78
|
+
configuration[:excluded_modules] ||= []
|
79
|
+
|
28
80
|
# Require defaults
|
29
81
|
configuration[:requires] ||= []
|
30
82
|
configuration[:relative_requires] ||= []
|
@@ -53,6 +105,15 @@ command :run do |c|
|
|
53
105
|
break_params: configuration[:style][:break_params],
|
54
106
|
tab_size: configuration[:style][:tab_size]
|
55
107
|
)
|
108
|
+
|
109
|
+
if configuration[:parser]
|
110
|
+
Parlour::TypeLoader.load_project(
|
111
|
+
configuration[:parser][:root],
|
112
|
+
inclusions: configuration[:parser][:included_paths],
|
113
|
+
exclusions: configuration[:parser][:excluded_paths],
|
114
|
+
generator: gen,
|
115
|
+
)
|
116
|
+
end
|
56
117
|
Parlour::Plugin.run_plugins(plugin_instances, gen)
|
57
118
|
|
58
119
|
# Run a pass of the conflict resolver
|
@@ -74,6 +135,14 @@ command :run do |c|
|
|
74
135
|
choice == 0 ? nil : candidates[choice - 1]
|
75
136
|
end
|
76
137
|
|
138
|
+
if !configuration[:included_modules].empty? || !configuration[:excluded_modules].empty?
|
139
|
+
remove_unwanted_modules(
|
140
|
+
gen.root,
|
141
|
+
included_modules: configuration[:included_modules],
|
142
|
+
excluded_modules: configuration[:excluded_modules],
|
143
|
+
)
|
144
|
+
end
|
145
|
+
|
77
146
|
# Figure out strictness levels
|
78
147
|
requested_strictness_levels = plugin_instances.map do |plugin|
|
79
148
|
s = plugin.strictness&.to_s
|
@@ -97,8 +166,23 @@ command :run do |c|
|
|
97
166
|
end
|
98
167
|
end
|
99
168
|
|
100
|
-
# Write the final
|
101
|
-
|
169
|
+
# Write the final files
|
170
|
+
if configuration[:output_file][:rbi]
|
171
|
+
FileUtils.mkdir_p(File.dirname(configuration[:output_file][:rbi]))
|
172
|
+
File.write(configuration[:output_file][:rbi], gen.rbi(strictness))
|
173
|
+
end
|
174
|
+
if configuration[:output_file][:rbs]
|
175
|
+
gen.root.generalize_from_rbi!
|
176
|
+
rbs_gen = Parlour::RbsGenerator.new
|
177
|
+
|
178
|
+
converter = Parlour::Conversion::RbiToRbs.new(rbs_gen)
|
179
|
+
gen.root.children.each do |child|
|
180
|
+
converter.convert_object(child, rbs_gen.root)
|
181
|
+
end
|
182
|
+
|
183
|
+
FileUtils.mkdir_p(File.dirname(configuration[:output_file][:rbs]))
|
184
|
+
File.write(configuration[:output_file][:rbs], rbs_gen.rbs)
|
185
|
+
end
|
102
186
|
end
|
103
187
|
end
|
104
188
|
|
@@ -122,3 +206,24 @@ def keys_to_symbols(hash)
|
|
122
206
|
]
|
123
207
|
end.to_h
|
124
208
|
end
|
209
|
+
|
210
|
+
def remove_unwanted_modules(root, included_modules:, excluded_modules:, prefix: nil)
|
211
|
+
root.children.select! do |child|
|
212
|
+
module_name = "#{prefix}#{child.name}"
|
213
|
+
|
214
|
+
if child.respond_to?(:children)
|
215
|
+
remove_unwanted_modules(
|
216
|
+
child,
|
217
|
+
included_modules: included_modules,
|
218
|
+
excluded_modules: excluded_modules,
|
219
|
+
prefix: "#{module_name}::",
|
220
|
+
)
|
221
|
+
has_included_children = !child.children.empty?
|
222
|
+
end
|
223
|
+
|
224
|
+
included = included_modules.empty? ? true : included_modules.any? { |m| module_name.start_with?(m) }
|
225
|
+
excluded = excluded_modules.empty? ? false : excluded_modules.any? { |m| module_name.start_with?(m) }
|
226
|
+
|
227
|
+
(included || has_included_children) && !excluded
|
228
|
+
end
|
229
|
+
end
|
data/lib/parlour.rb
CHANGED
@@ -9,12 +9,17 @@ require 'parlour/kernel_hack'
|
|
9
9
|
|
10
10
|
require 'parlour/plugin'
|
11
11
|
|
12
|
+
require 'parlour/types'
|
13
|
+
|
14
|
+
require 'parlour/options'
|
15
|
+
require 'parlour/typed_object'
|
16
|
+
require 'parlour/generator'
|
12
17
|
require 'parlour/rbi_generator/parameter'
|
13
18
|
require 'parlour/rbi_generator/rbi_object'
|
19
|
+
require 'parlour/rbi_generator/type_alias'
|
14
20
|
require 'parlour/rbi_generator/method'
|
15
21
|
require 'parlour/rbi_generator/attribute'
|
16
22
|
require 'parlour/rbi_generator/arbitrary'
|
17
|
-
require 'parlour/rbi_generator/options'
|
18
23
|
require 'parlour/rbi_generator/include'
|
19
24
|
require 'parlour/rbi_generator/extend'
|
20
25
|
require 'parlour/rbi_generator/constant'
|
@@ -22,9 +27,32 @@ require 'parlour/rbi_generator/namespace'
|
|
22
27
|
require 'parlour/rbi_generator/module_namespace'
|
23
28
|
require 'parlour/rbi_generator/class_namespace'
|
24
29
|
require 'parlour/rbi_generator/enum_class_namespace'
|
30
|
+
require 'parlour/rbi_generator/struct_prop'
|
31
|
+
require 'parlour/rbi_generator/struct_class_namespace'
|
25
32
|
require 'parlour/rbi_generator'
|
26
33
|
require 'parlour/detached_rbi_generator'
|
27
34
|
|
35
|
+
require 'parlour/rbs_generator/rbs_object'
|
36
|
+
require 'parlour/rbs_generator/type_alias'
|
37
|
+
require 'parlour/rbs_generator/namespace'
|
38
|
+
require 'parlour/rbs_generator/method'
|
39
|
+
require 'parlour/rbs_generator/arbitrary'
|
40
|
+
require 'parlour/rbs_generator/attribute'
|
41
|
+
require 'parlour/rbs_generator/block'
|
42
|
+
require 'parlour/rbs_generator/class_namespace'
|
43
|
+
require 'parlour/rbs_generator/constant'
|
44
|
+
require 'parlour/rbs_generator/extend'
|
45
|
+
require 'parlour/rbs_generator/include'
|
46
|
+
require 'parlour/rbs_generator/method_signature'
|
47
|
+
require 'parlour/rbs_generator/module_namespace'
|
48
|
+
require 'parlour/rbs_generator/interface_namespace'
|
49
|
+
require 'parlour/rbs_generator/parameter'
|
50
|
+
require 'parlour/rbs_generator'
|
51
|
+
require 'parlour/detached_rbs_generator'
|
52
|
+
|
53
|
+
require 'parlour/conversion/converter'
|
54
|
+
require 'parlour/conversion/rbi_to_rbs'
|
55
|
+
|
28
56
|
require 'parlour/conflict_resolver'
|
29
57
|
|
30
58
|
require 'parlour/parse_error'
|
@@ -1,4 +1,6 @@
|
|
1
1
|
# typed: true
|
2
|
+
require 'set'
|
3
|
+
|
2
4
|
module Parlour
|
3
5
|
# Responsible for resolving conflicts (that is, multiple definitions with the
|
4
6
|
# same name) between objects defined in the same namespace.
|
@@ -11,7 +13,7 @@ module Parlour
|
|
11
13
|
resolver: T.proc.params(
|
12
14
|
desc: String,
|
13
15
|
choices: T::Array[RbiGenerator::RbiObject]
|
14
|
-
).returns(RbiGenerator::RbiObject)
|
16
|
+
).returns(T.nilable(RbiGenerator::RbiObject))
|
15
17
|
).void
|
16
18
|
end
|
17
19
|
# Given a namespace, attempts to automatically resolve conflicts in the
|
@@ -21,13 +23,13 @@ module Parlour
|
|
21
23
|
# All children of the given namespace which are also namespaces are
|
22
24
|
# processed recursively, so passing {RbiGenerator#root} will eliminate all
|
23
25
|
# conflicts in the entire object tree.
|
24
|
-
#
|
26
|
+
#
|
25
27
|
# If automatic resolution is not possible, the block passed to this method
|
26
28
|
# is invoked and passed two arguments: a message on what the conflict is,
|
27
29
|
# and an array of candidate objects. The block should return one of these
|
28
30
|
# candidate objects, which will be kept, and all other definitions are
|
29
31
|
# deleted. Alternatively, the block may return nil, which will delete all
|
30
|
-
# definitions. The block may be invoked many times from one call to
|
32
|
+
# definitions. The block may be invoked many times from one call to
|
31
33
|
# {resolve_conflicts}, one for each unresolvable conflict.
|
32
34
|
#
|
33
35
|
# @param namespace [RbiGenerator::Namespace] The starting namespace to
|
@@ -42,7 +44,14 @@ module Parlour
|
|
42
44
|
Debugging.debug_puts(self, Debugging::Tree.begin("Resolving conflicts for #{namespace.name}..."))
|
43
45
|
|
44
46
|
# Check for multiple definitions with the same name
|
45
|
-
|
47
|
+
# (Special case here: writer attributes get an "=" appended to their name)
|
48
|
+
grouped_by_name_children = namespace.children.group_by do |child|
|
49
|
+
if RbiGenerator::Attribute === child && child.kind == :writer
|
50
|
+
"#{child.name}=" unless child.name.end_with?('=')
|
51
|
+
else
|
52
|
+
child.name
|
53
|
+
end
|
54
|
+
end
|
46
55
|
|
47
56
|
grouped_by_name_children.each do |name, children|
|
48
57
|
Debugging.debug_puts(self, Debugging::Tree.begin("Checking children named #{name}..."))
|
@@ -50,7 +59,7 @@ module Parlour
|
|
50
59
|
if children.length > 1
|
51
60
|
Debugging.debug_puts(self, Debugging::Tree.here("Possible conflict between #{children.length} objects"))
|
52
61
|
|
53
|
-
# Special case: do we have two methods, one of which is a class method
|
62
|
+
# Special case: do we have two methods, one of which is a class method
|
54
63
|
# and the other isn't? If so, do nothing - this is fine
|
55
64
|
if children.length == 2 &&
|
56
65
|
children.all? { |c| c.is_a?(RbiGenerator::Method) } &&
|
@@ -60,7 +69,22 @@ module Parlour
|
|
60
69
|
next
|
61
70
|
end
|
62
71
|
|
63
|
-
# Special case:
|
72
|
+
# Special case: if we remove the namespaces, is everything either an
|
73
|
+
# include or an extend? If so, do nothing - this is fine
|
74
|
+
if children \
|
75
|
+
.reject { |c| c.is_a?(RbiGenerator::Namespace) }
|
76
|
+
.then do |x|
|
77
|
+
!x.empty? && x.all? do |c|
|
78
|
+
c.is_a?(RbiGenerator::Include) || c.is_a?(RbiGenerator::Extend)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
deduplicate_mixins_of_name(namespace, name)
|
82
|
+
|
83
|
+
Debugging.debug_puts(self, Debugging::Tree.end("Includes/extends do not conflict with namespaces; no resolution required"))
|
84
|
+
next
|
85
|
+
end
|
86
|
+
|
87
|
+
# Special case: do we have two attributes, one of which is a class
|
64
88
|
# attribute and the other isn't? If so, do nothing - this is fine
|
65
89
|
if children.length == 2 &&
|
66
90
|
children.all? { |c| c.is_a?(RbiGenerator::Attribute) } &&
|
@@ -70,13 +94,13 @@ module Parlour
|
|
70
94
|
next
|
71
95
|
end
|
72
96
|
|
73
|
-
# Special case: are they all clearly equal? If so, remove all but one
|
97
|
+
# Optimization for Special case: are they all clearly equal? If so, remove all but one
|
74
98
|
if all_eql?(children)
|
75
99
|
Debugging.debug_puts(self, Debugging::Tree.end("All children are identical"))
|
76
100
|
|
77
101
|
# All of the children are the same, so this deletes all of them
|
78
102
|
namespace.children.delete(T.must(children.first))
|
79
|
-
|
103
|
+
|
80
104
|
# Re-add one child
|
81
105
|
namespace.children << T.must(children.first)
|
82
106
|
next
|
@@ -117,14 +141,25 @@ module Parlour
|
|
117
141
|
choice = resolver.call("Non-namespace item in a differing namespace conflict is not a single method", non_namespaces)
|
118
142
|
non_namespaces = []
|
119
143
|
non_namespaces << choice if choice
|
120
|
-
end
|
144
|
+
end
|
121
145
|
end
|
122
146
|
|
123
147
|
non_namespaces.each do |x|
|
124
148
|
namespace.children << x
|
125
149
|
end
|
126
150
|
|
127
|
-
|
151
|
+
# For certain namespace types the order matters. For example, if there's
|
152
|
+
# both a `Namespace` and `ModuleNamespace` then merging the two would
|
153
|
+
# produce different results depending on which is first.
|
154
|
+
first_index = (
|
155
|
+
namespaces.find_index { |x| RbiGenerator::EnumClassNamespace === x || RbiGenerator::StructClassNamespace === x } ||
|
156
|
+
namespaces.find_index { |x| RbiGenerator::ClassNamespace === x } ||
|
157
|
+
namespaces.find_index { |x| RbiGenerator::ModuleNamespace === x } ||
|
158
|
+
0
|
159
|
+
)
|
160
|
+
|
161
|
+
first = namespaces.delete_at(first_index)
|
162
|
+
rest = namespaces
|
128
163
|
else
|
129
164
|
raise 'unknown merge strategy; this is a Parlour bug'
|
130
165
|
end
|
@@ -161,16 +196,16 @@ module Parlour
|
|
161
196
|
|
162
197
|
sig { params(arr: T::Array[T.untyped]).returns(T.nilable(Symbol)) }
|
163
198
|
# Given an array, if all elements in the array are instances of the exact
|
164
|
-
# same class or are otherwise mergeable (for example Namespace and
|
199
|
+
# same class or are otherwise mergeable (for example Namespace and
|
165
200
|
# ClassNamespace), returns the kind of merge which needs to be made. A
|
166
201
|
# return value of nil indicates that the values cannot be merged.
|
167
202
|
#
|
168
203
|
# The following kinds are available:
|
169
204
|
# - They are all the same. (:normal)
|
170
|
-
# - There are exactly two types, one of which is Namespace and other is a
|
205
|
+
# - There are exactly two types, one of which is Namespace and other is a
|
171
206
|
# subclass of it. (:differing_namespaces)
|
172
207
|
# - One of them is Namespace or a subclass (or both, as described above),
|
173
|
-
# and the only other is Method. (also :differing_namespaces)
|
208
|
+
# and the only other is Method. (also :differing_namespaces)
|
174
209
|
#
|
175
210
|
# @param arr [Array] The array.
|
176
211
|
# @return [Symbol] The merge strategy to use, or nil if they can't be
|
@@ -182,21 +217,13 @@ module Parlour
|
|
182
217
|
|
183
218
|
# Find all the namespaces and non-namespaces
|
184
219
|
namespace_types, non_namespace_types = array_types.partition { |x| x <= RbiGenerator::Namespace }
|
220
|
+
exactly_namespace, namespace_subclasses = namespace_types.partition { |x| x == RbiGenerator::Namespace }
|
221
|
+
|
222
|
+
return nil unless namespace_subclasses.empty? \
|
223
|
+
|| (namespace_subclasses.length == 1 && namespace_subclasses.first < RbiGenerator::Namespace) \
|
224
|
+
|| namespace_subclasses.to_set == Set[RbiGenerator::ClassNamespace, RbiGenerator::StructClassNamespace] \
|
225
|
+
|| namespace_subclasses.to_set == Set[RbiGenerator::ClassNamespace, RbiGenerator::EnumClassNamespace]
|
185
226
|
|
186
|
-
# If there are two namespace types, one should be Namespace and the other
|
187
|
-
# should be a subclass of it
|
188
|
-
if namespace_types.length == 2
|
189
|
-
exactly_namespace, exactly_one_subclass = namespace_types.partition { |x| x == RbiGenerator::Namespace }
|
190
|
-
|
191
|
-
return nil unless exactly_namespace.length == 1 \
|
192
|
-
&& exactly_one_subclass.length == 1 \
|
193
|
-
&& exactly_one_subclass.first < RbiGenerator::Namespace
|
194
|
-
elsif namespace_types.length != 1
|
195
|
-
# The only other valid number of namespaces is 1, where we don't need to
|
196
|
-
# check anything
|
197
|
-
return nil
|
198
|
-
end
|
199
|
-
|
200
227
|
# It's OK, albeit cursed, for there to be a method with the same name as
|
201
228
|
# a namespace (Rainbow does this)
|
202
229
|
return nil if non_namespace_types.length != 0 && non_namespace_types != [RbiGenerator::Method]
|
@@ -213,5 +240,26 @@ module Parlour
|
|
213
240
|
def all_eql?(arr)
|
214
241
|
arr.each_cons(2).all? { |x, y| x == y }
|
215
242
|
end
|
243
|
+
|
244
|
+
sig { params(namespace: RbiGenerator::Namespace, name: T.nilable(String)).void }
|
245
|
+
# Given a namespace and a child name, removes all duplicate children that are mixins
|
246
|
+
# and that have the given name, except the first found instance.
|
247
|
+
#
|
248
|
+
# @param namespace [RbiGenerator::Namespace] The namespace to deduplicate mixins in.
|
249
|
+
# @param name [String] The name of the mixin modules to deduplicate.
|
250
|
+
# @return [void]
|
251
|
+
def deduplicate_mixins_of_name(namespace, name)
|
252
|
+
found_map = {}
|
253
|
+
namespace.children.delete_if do |x|
|
254
|
+
# ignore children whose names don't match
|
255
|
+
next unless x.name == name
|
256
|
+
# ignore children that are not mixins
|
257
|
+
next unless x.is_a?(RbiGenerator::Include) || x.is_a?(RbiGenerator::Extend)
|
258
|
+
|
259
|
+
delete = found_map.key?(x.class)
|
260
|
+
found_map[x.class] = true
|
261
|
+
delete
|
262
|
+
end
|
263
|
+
end
|
216
264
|
end
|
217
265
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# typed: true
|
2
|
+
require 'rainbow'
|
3
|
+
|
4
|
+
module Parlour
|
5
|
+
module Conversion
|
6
|
+
# An abstract class which converts between the node trees of two type
|
7
|
+
# systems.
|
8
|
+
class Converter
|
9
|
+
extend T::Sig
|
10
|
+
extend T::Helpers
|
11
|
+
abstract!
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@warnings = []
|
15
|
+
end
|
16
|
+
|
17
|
+
sig { returns(T::Array[[String, TypedObject]]) }
|
18
|
+
attr_reader :warnings
|
19
|
+
|
20
|
+
sig { params(msg: String, node: RbiGenerator::RbiObject).void }
|
21
|
+
def add_warning(msg, node)
|
22
|
+
warnings << [msg, node]
|
23
|
+
|
24
|
+
return if $VERBOSE.nil?
|
25
|
+
class_name = T.must(self.class.name).split('::').last
|
26
|
+
print Rainbow("Parlour warning: ").yellow.dark.bold
|
27
|
+
print Rainbow("#{class_name}: ").magenta.bright.bold
|
28
|
+
puts msg
|
29
|
+
print Rainbow(" └ at object: ").blue.bright.bold
|
30
|
+
puts node.describe
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,223 @@
|
|
1
|
+
# typed: true
|
2
|
+
module Parlour
|
3
|
+
module Conversion
|
4
|
+
# Converts RBI types to RBS types.
|
5
|
+
class RbiToRbs < Converter
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
sig { params(rbs_gen: RbsGenerator).void }
|
9
|
+
def initialize(rbs_gen)
|
10
|
+
super()
|
11
|
+
@rbs_gen = rbs_gen
|
12
|
+
end
|
13
|
+
|
14
|
+
sig { returns(RbsGenerator) }
|
15
|
+
attr_reader :rbs_gen
|
16
|
+
|
17
|
+
sig { params(from: RbiGenerator::Namespace, to: RbsGenerator::Namespace).void }
|
18
|
+
def convert_all(from, to)
|
19
|
+
from.children.each do |child|
|
20
|
+
convert_object(child, to)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
sig do
|
25
|
+
params(
|
26
|
+
node: RbiGenerator::RbiObject,
|
27
|
+
new_parent: RbsGenerator::Namespace,
|
28
|
+
).void
|
29
|
+
end
|
30
|
+
def convert_object(node, new_parent)
|
31
|
+
case node
|
32
|
+
when RbiGenerator::StructClassNamespace
|
33
|
+
add_warning 'performing a one-way conversion of an RBI struct to RBS', node
|
34
|
+
|
35
|
+
klass = new_parent.create_class(node.name)
|
36
|
+
klass.add_comments(node.comments)
|
37
|
+
|
38
|
+
# Create a constructor
|
39
|
+
klass.create_method('initialize', [
|
40
|
+
RbsGenerator::MethodSignature.new(
|
41
|
+
node.props.map do |prop|
|
42
|
+
RbsGenerator::Parameter.new(
|
43
|
+
"#{prop.name}:",
|
44
|
+
type: prop.type,
|
45
|
+
required: !prop.optional,
|
46
|
+
)
|
47
|
+
end,
|
48
|
+
nil,
|
49
|
+
)
|
50
|
+
])
|
51
|
+
|
52
|
+
# Make each prop a getter (and setter, if not immutable) attribute
|
53
|
+
node.props.each do |prop|
|
54
|
+
klass.create_attribute(
|
55
|
+
prop.name,
|
56
|
+
kind: prop.immutable ? :reader : :accessor,
|
57
|
+
type: prop.type,
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
klass
|
62
|
+
|
63
|
+
when RbiGenerator::EnumClassNamespace
|
64
|
+
add_warning 'performing a one-way conversion of an RBI enum to RBS', node
|
65
|
+
|
66
|
+
klass = new_parent.create_class(node.name)
|
67
|
+
klass.add_comments(node.comments)
|
68
|
+
|
69
|
+
# Define .values
|
70
|
+
klass.create_method('values', [
|
71
|
+
RbsGenerator::MethodSignature.new([], Types::Array.new(node.name))
|
72
|
+
], class_method: true)
|
73
|
+
|
74
|
+
# Define each enum variant
|
75
|
+
node.enums.each do |variant|
|
76
|
+
# We don't care about any extra value
|
77
|
+
variant = variant[0] if Array === variant
|
78
|
+
|
79
|
+
klass.create_constant(variant, type: node.name)
|
80
|
+
end
|
81
|
+
|
82
|
+
klass
|
83
|
+
|
84
|
+
when RbiGenerator::Arbitrary
|
85
|
+
add_warning 'converting type of Arbitrary is likely to cause syntax errors; doing it anyway', node
|
86
|
+
new_parent.create_arbitrary(
|
87
|
+
code: node.code,
|
88
|
+
).add_comments(node.comments)
|
89
|
+
|
90
|
+
when RbiGenerator::Attribute
|
91
|
+
if node.class_attribute
|
92
|
+
add_warning 'RBS does not support class attributes; dropping', node
|
93
|
+
return
|
94
|
+
end
|
95
|
+
new_parent.create_attribute(
|
96
|
+
node.name,
|
97
|
+
kind: node.kind,
|
98
|
+
type: node.type,
|
99
|
+
).add_comments(node.comments)
|
100
|
+
|
101
|
+
when RbiGenerator::ClassNamespace
|
102
|
+
if node.abstract
|
103
|
+
add_warning 'RBS does not support abstract classes', node
|
104
|
+
end
|
105
|
+
klass = new_parent.create_class(
|
106
|
+
node.name,
|
107
|
+
superclass: node.superclass
|
108
|
+
)
|
109
|
+
klass.add_comments(node.comments)
|
110
|
+
node.children.each do |child|
|
111
|
+
convert_object(child, klass)
|
112
|
+
end
|
113
|
+
|
114
|
+
when RbiGenerator::Constant
|
115
|
+
if node.eigen_constant
|
116
|
+
add_warning 'RBS does not support constants on eigenclasses; dropping', node
|
117
|
+
return
|
118
|
+
end
|
119
|
+
new_parent.create_constant(
|
120
|
+
node.name,
|
121
|
+
type: node.value,
|
122
|
+
).add_comments(node.comments)
|
123
|
+
|
124
|
+
when RbiGenerator::Extend
|
125
|
+
new_parent.create_extend(node.name).add_comments(node.comments)
|
126
|
+
|
127
|
+
when RbiGenerator::Include
|
128
|
+
new_parent.create_include(node.name).add_comments(node.comments)
|
129
|
+
|
130
|
+
when RbiGenerator::Method
|
131
|
+
# Convert parameters
|
132
|
+
parameters = node.parameters
|
133
|
+
.reject { |param| param.kind == :block }
|
134
|
+
.map do |param|
|
135
|
+
RbsGenerator::Parameter.new(
|
136
|
+
param.name,
|
137
|
+
type: param.type,
|
138
|
+
required: param.default.nil?
|
139
|
+
)
|
140
|
+
end
|
141
|
+
|
142
|
+
# Find block if there is one
|
143
|
+
block_param = node.parameters.find { |param| param.kind == :block }
|
144
|
+
if block_param
|
145
|
+
if String === block_param.type
|
146
|
+
add_warning "block must have a Types::Type for conversion; dropping block", node
|
147
|
+
block = nil
|
148
|
+
else
|
149
|
+
# A nilable proc is an optional block
|
150
|
+
block_param_type = block_param.type
|
151
|
+
if Types::Nilable === block_param_type && Types::Proc === block_param_type.type
|
152
|
+
t = T.cast(block_param_type.type, Types::Proc)
|
153
|
+
required = false
|
154
|
+
block = RbsGenerator::Block.new(t, required)
|
155
|
+
elsif Types::Proc === block_param_type
|
156
|
+
t = block_param_type
|
157
|
+
required = true
|
158
|
+
block = RbsGenerator::Block.new(t, required)
|
159
|
+
elsif Types::Untyped === block_param_type
|
160
|
+
# Consider there to be a block of unknown types
|
161
|
+
block = RbsGenerator::Block.new(
|
162
|
+
Types::Proc.new(
|
163
|
+
[
|
164
|
+
Types::Proc::Parameter.new('*args', Types::Untyped.new),
|
165
|
+
Types::Proc::Parameter.new('**kwargs', Types::Untyped.new),
|
166
|
+
],
|
167
|
+
Types::Untyped.new,
|
168
|
+
),
|
169
|
+
false,
|
170
|
+
)
|
171
|
+
else
|
172
|
+
add_warning 'block type must be a Types::Proc (or nilable one); dropping block', node
|
173
|
+
end
|
174
|
+
end
|
175
|
+
else
|
176
|
+
block = nil
|
177
|
+
end
|
178
|
+
|
179
|
+
new_parent.create_method(
|
180
|
+
node.name,
|
181
|
+
[
|
182
|
+
RbsGenerator::MethodSignature.new(
|
183
|
+
parameters,
|
184
|
+
node.return_type,
|
185
|
+
block: block,
|
186
|
+
type_parameters: node.type_parameters,
|
187
|
+
)
|
188
|
+
],
|
189
|
+
class_method: node.class_method,
|
190
|
+
).add_comments(node.comments)
|
191
|
+
|
192
|
+
when RbiGenerator::ModuleNamespace
|
193
|
+
if node.interface
|
194
|
+
rbs_node = new_parent.create_interface(
|
195
|
+
node.name,
|
196
|
+
)
|
197
|
+
else
|
198
|
+
rbs_node = new_parent.create_module(
|
199
|
+
node.name,
|
200
|
+
)
|
201
|
+
end
|
202
|
+
rbs_node.add_comments(node.comments)
|
203
|
+
node.children.each do |child|
|
204
|
+
convert_object(child, rbs_node)
|
205
|
+
end
|
206
|
+
|
207
|
+
when RbiGenerator::Namespace
|
208
|
+
add_warning 'unspecialized namespaces are not supposed to be in the tree; you may run into issues', node
|
209
|
+
namespace = RbsGenerator::Namespace.new(rbs_gen)
|
210
|
+
namespace.add_comments(node.comments)
|
211
|
+
node.children.each do |child|
|
212
|
+
convert_object(child, namespace)
|
213
|
+
end
|
214
|
+
new_parent.children << namespace
|
215
|
+
|
216
|
+
else
|
217
|
+
raise "missing conversion for #{node.describe}"
|
218
|
+
# TODO: stick a T.absurd here
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|