tapioca 0.4.27 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +15 -15
- data/README.md +2 -2
- data/Rakefile +5 -7
- data/exe/tapioca +2 -2
- data/lib/tapioca/cli.rb +172 -2
- data/lib/tapioca/compilers/dsl/aasm.rb +122 -0
- data/lib/tapioca/compilers/dsl/action_controller_helpers.rb +52 -12
- data/lib/tapioca/compilers/dsl/action_mailer.rb +6 -9
- data/lib/tapioca/compilers/dsl/active_job.rb +8 -12
- data/lib/tapioca/compilers/dsl/active_model_attributes.rb +131 -0
- data/lib/tapioca/compilers/dsl/active_model_secure_password.rb +101 -0
- data/lib/tapioca/compilers/dsl/active_record_associations.rb +33 -54
- data/lib/tapioca/compilers/dsl/active_record_columns.rb +10 -105
- data/lib/tapioca/compilers/dsl/active_record_enum.rb +8 -10
- data/lib/tapioca/compilers/dsl/active_record_fixtures.rb +86 -0
- data/lib/tapioca/compilers/dsl/active_record_scope.rb +7 -10
- data/lib/tapioca/compilers/dsl/active_record_typed_store.rb +5 -8
- data/lib/tapioca/compilers/dsl/active_resource.rb +9 -37
- data/lib/tapioca/compilers/dsl/active_storage.rb +98 -0
- data/lib/tapioca/compilers/dsl/active_support_concern.rb +106 -0
- data/lib/tapioca/compilers/dsl/active_support_current_attributes.rb +13 -8
- data/lib/tapioca/compilers/dsl/base.rb +108 -82
- data/lib/tapioca/compilers/dsl/config.rb +111 -0
- data/lib/tapioca/compilers/dsl/frozen_record.rb +5 -7
- data/lib/tapioca/compilers/dsl/identity_cache.rb +66 -29
- data/lib/tapioca/compilers/dsl/mixed_in_class_attributes.rb +74 -0
- data/lib/tapioca/compilers/dsl/protobuf.rb +19 -69
- data/lib/tapioca/compilers/dsl/sidekiq_worker.rb +25 -12
- data/lib/tapioca/compilers/dsl/smart_properties.rb +21 -33
- data/lib/tapioca/compilers/dsl/state_machines.rb +56 -78
- data/lib/tapioca/compilers/dsl/url_helpers.rb +7 -10
- data/lib/tapioca/compilers/dsl_compiler.rb +25 -40
- data/lib/tapioca/compilers/dynamic_mixin_compiler.rb +198 -0
- data/lib/tapioca/compilers/requires_compiler.rb +2 -2
- data/lib/tapioca/compilers/sorbet.rb +25 -5
- data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +122 -206
- data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +4 -4
- data/lib/tapioca/compilers/symbol_table_compiler.rb +5 -11
- data/lib/tapioca/compilers/todos_compiler.rb +1 -1
- data/lib/tapioca/config.rb +3 -0
- data/lib/tapioca/config_builder.rb +5 -2
- data/lib/tapioca/constant_locator.rb +6 -8
- data/lib/tapioca/gemfile.rb +14 -11
- data/lib/tapioca/generators/base.rb +61 -0
- data/lib/tapioca/generators/dsl.rb +362 -0
- data/lib/tapioca/generators/gem.rb +345 -0
- data/lib/tapioca/generators/init.rb +79 -0
- data/lib/tapioca/generators/require.rb +52 -0
- data/lib/tapioca/generators/todo.rb +76 -0
- data/lib/tapioca/generators.rb +9 -0
- data/lib/tapioca/generic_type_registry.rb +25 -98
- data/lib/tapioca/helpers/active_record_column_type_helper.rb +98 -0
- data/lib/tapioca/internal.rb +2 -10
- data/lib/tapioca/loader.rb +11 -31
- data/lib/tapioca/rbi_ext/model.rb +166 -0
- data/lib/tapioca/reflection.rb +138 -0
- data/lib/tapioca/sorbet_ext/fixed_hash_patch.rb +1 -1
- data/lib/tapioca/sorbet_ext/generic_name_patch.rb +72 -4
- data/lib/tapioca/sorbet_ext/name_patch.rb +1 -1
- data/lib/tapioca/version.rb +1 -1
- data/lib/tapioca.rb +3 -0
- metadata +45 -23
- data/lib/tapioca/cli/main.rb +0 -146
- data/lib/tapioca/core_ext/class.rb +0 -28
- data/lib/tapioca/core_ext/string.rb +0 -18
- data/lib/tapioca/generator.rb +0 -633
- data/lib/tapioca/rbi/model.rb +0 -405
- data/lib/tapioca/rbi/printer.rb +0 -410
- data/lib/tapioca/rbi/rewriters/group_nodes.rb +0 -106
- data/lib/tapioca/rbi/rewriters/nest_non_public_methods.rb +0 -65
- data/lib/tapioca/rbi/rewriters/nest_singleton_methods.rb +0 -42
- data/lib/tapioca/rbi/rewriters/sort_nodes.rb +0 -86
- data/lib/tapioca/rbi/visitor.rb +0 -21
@@ -1,48 +1,56 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require
|
4
|
+
require "pathname"
|
5
5
|
|
6
6
|
module Tapioca
|
7
7
|
module Compilers
|
8
8
|
module SymbolTable
|
9
9
|
class SymbolGenerator
|
10
10
|
extend(T::Sig)
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
11
|
+
include(Reflection)
|
12
|
+
|
13
|
+
IGNORED_SYMBOLS = T.let(["YAML", "MiniTest", "Mutex"], T::Array[String])
|
14
|
+
IGNORED_COMMENTS = T.let([
|
15
|
+
":doc:",
|
16
|
+
":nodoc:",
|
17
|
+
"typed:",
|
18
|
+
"frozen_string_literal:",
|
19
|
+
"encoding:",
|
20
|
+
"warn_indent:",
|
21
|
+
"shareable_constant_value:",
|
22
|
+
"rubocop:",
|
23
|
+
], T::Array[String])
|
24
|
+
|
25
|
+
sig { returns(Gemfile::GemSpec) }
|
26
|
+
attr_reader :gem
|
27
|
+
|
28
|
+
sig { returns(Integer) }
|
29
|
+
attr_reader :indent
|
30
|
+
|
31
|
+
sig { params(gem: Gemfile::GemSpec, indent: Integer, include_doc: T::Boolean).void }
|
32
|
+
def initialize(gem, indent = 0, include_doc = false)
|
22
33
|
@gem = gem
|
23
34
|
@indent = indent
|
24
|
-
@seen = Set.new
|
25
|
-
@alias_namespace
|
35
|
+
@seen = T.let(Set.new, T::Set[String])
|
36
|
+
@alias_namespace = T.let(Set.new, T::Set[String])
|
26
37
|
@symbol_queue = T.let(symbols.sort.dup, T::Array[String])
|
27
|
-
|
38
|
+
@symbols = T.let(nil, T.nilable(T::Set[String]))
|
39
|
+
@include_doc = include_doc
|
28
40
|
|
29
|
-
|
30
|
-
|
31
|
-
rbi = RBI::Tree.new
|
32
|
-
|
33
|
-
generate_from_symbol(rbi, T.must(@symbol_queue.shift)) until @symbol_queue.empty?
|
41
|
+
gem.parse_yard_docs if include_doc
|
42
|
+
end
|
34
43
|
|
35
|
-
|
36
|
-
|
37
|
-
rbi.
|
38
|
-
rbi.sort_nodes!
|
39
|
-
rbi.string
|
44
|
+
sig { params(rbi: RBI::File).void }
|
45
|
+
def generate(rbi)
|
46
|
+
generate_from_symbol(rbi.root, T.must(@symbol_queue.shift)) until @symbol_queue.empty?
|
40
47
|
end
|
41
48
|
|
42
49
|
private
|
43
50
|
|
51
|
+
sig { params(name: T.nilable(String)).void }
|
44
52
|
def add_to_symbol_queue(name)
|
45
|
-
@symbol_queue << name unless symbols.include?(name) || symbol_ignored?(name)
|
53
|
+
@symbol_queue << name unless name.nil? || symbols.include?(name) || symbol_ignored?(name)
|
46
54
|
end
|
47
55
|
|
48
56
|
sig { returns(T::Set[String]) }
|
@@ -57,8 +65,8 @@ module Tapioca
|
|
57
65
|
def engine_symbols(symbols)
|
58
66
|
return Set.new unless Object.const_defined?("Rails::Engine")
|
59
67
|
|
60
|
-
engine = Object.const_get("Rails::Engine")
|
61
|
-
.
|
68
|
+
engine = descendants_of(Object.const_get("Rails::Engine"))
|
69
|
+
.reject(&:abstract_railtie?)
|
62
70
|
.find do |klass|
|
63
71
|
name = name_of(klass)
|
64
72
|
!name.nil? && symbols.include?(name)
|
@@ -102,7 +110,7 @@ module Tapioca
|
|
102
110
|
return unless constant
|
103
111
|
return unless name
|
104
112
|
return if name.strip.empty?
|
105
|
-
return if name.start_with?(
|
113
|
+
return if name.start_with?("#<")
|
106
114
|
return if name.downcase == name
|
107
115
|
return if alias_namespaced?(name)
|
108
116
|
return if seen?(name)
|
@@ -144,7 +152,9 @@ module Tapioca
|
|
144
152
|
sig { params(tree: RBI::Tree, name: String, value: BasicObject).void.checked(:never) }
|
145
153
|
def compile_object(tree, name, value)
|
146
154
|
return if symbol_ignored?(name)
|
155
|
+
|
147
156
|
klass = class_of(value)
|
157
|
+
return if klass == TypeMember || klass == TypeTemplate
|
148
158
|
|
149
159
|
klass_name = if klass == ObjectSpace::WeakMap
|
150
160
|
# WeakMap is an implicit generic with one type variable
|
@@ -155,28 +165,33 @@ module Tapioca
|
|
155
165
|
name_of(klass)
|
156
166
|
end
|
157
167
|
|
168
|
+
comments = documentation_comments(name)
|
169
|
+
|
158
170
|
if klass_name == "T::Private::Types::TypeAlias"
|
159
|
-
|
171
|
+
constant = RBI::Const.new(name, "T.type_alias { #{T.unsafe(value).aliased_type} }", comments: comments)
|
172
|
+
tree << constant
|
160
173
|
return
|
161
174
|
end
|
162
175
|
|
163
176
|
return if klass_name&.start_with?("T::Types::", "T::Private::")
|
164
177
|
|
165
178
|
type_name = klass_name || "T.untyped"
|
179
|
+
constant = RBI::Const.new(name, "T.let(T.unsafe(nil), #{type_name})", comments: comments)
|
166
180
|
|
167
|
-
tree <<
|
181
|
+
tree << constant
|
168
182
|
end
|
169
183
|
|
170
184
|
sig { params(tree: RBI::Tree, name: String, constant: Module).void }
|
171
185
|
def compile_module(tree, name, constant)
|
172
186
|
return unless defined_in_gem?(constant, strict: false)
|
173
187
|
|
188
|
+
comments = documentation_comments(name)
|
174
189
|
scope =
|
175
190
|
if constant.is_a?(Class)
|
176
191
|
superclass = compile_superclass(constant)
|
177
|
-
RBI::Class.new(name, superclass_name: superclass)
|
192
|
+
RBI::Class.new(name, superclass_name: superclass, comments: comments)
|
178
193
|
else
|
179
|
-
RBI::Module.new(name)
|
194
|
+
RBI::Module.new(name, comments: comments)
|
180
195
|
end
|
181
196
|
|
182
197
|
compile_body(scope, name, constant)
|
@@ -189,13 +204,27 @@ module Tapioca
|
|
189
204
|
|
190
205
|
sig { params(tree: RBI::Tree, name: String, constant: Module).void }
|
191
206
|
def compile_body(tree, name, constant)
|
207
|
+
# Compiling type variables must happen first to populate generic names
|
208
|
+
compile_type_variables(tree, constant)
|
192
209
|
compile_methods(tree, name, constant)
|
193
210
|
compile_module_helpers(tree, constant)
|
194
|
-
compile_type_variables(tree, constant)
|
195
211
|
compile_mixins(tree, constant)
|
196
|
-
compile_mixes_in_class_methods(tree, constant)
|
197
212
|
compile_props(tree, constant)
|
198
213
|
compile_enums(tree, constant)
|
214
|
+
compile_dynamic_mixins(tree, constant)
|
215
|
+
end
|
216
|
+
|
217
|
+
sig { params(tree: RBI::Tree, constant: Module).void }
|
218
|
+
def compile_dynamic_mixins(tree, constant)
|
219
|
+
return if constant.is_a?(Class)
|
220
|
+
|
221
|
+
mixin_compiler = DynamicMixinCompiler.new(constant)
|
222
|
+
mixin_compiler.compile_class_attributes(tree)
|
223
|
+
dynamic_extends, dynamic_includes = mixin_compiler.compile_mixes_in_class_methods(tree)
|
224
|
+
|
225
|
+
(dynamic_includes + dynamic_extends).each do |mod|
|
226
|
+
add_to_symbol_queue(name_of(mod))
|
227
|
+
end
|
199
228
|
end
|
200
229
|
|
201
230
|
sig { params(tree: RBI::Tree, constant: Module).void }
|
@@ -267,12 +296,10 @@ module Tapioca
|
|
267
296
|
# Create a map of subconstants (via their object ids) to their names.
|
268
297
|
# We need this later when we want to lookup the name of the registered type
|
269
298
|
# variable via the value of the type variable constant.
|
270
|
-
subconstant_to_name_lookup = constants_of(constant)
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
]
|
275
|
-
end.to_h
|
299
|
+
subconstant_to_name_lookup = constants_of(constant)
|
300
|
+
.each_with_object({}.compare_by_identity) do |constant_name, table|
|
301
|
+
table[resolve_constant(constant_name.to_s, namespace: constant)] = constant_name.to_s
|
302
|
+
end
|
276
303
|
|
277
304
|
# Map each type variable to its string representation.
|
278
305
|
#
|
@@ -283,12 +310,13 @@ module Tapioca
|
|
283
310
|
# By looping over these entries and then getting the actual constant name
|
284
311
|
# from the `subconstant_to_name_lookup` we defined above, gives us all the
|
285
312
|
# information we need to serialize type variable definitions.
|
286
|
-
type_variable_declarations = type_variables.map do |
|
287
|
-
constant_name = subconstant_to_name_lookup[
|
313
|
+
type_variable_declarations = type_variables.map do |type_variable, serialized_type_variable|
|
314
|
+
constant_name = subconstant_to_name_lookup[type_variable]
|
315
|
+
type_variable.name = constant_name
|
288
316
|
# Here, we know that constant_value will be an instance of
|
289
317
|
# T::Types::CustomTypeVariable, which knows how to serialize
|
290
318
|
# itself to a type_member/type_template
|
291
|
-
tree << RBI::TypeMember.new(constant_name
|
319
|
+
tree << RBI::TypeMember.new(constant_name, serialized_type_variable)
|
292
320
|
end
|
293
321
|
|
294
322
|
return if type_variable_declarations.empty?
|
@@ -392,71 +420,6 @@ module Tapioca
|
|
392
420
|
end
|
393
421
|
end
|
394
422
|
|
395
|
-
sig { params(tree: RBI::Tree, constant: Module).void }
|
396
|
-
def compile_mixes_in_class_methods(tree, constant)
|
397
|
-
return if constant.is_a?(Class)
|
398
|
-
|
399
|
-
mixins_from_modules = {}
|
400
|
-
|
401
|
-
Class.new do
|
402
|
-
# rubocop:disable Style/MethodMissingSuper, Style/MissingRespondToMissing
|
403
|
-
def method_missing(symbol, *args)
|
404
|
-
end
|
405
|
-
|
406
|
-
define_singleton_method(:include) do |mod|
|
407
|
-
begin
|
408
|
-
before = singleton_class.ancestors
|
409
|
-
super(mod).tap do
|
410
|
-
mixins_from_modules[mod] = singleton_class.ancestors - before
|
411
|
-
end
|
412
|
-
rescue Exception # rubocop:disable Lint/RescueException
|
413
|
-
# this is a best effort, bail if we can't perform this
|
414
|
-
end
|
415
|
-
end
|
416
|
-
|
417
|
-
class << self
|
418
|
-
def method_missing(symbol, *args)
|
419
|
-
end
|
420
|
-
end
|
421
|
-
# rubocop:enable Style/MethodMissingSuper, Style/MissingRespondToMissing
|
422
|
-
end.include(constant)
|
423
|
-
|
424
|
-
all_dynamic_extends = mixins_from_modules.delete(constant)
|
425
|
-
all_dynamic_includes = mixins_from_modules.keys
|
426
|
-
dynamic_extends_from_dynamic_includes = mixins_from_modules.values.flatten
|
427
|
-
dynamic_extends = all_dynamic_extends - dynamic_extends_from_dynamic_includes
|
428
|
-
|
429
|
-
all_dynamic_includes
|
430
|
-
.select { |mod| (name = name_of(mod)) && !name.start_with?("T::") }
|
431
|
-
.map do |mod|
|
432
|
-
add_to_symbol_queue(name_of(mod))
|
433
|
-
|
434
|
-
qname = qualified_name_of(mod)
|
435
|
-
tree << RBI::Include.new(T.must(qname))
|
436
|
-
end.join("\n")
|
437
|
-
|
438
|
-
ancestors = singleton_class_of(constant).ancestors
|
439
|
-
extends_as_concern = ancestors.any? do |mod|
|
440
|
-
qualified_name_of(mod) == "::ActiveSupport::Concern"
|
441
|
-
end
|
442
|
-
class_methods_module = resolve_constant("#{name_of(constant)}::ClassMethods")
|
443
|
-
|
444
|
-
mixed_in_module = if extends_as_concern && Module === class_methods_module
|
445
|
-
class_methods_module
|
446
|
-
else
|
447
|
-
dynamic_extends.find { |mod| mod != constant }
|
448
|
-
end
|
449
|
-
|
450
|
-
return if mixed_in_module.nil?
|
451
|
-
|
452
|
-
qualified_name = qualified_name_of(mixed_in_module)
|
453
|
-
return if qualified_name.nil? || qualified_name == ""
|
454
|
-
|
455
|
-
tree << RBI::MixesInClassMethods.new(qualified_name)
|
456
|
-
rescue
|
457
|
-
nil # silence errors
|
458
|
-
end
|
459
|
-
|
460
423
|
sig { params(tree: RBI::Tree, name: String, constant: Module).void }
|
461
424
|
def compile_methods(tree, name, constant)
|
462
425
|
compile_method(
|
@@ -486,11 +449,11 @@ module Tapioca
|
|
486
449
|
next if name == :initialize
|
487
450
|
vis = case visibility
|
488
451
|
when :protected
|
489
|
-
RBI::
|
452
|
+
RBI::Protected.new
|
490
453
|
when :private
|
491
|
-
RBI::
|
454
|
+
RBI::Private.new
|
492
455
|
else
|
493
|
-
RBI::
|
456
|
+
RBI::Public.new
|
494
457
|
end
|
495
458
|
compile_method(tree, module_name, mod, mod.instance_method(name), vis)
|
496
459
|
end
|
@@ -500,9 +463,9 @@ module Tapioca
|
|
500
463
|
sig { params(mod: Module).returns(T::Hash[Symbol, T::Array[Symbol]]) }
|
501
464
|
def method_names_by_visibility(mod)
|
502
465
|
{
|
503
|
-
public:
|
504
|
-
protected:
|
505
|
-
private:
|
466
|
+
public: public_instance_methods_of(mod),
|
467
|
+
protected: protected_instance_methods_of(mod),
|
468
|
+
private: private_instance_methods_of(mod),
|
506
469
|
}
|
507
470
|
end
|
508
471
|
|
@@ -513,7 +476,7 @@ module Tapioca
|
|
513
476
|
constant
|
514
477
|
.props
|
515
478
|
.keys
|
516
|
-
.include?(method_name.gsub(/=$/,
|
479
|
+
.include?(method_name.gsub(/=$/, "").to_sym)
|
517
480
|
end
|
518
481
|
|
519
482
|
sig do
|
@@ -525,7 +488,7 @@ module Tapioca
|
|
525
488
|
visibility: RBI::Visibility
|
526
489
|
).void
|
527
490
|
end
|
528
|
-
def compile_method(tree, symbol_name, constant, method, visibility = RBI::
|
491
|
+
def compile_method(tree, symbol_name, constant, method, visibility = RBI::Public.new)
|
529
492
|
return unless method
|
530
493
|
return unless method.owner == constant
|
531
494
|
return if symbol_ignored?(symbol_name) && !method_in_gem?(method)
|
@@ -572,7 +535,15 @@ module Tapioca
|
|
572
535
|
[type, name]
|
573
536
|
end
|
574
537
|
|
575
|
-
|
538
|
+
separator = constant.singleton_class? ? "." : "#"
|
539
|
+
comments = documentation_comments("#{symbol_name}#{separator}#{method_name}")
|
540
|
+
rbi_method = RBI::Method.new(
|
541
|
+
method_name,
|
542
|
+
is_singleton: constant.singleton_class?,
|
543
|
+
visibility: visibility,
|
544
|
+
comments: comments
|
545
|
+
)
|
546
|
+
|
576
547
|
rbi_method.sigs << compile_signature(signature, sanitized_parameters) if signature
|
577
548
|
|
578
549
|
sanitized_parameters.each do |type, name|
|
@@ -615,7 +586,7 @@ module Tapioca
|
|
615
586
|
sig << RBI::SigParam.new(name, type)
|
616
587
|
end
|
617
588
|
|
618
|
-
return_type =
|
589
|
+
return_type = name_of_type(signature.return_type)
|
619
590
|
sig.return_type = sanitize_signature_types(return_type)
|
620
591
|
add_to_symbol_queue(sig.return_type)
|
621
592
|
|
@@ -638,12 +609,24 @@ module Tapioca
|
|
638
609
|
sig
|
639
610
|
end
|
640
611
|
|
612
|
+
sig { params(sig_string: String).returns(String) }
|
613
|
+
def sanitize_signature_types(sig_string)
|
614
|
+
sig_string
|
615
|
+
.gsub(".returns(<VOID>)", ".void")
|
616
|
+
.gsub("<VOID>", "void")
|
617
|
+
.gsub("<NOT-TYPED>", "T.untyped")
|
618
|
+
.gsub(".params()", "")
|
619
|
+
end
|
620
|
+
|
641
621
|
sig { params(symbol_name: String).returns(T::Boolean) }
|
642
622
|
def symbol_ignored?(symbol_name)
|
643
623
|
SymbolLoader.ignore_symbol?(symbol_name)
|
644
624
|
end
|
645
625
|
|
646
|
-
SPECIAL_METHOD_NAMES =
|
626
|
+
SPECIAL_METHOD_NAMES = T.let([
|
627
|
+
"!", "~", "+@", "**", "-@", "*", "/", "%", "+", "-", "<<", ">>", "&", "|", "^",
|
628
|
+
"<", "<=", "=>", ">", ">=", "==", "===", "!=", "=~", "!~", "<=>", "[]", "[]=", "`"
|
629
|
+
], T::Array[String])
|
647
630
|
|
648
631
|
sig { params(name: String).returns(T::Boolean) }
|
649
632
|
def valid_method_name?(name)
|
@@ -702,46 +685,13 @@ module Tapioca
|
|
702
685
|
@seen.include?(name)
|
703
686
|
end
|
704
687
|
|
688
|
+
sig { params(constant: Module).returns(T.nilable(UnboundMethod)) }
|
705
689
|
def initialize_method_for(constant)
|
706
690
|
constant.instance_method(:initialize)
|
707
691
|
rescue
|
708
692
|
nil
|
709
693
|
end
|
710
694
|
|
711
|
-
sig { params(constant: BasicObject).returns(Class).checked(:never) }
|
712
|
-
def class_of(constant)
|
713
|
-
Kernel.instance_method(:class).bind(constant).call
|
714
|
-
end
|
715
|
-
|
716
|
-
sig { params(constant: Module).returns(T::Array[Symbol]) }
|
717
|
-
def constants_of(constant)
|
718
|
-
Module.instance_method(:constants).bind(constant).call(false)
|
719
|
-
end
|
720
|
-
|
721
|
-
sig { params(constant: Module).returns(T.nilable(String)) }
|
722
|
-
def raw_name_of(constant)
|
723
|
-
Module.instance_method(:name).bind(constant).call
|
724
|
-
end
|
725
|
-
|
726
|
-
sig { params(constant: Module).returns(Class) }
|
727
|
-
def singleton_class_of(constant)
|
728
|
-
Object.instance_method(:singleton_class).bind(constant).call
|
729
|
-
end
|
730
|
-
|
731
|
-
sig { params(constant: Module).returns(T::Array[Module]) }
|
732
|
-
def ancestors_of(constant)
|
733
|
-
Module.instance_method(:ancestors).bind(constant).call
|
734
|
-
end
|
735
|
-
|
736
|
-
sig { params(constant: Module).returns(T::Array[Module]) }
|
737
|
-
def inherited_ancestors_of(constant)
|
738
|
-
if Class === constant
|
739
|
-
ancestors_of(superclass_of(constant) || Object)
|
740
|
-
else
|
741
|
-
Module.ancestors
|
742
|
-
end
|
743
|
-
end
|
744
|
-
|
745
695
|
sig { params(constant: Module).returns(T::Array[Module]) }
|
746
696
|
def interesting_ancestors_of(constant)
|
747
697
|
inherited_ancestors_ids = Set.new(
|
@@ -771,9 +721,9 @@ module Tapioca
|
|
771
721
|
|
772
722
|
sig { params(constant: Module).returns(T.nilable(String)) }
|
773
723
|
def name_of(constant)
|
774
|
-
name = name_of_proxy_target(constant)
|
724
|
+
name = name_of_proxy_target(constant, super(class_of(constant)))
|
775
725
|
return name if name
|
776
|
-
name =
|
726
|
+
name = super(constant)
|
777
727
|
return if name.nil?
|
778
728
|
return unless are_equal?(constant, resolve_constant(name, inherit: true))
|
779
729
|
name = "Struct" if name =~ /^(::)?Struct::[^:]+$/
|
@@ -793,67 +743,33 @@ module Tapioca
|
|
793
743
|
"#{type_name}[#{type_variable_names}]"
|
794
744
|
end
|
795
745
|
|
796
|
-
sig { params(constant: Module).returns(T.nilable(String)) }
|
797
|
-
def name_of_proxy_target(constant)
|
798
|
-
|
799
|
-
return unless raw_name_of(klass) == "ActiveSupport::Deprecation::DeprecatedConstantProxy"
|
746
|
+
sig { params(constant: Module, class_name: T.nilable(String)).returns(T.nilable(String)) }
|
747
|
+
def name_of_proxy_target(constant, class_name)
|
748
|
+
return unless class_name == "ActiveSupport::Deprecation::DeprecatedConstantProxy"
|
800
749
|
# We are dealing with a ActiveSupport::Deprecation::DeprecatedConstantProxy
|
801
750
|
# so try to get the name of the target class
|
802
751
|
begin
|
803
|
-
target =
|
752
|
+
target = constant.__send__(:target)
|
804
753
|
rescue NoMethodError
|
805
754
|
return
|
806
755
|
end
|
807
756
|
|
808
|
-
|
757
|
+
name_of(target)
|
809
758
|
end
|
810
759
|
|
811
|
-
sig { params(
|
812
|
-
def
|
813
|
-
|
814
|
-
return if name.nil?
|
760
|
+
sig { params(name: String).returns(T::Array[RBI::Comment]) }
|
761
|
+
def documentation_comments(name)
|
762
|
+
return [] unless @include_doc
|
815
763
|
|
816
|
-
|
817
|
-
|
818
|
-
else
|
819
|
-
"::#{name}"
|
820
|
-
end
|
821
|
-
end
|
822
|
-
|
823
|
-
sig { params(constant: Class).returns(T.nilable(Class)) }
|
824
|
-
def superclass_of(constant)
|
825
|
-
Class.instance_method(:superclass).bind(constant).call
|
826
|
-
end
|
827
|
-
|
828
|
-
sig { params(method: T.any(UnboundMethod, Method)).returns(T.untyped) }
|
829
|
-
def signature_of(method)
|
830
|
-
T::Private::Methods.signature_for_method(method)
|
831
|
-
rescue LoadError, StandardError
|
832
|
-
nil
|
833
|
-
end
|
764
|
+
yard_docs = YARD::Registry.at(name)
|
765
|
+
return [] unless yard_docs
|
834
766
|
|
835
|
-
|
836
|
-
|
837
|
-
sig_string
|
838
|
-
.gsub(".returns(<VOID>)", ".void")
|
839
|
-
.gsub("<VOID>", "void")
|
840
|
-
.gsub("<NOT-TYPED>", "T.untyped")
|
841
|
-
.gsub(".params()", "")
|
842
|
-
end
|
843
|
-
|
844
|
-
sig { params(constant: T::Types::Base).returns(String) }
|
845
|
-
def type_of(constant)
|
846
|
-
constant.to_s.gsub(/\bAttachedClass\b/, "T.attached_class")
|
847
|
-
end
|
848
|
-
|
849
|
-
sig { params(object: BasicObject).returns(Integer).checked(:never) }
|
850
|
-
def object_id_of(object)
|
851
|
-
Object.instance_method(:object_id).bind(object).call
|
852
|
-
end
|
767
|
+
docstring = yard_docs.docstring
|
768
|
+
return [] if /(copyright|license)/i.match?(docstring)
|
853
769
|
|
854
|
-
|
855
|
-
|
856
|
-
|
770
|
+
docstring.lines
|
771
|
+
.reject { |line| IGNORED_COMMENTS.any? { |comment| line.include?(comment) } }
|
772
|
+
.map! { |line| RBI::Comment.new(line) }
|
857
773
|
end
|
858
774
|
end
|
859
775
|
end
|
@@ -1,8 +1,8 @@
|
|
1
1
|
# typed: true
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require
|
5
|
-
require
|
4
|
+
require "json"
|
5
|
+
require "tempfile"
|
6
6
|
|
7
7
|
module Tapioca
|
8
8
|
module Compilers
|
@@ -25,7 +25,7 @@ module Tapioca
|
|
25
25
|
|
26
26
|
sig { params(paths: T::Array[String]).returns(T::Set[String]) }
|
27
27
|
def load_symbols(paths)
|
28
|
-
output = T.cast(Tempfile.create(
|
28
|
+
output = T.cast(Tempfile.create("sorbet") do |file|
|
29
29
|
file.write(Array(paths).join("\n"))
|
30
30
|
file.flush
|
31
31
|
|
@@ -69,7 +69,7 @@ module Tapioca
|
|
69
69
|
# TODO: CLASS is removed since v0.4.4730 of Sorbet
|
70
70
|
# but keeping here for backward compatibility. Remove
|
71
71
|
# once the minimum version is moved past that.
|
72
|
-
next unless
|
72
|
+
next unless ["CLASS", "CLASS_OR_MODULE", "STATIC_FIELD"].include?(kind)
|
73
73
|
next if name =~ /[<>()$]/
|
74
74
|
next if name =~ /^[0-9]+$/
|
75
75
|
next if name == "T::Helpers"
|
@@ -6,17 +6,11 @@ module Tapioca
|
|
6
6
|
class SymbolTableCompiler
|
7
7
|
extend(T::Sig)
|
8
8
|
|
9
|
-
sig
|
10
|
-
|
11
|
-
|
12
|
-
indent
|
13
|
-
|
14
|
-
end
|
15
|
-
def compile(
|
16
|
-
gem,
|
17
|
-
indent = 0
|
18
|
-
)
|
19
|
-
Tapioca::Compilers::SymbolTable::SymbolGenerator.new(gem, indent).generate
|
9
|
+
sig { params(gem: Gemfile::GemSpec, rbi: RBI::File, indent: Integer, include_docs: T::Boolean).void }
|
10
|
+
def compile(gem, rbi, indent = 0, include_docs = false)
|
11
|
+
Tapioca::Compilers::SymbolTable::SymbolGenerator
|
12
|
+
.new(gem, indent, include_docs)
|
13
|
+
.generate(rbi)
|
20
14
|
end
|
21
15
|
end
|
22
16
|
end
|
@@ -13,7 +13,7 @@ module Tapioca
|
|
13
13
|
def compile
|
14
14
|
list_todos.each_line.map do |line|
|
15
15
|
next if line.include?("<") || line.include?("class_of")
|
16
|
-
"module #{line.strip.gsub(
|
16
|
+
"module #{line.strip.gsub("T.untyped::", "")}; end"
|
17
17
|
end.compact.join("\n")
|
18
18
|
end
|
19
19
|
|
data/lib/tapioca/config.rb
CHANGED
@@ -9,9 +9,12 @@ module Tapioca
|
|
9
9
|
const(:prerequire, T.nilable(String))
|
10
10
|
const(:postrequire, String)
|
11
11
|
const(:exclude, T::Array[String])
|
12
|
+
const(:exclude_generators, T::Array[String])
|
12
13
|
const(:typed_overrides, T::Hash[String, String])
|
13
14
|
const(:todos_path, String)
|
14
15
|
const(:generators, T::Array[String])
|
16
|
+
const(:file_header, T::Boolean, default: true)
|
17
|
+
const(:doc, T::Boolean, default: false)
|
15
18
|
|
16
19
|
sig { returns(Pathname) }
|
17
20
|
def outpath
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# typed: strict
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require
|
4
|
+
require "yaml"
|
5
5
|
|
6
6
|
module Tapioca
|
7
7
|
class ConfigBuilder
|
@@ -33,7 +33,7 @@ module Tapioca
|
|
33
33
|
sig { params(command: Symbol).returns(T::Hash[String, T.untyped]) }
|
34
34
|
def default_options(command)
|
35
35
|
default_outdir = case command
|
36
|
-
when :sync, :generate
|
36
|
+
when :sync, :generate, :gem
|
37
37
|
Config::DEFAULT_GEMDIR
|
38
38
|
when :dsl
|
39
39
|
Config::DEFAULT_DSLDIR
|
@@ -62,9 +62,12 @@ module Tapioca
|
|
62
62
|
"postrequire" => Config::DEFAULT_POSTREQUIRE,
|
63
63
|
"outdir" => nil,
|
64
64
|
"exclude" => [],
|
65
|
+
"exclude_generators" => [],
|
65
66
|
"typed_overrides" => Config::DEFAULT_OVERRIDES,
|
66
67
|
"todos_path" => Config::DEFAULT_TODOSPATH,
|
67
68
|
"generators" => [],
|
69
|
+
"file_header" => true,
|
70
|
+
"doc" => false,
|
68
71
|
}.freeze, T::Hash[String, T.untyped])
|
69
72
|
end
|
70
73
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# typed: true
|
2
2
|
# frozen_string_literal: true
|
3
3
|
|
4
|
-
require
|
4
|
+
require "set"
|
5
5
|
|
6
6
|
module Tapioca
|
7
7
|
# Registers a TracePoint immediately upon load to track points at which
|
@@ -9,15 +9,14 @@ module Tapioca
|
|
9
9
|
# correspondence between classes/modules and files, as this information isn't
|
10
10
|
# available in the ruby runtime without extra accounting.
|
11
11
|
module ConstantLocator
|
12
|
-
|
12
|
+
extend Reflection
|
13
13
|
|
14
|
-
|
15
|
-
private_constant :NAME
|
14
|
+
@class_files = {}
|
16
15
|
|
17
16
|
# Immediately activated upon load. Observes class/module definition.
|
18
17
|
TracePoint.trace(:class) do |tp|
|
19
18
|
unless tp.self.singleton_class?
|
20
|
-
key =
|
19
|
+
key = name_of(tp.self)
|
21
20
|
@class_files[key] ||= Set.new
|
22
21
|
@class_files[key] << tp.path
|
23
22
|
end
|
@@ -26,11 +25,10 @@ module Tapioca
|
|
26
25
|
# Returns the files in which this class or module was opened. Doesn't know
|
27
26
|
# about situations where the class was opened prior to +require+ing,
|
28
27
|
# or where metaprogramming was used via +eval+, etc.
|
29
|
-
def files_for(klass)
|
30
|
-
name = String === klass ? klass :
|
28
|
+
def self.files_for(klass)
|
29
|
+
name = String === klass ? klass : name_of(klass)
|
31
30
|
files = @class_files[name]
|
32
31
|
files || Set.new
|
33
32
|
end
|
34
|
-
module_function :files_for
|
35
33
|
end
|
36
34
|
end
|