tapioca 0.6.4 → 0.7.2
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/Gemfile +8 -2
- data/README.md +27 -15
- data/Rakefile +10 -14
- data/lib/tapioca/cli.rb +65 -80
- data/lib/tapioca/{generators/base.rb → commands/command.rb} +16 -9
- data/lib/tapioca/{generators → commands}/dsl.rb +59 -45
- data/lib/tapioca/{generators → commands}/gem.rb +93 -30
- data/lib/tapioca/{generators → commands}/init.rb +9 -13
- data/lib/tapioca/{generators → commands}/require.rb +8 -10
- data/lib/tapioca/commands/todo.rb +86 -0
- data/lib/tapioca/commands.rb +13 -0
- data/lib/tapioca/dsl/compiler.rb +185 -0
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/aasm.rb +12 -9
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/action_controller_helpers.rb +13 -20
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/action_mailer.rb +10 -8
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_job.rb +11 -9
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_model_attributes.rb +13 -11
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_model_secure_password.rb +10 -12
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_associations.rb +28 -34
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_columns.rb +18 -16
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_enum.rb +14 -12
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_fixtures.rb +12 -8
- data/lib/tapioca/dsl/compilers/active_record_relations.rb +712 -0
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_scope.rb +21 -20
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_record_typed_store.rb +11 -16
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_resource.rb +10 -8
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_storage.rb +14 -10
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_support_concern.rb +19 -14
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/active_support_current_attributes.rb +16 -21
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/config.rb +11 -9
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/frozen_record.rb +13 -11
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/identity_cache.rb +23 -22
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/mixed_in_class_attributes.rb +12 -10
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/protobuf.rb +22 -10
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/rails_generators.rb +12 -13
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/sidekiq_worker.rb +14 -13
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/smart_properties.rb +11 -9
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/state_machines.rb +12 -10
- data/lib/tapioca/{compilers/dsl → dsl/compilers}/url_helpers.rb +20 -15
- data/lib/tapioca/dsl/compilers.rb +31 -0
- data/lib/tapioca/{compilers/dsl → dsl}/extensions/frozen_record.rb +2 -2
- data/lib/tapioca/dsl/helpers/active_record_column_type_helper.rb +114 -0
- data/lib/tapioca/dsl/helpers/active_record_constants_helper.rb +29 -0
- data/lib/tapioca/{compilers/dsl → dsl/helpers}/param_helper.rb +6 -3
- data/lib/tapioca/dsl/pipeline.rb +169 -0
- data/lib/tapioca/gem/events.rb +120 -0
- data/lib/tapioca/gem/listeners/base.rb +48 -0
- data/lib/tapioca/gem/listeners/dynamic_mixins.rb +32 -0
- data/lib/tapioca/gem/listeners/methods.rb +183 -0
- data/lib/tapioca/gem/listeners/mixins.rb +101 -0
- data/lib/tapioca/gem/listeners/remove_empty_payload_scopes.rb +21 -0
- data/lib/tapioca/gem/listeners/sorbet_enums.rb +26 -0
- data/lib/tapioca/gem/listeners/sorbet_helpers.rb +29 -0
- data/lib/tapioca/gem/listeners/sorbet_props.rb +33 -0
- data/lib/tapioca/gem/listeners/sorbet_required_ancestors.rb +23 -0
- data/lib/tapioca/gem/listeners/sorbet_signatures.rb +79 -0
- data/lib/tapioca/gem/listeners/sorbet_type_variables.rb +51 -0
- data/lib/tapioca/gem/listeners/subconstants.rb +37 -0
- data/lib/tapioca/gem/listeners/yard_doc.rb +96 -0
- data/lib/tapioca/gem/listeners.rb +16 -0
- data/lib/tapioca/gem/pipeline.rb +365 -0
- data/lib/tapioca/helpers/cli_helper.rb +7 -0
- data/lib/tapioca/helpers/config_helper.rb +5 -8
- data/lib/tapioca/helpers/shims_helper.rb +87 -0
- data/lib/tapioca/helpers/signatures_helper.rb +17 -0
- data/lib/tapioca/helpers/sorbet_helper.rb +57 -0
- data/lib/tapioca/helpers/test/dsl_compiler.rb +118 -0
- data/lib/tapioca/helpers/test/isolation.rb +1 -1
- data/lib/tapioca/helpers/test/template.rb +13 -2
- data/lib/tapioca/helpers/type_variable_helper.rb +43 -0
- data/lib/tapioca/internal.rb +18 -10
- data/lib/tapioca/rbi_ext/model.rb +14 -50
- data/lib/tapioca/rbi_formatter.rb +37 -0
- data/lib/tapioca/runtime/dynamic_mixin_compiler.rb +227 -0
- data/lib/tapioca/runtime/generic_type_registry.rb +168 -0
- data/lib/tapioca/runtime/loader.rb +123 -0
- data/lib/tapioca/runtime/reflection.rb +157 -0
- data/lib/tapioca/runtime/trackers/autoload.rb +72 -0
- data/lib/tapioca/runtime/trackers/constant_definition.rb +44 -0
- data/lib/tapioca/runtime/trackers/mixin.rb +80 -0
- data/lib/tapioca/runtime/trackers/required_ancestor.rb +50 -0
- data/lib/tapioca/{trackers.rb → runtime/trackers.rb} +4 -3
- data/lib/tapioca/sorbet_ext/generic_name_patch.rb +69 -34
- data/lib/tapioca/sorbet_ext/name_patch.rb +7 -1
- data/lib/tapioca/{compilers → static}/requires_compiler.rb +2 -2
- data/lib/tapioca/static/symbol_loader.rb +83 -0
- data/lib/tapioca/static/symbol_table_parser.rb +63 -0
- data/lib/tapioca/version.rb +1 -1
- data/lib/tapioca.rb +2 -7
- metadata +83 -62
- data/lib/tapioca/compilers/dsl/active_record_relations.rb +0 -720
- data/lib/tapioca/compilers/dsl/base.rb +0 -195
- data/lib/tapioca/compilers/dsl/helper/active_record_constants.rb +0 -27
- data/lib/tapioca/compilers/dsl_compiler.rb +0 -134
- data/lib/tapioca/compilers/dynamic_mixin_compiler.rb +0 -223
- data/lib/tapioca/compilers/sorbet.rb +0 -59
- data/lib/tapioca/compilers/symbol_table/symbol_generator.rb +0 -780
- data/lib/tapioca/compilers/symbol_table/symbol_loader.rb +0 -90
- data/lib/tapioca/compilers/symbol_table_compiler.rb +0 -17
- data/lib/tapioca/compilers/todos_compiler.rb +0 -32
- data/lib/tapioca/generators/todo.rb +0 -76
- data/lib/tapioca/generators.rb +0 -9
- data/lib/tapioca/generic_type_registry.rb +0 -164
- data/lib/tapioca/helpers/active_record_column_type_helper.rb +0 -108
- data/lib/tapioca/loader.rb +0 -119
- data/lib/tapioca/reflection.rb +0 -151
- data/lib/tapioca/trackers/autoload.rb +0 -70
- data/lib/tapioca/trackers/constant_definition.rb +0 -42
- data/lib/tapioca/trackers/mixin.rb +0 -78
|
@@ -1,780 +0,0 @@
|
|
|
1
|
-
# typed: strict
|
|
2
|
-
# frozen_string_literal: true
|
|
3
|
-
|
|
4
|
-
require "pathname"
|
|
5
|
-
|
|
6
|
-
module Tapioca
|
|
7
|
-
module Compilers
|
|
8
|
-
module SymbolTable
|
|
9
|
-
class SymbolGenerator
|
|
10
|
-
extend(T::Sig)
|
|
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)
|
|
33
|
-
@gem = gem
|
|
34
|
-
@indent = indent
|
|
35
|
-
@seen = T.let(Set.new, T::Set[String])
|
|
36
|
-
@alias_namespace = T.let(Set.new, T::Set[String])
|
|
37
|
-
@symbol_queue = T.let(symbols.sort.dup, T::Array[String])
|
|
38
|
-
@symbols = T.let(nil, T.nilable(T::Set[String]))
|
|
39
|
-
@include_doc = include_doc
|
|
40
|
-
|
|
41
|
-
gem.parse_yard_docs if include_doc
|
|
42
|
-
end
|
|
43
|
-
|
|
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?
|
|
47
|
-
end
|
|
48
|
-
|
|
49
|
-
private
|
|
50
|
-
|
|
51
|
-
sig { params(name: T.nilable(String)).void }
|
|
52
|
-
def add_to_symbol_queue(name)
|
|
53
|
-
@symbol_queue << name unless name.nil? || symbols.include?(name) || symbol_ignored?(name)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
sig { returns(T::Set[String]) }
|
|
57
|
-
def symbols
|
|
58
|
-
@symbols ||= begin
|
|
59
|
-
symbols = Tapioca::Compilers::SymbolTable::SymbolLoader.list_from_paths(gem.files)
|
|
60
|
-
symbols.union(engine_symbols(symbols))
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
sig { params(symbols: T::Set[String]).returns(T::Set[String]) }
|
|
65
|
-
def engine_symbols(symbols)
|
|
66
|
-
return Set.new unless Object.const_defined?("Rails::Engine")
|
|
67
|
-
|
|
68
|
-
engine = descendants_of(Object.const_get("Rails::Engine"))
|
|
69
|
-
.reject(&:abstract_railtie?)
|
|
70
|
-
.find do |klass|
|
|
71
|
-
name = name_of(klass)
|
|
72
|
-
!name.nil? && symbols.include?(name)
|
|
73
|
-
end
|
|
74
|
-
|
|
75
|
-
return Set.new unless engine
|
|
76
|
-
|
|
77
|
-
paths = engine.config.eager_load_paths.flat_map do |load_path|
|
|
78
|
-
Pathname.glob("#{load_path}/**/*.rb")
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
Tapioca::Compilers::SymbolTable::SymbolLoader.list_from_paths(paths)
|
|
82
|
-
rescue
|
|
83
|
-
Set.new
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
sig { params(tree: RBI::Tree, symbol: String).void }
|
|
87
|
-
def generate_from_symbol(tree, symbol)
|
|
88
|
-
constant = constantize(symbol)
|
|
89
|
-
|
|
90
|
-
return unless constant
|
|
91
|
-
|
|
92
|
-
compile(tree, symbol, constant)
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
sig { params(tree: RBI::Tree, name: T.nilable(String), constant: BasicObject).void.checked(:never) }
|
|
96
|
-
def compile(tree, name, constant)
|
|
97
|
-
return unless constant
|
|
98
|
-
return unless name
|
|
99
|
-
return if name.strip.empty?
|
|
100
|
-
return if name.start_with?("#<")
|
|
101
|
-
return if name.downcase == name
|
|
102
|
-
return if alias_namespaced?(name)
|
|
103
|
-
return if seen?(name)
|
|
104
|
-
return if T::Enum === constant # T::Enum instances are defined via `compile_enums`
|
|
105
|
-
|
|
106
|
-
mark_seen(name)
|
|
107
|
-
compile_constant(tree, name, constant)
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
sig { params(tree: RBI::Tree, name: String, constant: BasicObject).void.checked(:never) }
|
|
111
|
-
def compile_constant(tree, name, constant)
|
|
112
|
-
case constant
|
|
113
|
-
when Module
|
|
114
|
-
if name_of(constant) != name
|
|
115
|
-
compile_alias(tree, name, constant)
|
|
116
|
-
else
|
|
117
|
-
compile_module(tree, name, constant)
|
|
118
|
-
end
|
|
119
|
-
else
|
|
120
|
-
compile_object(tree, name, constant)
|
|
121
|
-
end
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
sig { params(tree: RBI::Tree, name: String, constant: Module).void }
|
|
125
|
-
def compile_alias(tree, name, constant)
|
|
126
|
-
return if symbol_ignored?(name)
|
|
127
|
-
|
|
128
|
-
target = name_of(constant)
|
|
129
|
-
# If target has no name, let's make it an anonymous class or module with `Class.new` or `Module.new`
|
|
130
|
-
target = "#{constant.class}.new" unless target
|
|
131
|
-
|
|
132
|
-
add_to_alias_namespace(name)
|
|
133
|
-
|
|
134
|
-
return if IGNORED_SYMBOLS.include?(name)
|
|
135
|
-
|
|
136
|
-
tree << RBI::Const.new(name, target)
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
sig { params(tree: RBI::Tree, name: String, value: BasicObject).void.checked(:never) }
|
|
140
|
-
def compile_object(tree, name, value)
|
|
141
|
-
return if symbol_ignored?(name)
|
|
142
|
-
|
|
143
|
-
klass = class_of(value)
|
|
144
|
-
|
|
145
|
-
klass_name = if klass == ObjectSpace::WeakMap
|
|
146
|
-
# WeakMap is an implicit generic with one type variable
|
|
147
|
-
"ObjectSpace::WeakMap[T.untyped]"
|
|
148
|
-
elsif T::Generic === klass
|
|
149
|
-
generic_name_of(klass)
|
|
150
|
-
else
|
|
151
|
-
name_of(klass)
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
comments = documentation_comments(name)
|
|
155
|
-
|
|
156
|
-
if klass_name == "T::Private::Types::TypeAlias"
|
|
157
|
-
type_alias = sanitize_signature_types(T.unsafe(value).aliased_type.to_s)
|
|
158
|
-
constant = RBI::Const.new(name, "T.type_alias { #{type_alias} }", comments: comments)
|
|
159
|
-
tree << constant
|
|
160
|
-
return
|
|
161
|
-
end
|
|
162
|
-
|
|
163
|
-
return if klass_name&.start_with?("T::Types::", "T::Private::")
|
|
164
|
-
|
|
165
|
-
type_name = klass_name || "T.untyped"
|
|
166
|
-
constant = RBI::Const.new(name, "T.let(T.unsafe(nil), #{type_name})", comments: comments)
|
|
167
|
-
|
|
168
|
-
tree << constant
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
sig { params(tree: RBI::Tree, name: String, constant: Module).void }
|
|
172
|
-
def compile_module(tree, name, constant)
|
|
173
|
-
return unless defined_in_gem?(constant, strict: false)
|
|
174
|
-
return if Tapioca::TypeVariableModule === constant
|
|
175
|
-
|
|
176
|
-
comments = documentation_comments(name)
|
|
177
|
-
scope =
|
|
178
|
-
if constant.is_a?(Class)
|
|
179
|
-
superclass = compile_superclass(constant)
|
|
180
|
-
RBI::Class.new(name, superclass_name: superclass, comments: comments)
|
|
181
|
-
else
|
|
182
|
-
RBI::Module.new(name, comments: comments)
|
|
183
|
-
end
|
|
184
|
-
|
|
185
|
-
compile_body(scope, name, constant)
|
|
186
|
-
|
|
187
|
-
return if symbol_ignored?(name) && scope.empty?
|
|
188
|
-
|
|
189
|
-
tree << scope
|
|
190
|
-
compile_subconstants(tree, name, constant)
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
sig { params(tree: RBI::Tree, name: String, constant: Module).void }
|
|
194
|
-
def compile_body(tree, name, constant)
|
|
195
|
-
# Compiling type variables must happen first to populate generic names
|
|
196
|
-
compile_type_variables(tree, constant)
|
|
197
|
-
compile_methods(tree, name, constant)
|
|
198
|
-
compile_module_helpers(tree, constant)
|
|
199
|
-
compile_mixins(tree, constant)
|
|
200
|
-
compile_props(tree, constant)
|
|
201
|
-
compile_enums(tree, constant)
|
|
202
|
-
compile_dynamic_mixins(tree, constant)
|
|
203
|
-
end
|
|
204
|
-
|
|
205
|
-
sig { params(tree: RBI::Tree, constant: Module).void }
|
|
206
|
-
def compile_dynamic_mixins(tree, constant)
|
|
207
|
-
return if constant.is_a?(Class)
|
|
208
|
-
|
|
209
|
-
mixin_compiler = DynamicMixinCompiler.new(constant)
|
|
210
|
-
mixin_compiler.compile_class_attributes(tree)
|
|
211
|
-
dynamic_extends, dynamic_includes = mixin_compiler.compile_mixes_in_class_methods(tree)
|
|
212
|
-
|
|
213
|
-
(dynamic_includes + dynamic_extends).each do |mod|
|
|
214
|
-
add_to_symbol_queue(name_of(mod))
|
|
215
|
-
end
|
|
216
|
-
end
|
|
217
|
-
|
|
218
|
-
sig { params(tree: RBI::Tree, constant: Module).void }
|
|
219
|
-
def compile_module_helpers(tree, constant)
|
|
220
|
-
abstract_type = T::Private::Abstract::Data.get(constant, :abstract_type) ||
|
|
221
|
-
T::Private::Abstract::Data.get(singleton_class_of(constant), :abstract_type)
|
|
222
|
-
|
|
223
|
-
tree << RBI::Helper.new(abstract_type.to_s) if abstract_type
|
|
224
|
-
tree << RBI::Helper.new("final") if T::Private::Final.final_module?(constant)
|
|
225
|
-
tree << RBI::Helper.new("sealed") if T::Private::Sealed.sealed_module?(constant)
|
|
226
|
-
end
|
|
227
|
-
|
|
228
|
-
sig { params(tree: RBI::Tree, constant: Module).void }
|
|
229
|
-
def compile_props(tree, constant)
|
|
230
|
-
return unless T::Props::ClassMethods === constant
|
|
231
|
-
|
|
232
|
-
constant.props.map do |name, prop|
|
|
233
|
-
type = prop.fetch(:type_object, "T.untyped").to_s.gsub(".returns(<VOID>)", ".void")
|
|
234
|
-
|
|
235
|
-
default = prop.key?(:default) ? "T.unsafe(nil)" : nil
|
|
236
|
-
tree << if prop.fetch(:immutable, false)
|
|
237
|
-
RBI::TStructConst.new(name.to_s, type, default: default)
|
|
238
|
-
else
|
|
239
|
-
RBI::TStructProp.new(name.to_s, type, default: default)
|
|
240
|
-
end
|
|
241
|
-
end
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
sig { params(tree: RBI::Tree, constant: Module).void }
|
|
245
|
-
def compile_enums(tree, constant)
|
|
246
|
-
return unless T::Enum > constant
|
|
247
|
-
|
|
248
|
-
enums = T.unsafe(constant).values.map do |enum_type|
|
|
249
|
-
enum_type.instance_variable_get(:@const_name).to_s
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
tree << RBI::TEnumBlock.new(enums)
|
|
253
|
-
end
|
|
254
|
-
|
|
255
|
-
sig { params(tree: RBI::Tree, name: String, constant: Module).void }
|
|
256
|
-
def compile_subconstants(tree, name, constant)
|
|
257
|
-
constants_of(constant).sort.uniq.map do |constant_name|
|
|
258
|
-
symbol = (name == "Object" ? "" : name) + "::#{constant_name}"
|
|
259
|
-
subconstant = constantize(symbol)
|
|
260
|
-
|
|
261
|
-
# Don't compile modules of Object because Object::Foo == Foo
|
|
262
|
-
# Don't compile modules of BasicObject because BasicObject::BasicObject == BasicObject
|
|
263
|
-
next if (Object == constant || BasicObject == constant) && Module === subconstant
|
|
264
|
-
next unless subconstant
|
|
265
|
-
|
|
266
|
-
compile(tree, symbol, subconstant)
|
|
267
|
-
end
|
|
268
|
-
end
|
|
269
|
-
|
|
270
|
-
sig { params(tree: RBI::Tree, constant: Module).void }
|
|
271
|
-
def compile_type_variables(tree, constant)
|
|
272
|
-
compile_type_variable_declarations(tree, constant)
|
|
273
|
-
|
|
274
|
-
sclass = RBI::SingletonClass.new
|
|
275
|
-
compile_type_variable_declarations(sclass, singleton_class_of(constant))
|
|
276
|
-
tree << sclass if sclass.nodes.length > 1
|
|
277
|
-
end
|
|
278
|
-
|
|
279
|
-
sig { params(tree: RBI::Tree, constant: Module).void }
|
|
280
|
-
def compile_type_variable_declarations(tree, constant)
|
|
281
|
-
# Try to find the type variables defined on this constant, bail if we can't
|
|
282
|
-
type_variables = GenericTypeRegistry.lookup_type_variables(constant)
|
|
283
|
-
return unless type_variables
|
|
284
|
-
|
|
285
|
-
# Map each type variable to its string representation.
|
|
286
|
-
#
|
|
287
|
-
# Each entry of `type_variables` maps a Module to a String,
|
|
288
|
-
# and the order they are inserted into the hash is the order they should be
|
|
289
|
-
# defined in the source code.
|
|
290
|
-
type_variable_declarations = type_variables.map do |type_variable|
|
|
291
|
-
type_variable_name = type_variable.name
|
|
292
|
-
next unless type_variable_name
|
|
293
|
-
|
|
294
|
-
tree << RBI::TypeMember.new(type_variable_name, type_variable.serialize)
|
|
295
|
-
end
|
|
296
|
-
|
|
297
|
-
return if type_variable_declarations.empty?
|
|
298
|
-
|
|
299
|
-
tree << RBI::Extend.new("T::Generic")
|
|
300
|
-
end
|
|
301
|
-
|
|
302
|
-
sig { params(constant: Class).returns(T.nilable(String)) }
|
|
303
|
-
def compile_superclass(constant)
|
|
304
|
-
superclass = T.let(nil, T.nilable(Class)) # rubocop:disable Lint/UselessAssignment
|
|
305
|
-
|
|
306
|
-
while (superclass = superclass_of(constant))
|
|
307
|
-
constant_name = name_of(constant)
|
|
308
|
-
constant = superclass
|
|
309
|
-
|
|
310
|
-
# Some types have "themselves" as their superclass
|
|
311
|
-
# which can happen via:
|
|
312
|
-
#
|
|
313
|
-
# class A < Numeric; end
|
|
314
|
-
# A = Class.new(A)
|
|
315
|
-
# A.superclass #=> A
|
|
316
|
-
#
|
|
317
|
-
# We compare names here to make sure we skip those
|
|
318
|
-
# superclass instances and walk up the chain.
|
|
319
|
-
#
|
|
320
|
-
# The name comparison is against the name of the constant
|
|
321
|
-
# resolved from the name of the superclass, since
|
|
322
|
-
# this is also possible:
|
|
323
|
-
#
|
|
324
|
-
# B = Class.new
|
|
325
|
-
# class A < B; end
|
|
326
|
-
# B = A
|
|
327
|
-
# A.superclass.name #=> "B"
|
|
328
|
-
# B #=> A
|
|
329
|
-
superclass_name = name_of(superclass)
|
|
330
|
-
next unless superclass_name
|
|
331
|
-
|
|
332
|
-
resolved_superclass = constantize(superclass_name)
|
|
333
|
-
next unless Module === resolved_superclass
|
|
334
|
-
next if name_of(resolved_superclass) == constant_name
|
|
335
|
-
|
|
336
|
-
# We found a suitable superclass
|
|
337
|
-
break
|
|
338
|
-
end
|
|
339
|
-
|
|
340
|
-
return if superclass == ::Object || superclass == ::Delegator
|
|
341
|
-
return if superclass.nil?
|
|
342
|
-
|
|
343
|
-
name = name_of(superclass)
|
|
344
|
-
return if name.nil? || name.empty?
|
|
345
|
-
|
|
346
|
-
add_to_symbol_queue(name)
|
|
347
|
-
|
|
348
|
-
"::#{name}"
|
|
349
|
-
end
|
|
350
|
-
|
|
351
|
-
sig { params(tree: RBI::Tree, constant: Module).void }
|
|
352
|
-
def compile_mixins(tree, constant)
|
|
353
|
-
singleton_class = singleton_class_of(constant)
|
|
354
|
-
|
|
355
|
-
interesting_ancestors = interesting_ancestors_of(constant)
|
|
356
|
-
interesting_singleton_class_ancestors = interesting_ancestors_of(singleton_class)
|
|
357
|
-
|
|
358
|
-
prepends = interesting_ancestors.take_while { |c| !are_equal?(constant, c) }
|
|
359
|
-
includes = interesting_ancestors.drop(prepends.size + 1)
|
|
360
|
-
extends = interesting_singleton_class_ancestors.reject do |mod|
|
|
361
|
-
Module != class_of(mod) || are_equal?(mod, singleton_class)
|
|
362
|
-
end
|
|
363
|
-
|
|
364
|
-
add_mixins(tree, prepends.reverse, Trackers::Mixin::Type::Prepend)
|
|
365
|
-
add_mixins(tree, includes.reverse, Trackers::Mixin::Type::Include)
|
|
366
|
-
add_mixins(tree, extends.reverse, Trackers::Mixin::Type::Extend)
|
|
367
|
-
end
|
|
368
|
-
|
|
369
|
-
sig do
|
|
370
|
-
params(
|
|
371
|
-
tree: RBI::Tree,
|
|
372
|
-
mods: T::Array[Module],
|
|
373
|
-
mixin_type: Trackers::Mixin::Type
|
|
374
|
-
).void
|
|
375
|
-
end
|
|
376
|
-
def add_mixins(tree, mods, mixin_type)
|
|
377
|
-
mods
|
|
378
|
-
.select do |mod|
|
|
379
|
-
name = name_of(mod)
|
|
380
|
-
|
|
381
|
-
name && !filtered_mixin?(name)
|
|
382
|
-
end
|
|
383
|
-
.map do |mod|
|
|
384
|
-
add_to_symbol_queue(name_of(mod))
|
|
385
|
-
|
|
386
|
-
qname = qualified_name_of(mod)
|
|
387
|
-
case mixin_type
|
|
388
|
-
# TODO: Sorbet currently does not handle prepend
|
|
389
|
-
# properly for method resolution, so we generate an
|
|
390
|
-
# include statement instead
|
|
391
|
-
when Trackers::Mixin::Type::Include, Trackers::Mixin::Type::Prepend
|
|
392
|
-
tree << RBI::Include.new(T.must(qname))
|
|
393
|
-
when Trackers::Mixin::Type::Extend
|
|
394
|
-
tree << RBI::Extend.new(T.must(qname))
|
|
395
|
-
end
|
|
396
|
-
end
|
|
397
|
-
end
|
|
398
|
-
|
|
399
|
-
sig { params(tree: RBI::Tree, name: String, constant: Module).void }
|
|
400
|
-
def compile_methods(tree, name, constant)
|
|
401
|
-
compile_method(
|
|
402
|
-
tree,
|
|
403
|
-
name,
|
|
404
|
-
constant,
|
|
405
|
-
initialize_method_for(constant)
|
|
406
|
-
)
|
|
407
|
-
|
|
408
|
-
compile_directly_owned_methods(tree, name, constant)
|
|
409
|
-
compile_directly_owned_methods(tree, name, singleton_class_of(constant))
|
|
410
|
-
end
|
|
411
|
-
|
|
412
|
-
sig do
|
|
413
|
-
params(
|
|
414
|
-
tree: RBI::Tree,
|
|
415
|
-
module_name: String,
|
|
416
|
-
mod: Module,
|
|
417
|
-
for_visibility: T::Array[Symbol]
|
|
418
|
-
).void
|
|
419
|
-
end
|
|
420
|
-
def compile_directly_owned_methods(tree, module_name, mod, for_visibility = [:public, :protected, :private])
|
|
421
|
-
method_names_by_visibility(mod)
|
|
422
|
-
.delete_if { |visibility, _method_list| !for_visibility.include?(visibility) }
|
|
423
|
-
.each do |visibility, method_list|
|
|
424
|
-
method_list.sort!.map do |name|
|
|
425
|
-
next if name == :initialize
|
|
426
|
-
vis = case visibility
|
|
427
|
-
when :protected
|
|
428
|
-
RBI::Protected.new
|
|
429
|
-
when :private
|
|
430
|
-
RBI::Private.new
|
|
431
|
-
else
|
|
432
|
-
RBI::Public.new
|
|
433
|
-
end
|
|
434
|
-
compile_method(tree, module_name, mod, mod.instance_method(name), vis)
|
|
435
|
-
end
|
|
436
|
-
end
|
|
437
|
-
end
|
|
438
|
-
|
|
439
|
-
sig { params(mod: Module).returns(T::Hash[Symbol, T::Array[Symbol]]) }
|
|
440
|
-
def method_names_by_visibility(mod)
|
|
441
|
-
{
|
|
442
|
-
public: public_instance_methods_of(mod),
|
|
443
|
-
protected: protected_instance_methods_of(mod),
|
|
444
|
-
private: private_instance_methods_of(mod),
|
|
445
|
-
}
|
|
446
|
-
end
|
|
447
|
-
|
|
448
|
-
sig { params(constant: Module, method_name: String).returns(T::Boolean) }
|
|
449
|
-
def struct_method?(constant, method_name)
|
|
450
|
-
return false unless T::Props::ClassMethods === constant
|
|
451
|
-
|
|
452
|
-
constant
|
|
453
|
-
.props
|
|
454
|
-
.keys
|
|
455
|
-
.include?(method_name.gsub(/=$/, "").to_sym)
|
|
456
|
-
end
|
|
457
|
-
|
|
458
|
-
sig do
|
|
459
|
-
params(
|
|
460
|
-
tree: RBI::Tree,
|
|
461
|
-
symbol_name: String,
|
|
462
|
-
constant: Module,
|
|
463
|
-
method: T.nilable(UnboundMethod),
|
|
464
|
-
visibility: RBI::Visibility
|
|
465
|
-
).void
|
|
466
|
-
end
|
|
467
|
-
def compile_method(tree, symbol_name, constant, method, visibility = RBI::Public.new)
|
|
468
|
-
return unless method
|
|
469
|
-
return unless method.owner == constant
|
|
470
|
-
return if symbol_ignored?(symbol_name) && !method_in_gem?(method)
|
|
471
|
-
|
|
472
|
-
signature = signature_of(method)
|
|
473
|
-
method = T.let(signature.method, UnboundMethod) if signature
|
|
474
|
-
|
|
475
|
-
method_name = method.name.to_s
|
|
476
|
-
return unless valid_method_name?(method_name)
|
|
477
|
-
return if struct_method?(constant, method_name)
|
|
478
|
-
return if method_name.start_with?("__t_props_generated_")
|
|
479
|
-
|
|
480
|
-
parameters = T.let(method.parameters, T::Array[[Symbol, T.nilable(Symbol)]])
|
|
481
|
-
|
|
482
|
-
sanitized_parameters = parameters.each_with_index.map do |(type, name), index|
|
|
483
|
-
fallback_arg_name = "_arg#{index}"
|
|
484
|
-
|
|
485
|
-
name = if name
|
|
486
|
-
name.to_s
|
|
487
|
-
else
|
|
488
|
-
# For attr_writer methods, Sorbet signatures have the name
|
|
489
|
-
# of the method (without the trailing = sign) as the name of
|
|
490
|
-
# the only parameter. So, if the parameter does not have a name
|
|
491
|
-
# then the replacement name should be the name of the method
|
|
492
|
-
# (minus trailing =) if and only if there is a signature for the
|
|
493
|
-
# method and the parameter is required and there is a single
|
|
494
|
-
# parameter and the signature also defines a single parameter and
|
|
495
|
-
# the name of the method ends with a = character.
|
|
496
|
-
writer_method_with_sig = (
|
|
497
|
-
signature && type == :req &&
|
|
498
|
-
parameters.size == 1 &&
|
|
499
|
-
signature.arg_types.size == 1 &&
|
|
500
|
-
method_name[-1] == "="
|
|
501
|
-
)
|
|
502
|
-
|
|
503
|
-
if writer_method_with_sig
|
|
504
|
-
method_name.delete_suffix("=")
|
|
505
|
-
else
|
|
506
|
-
fallback_arg_name
|
|
507
|
-
end
|
|
508
|
-
end
|
|
509
|
-
|
|
510
|
-
# Sanitize param names
|
|
511
|
-
name = fallback_arg_name unless valid_parameter_name?(name)
|
|
512
|
-
|
|
513
|
-
[type, name]
|
|
514
|
-
end
|
|
515
|
-
|
|
516
|
-
separator = constant.singleton_class? ? "." : "#"
|
|
517
|
-
comments = documentation_comments("#{symbol_name}#{separator}#{method_name}")
|
|
518
|
-
rbi_method = RBI::Method.new(
|
|
519
|
-
method_name,
|
|
520
|
-
is_singleton: constant.singleton_class?,
|
|
521
|
-
visibility: visibility,
|
|
522
|
-
comments: comments
|
|
523
|
-
)
|
|
524
|
-
|
|
525
|
-
rbi_method.sigs << compile_signature(signature, sanitized_parameters) if signature
|
|
526
|
-
|
|
527
|
-
sanitized_parameters.each do |type, name|
|
|
528
|
-
case type
|
|
529
|
-
when :req
|
|
530
|
-
rbi_method << RBI::Param.new(name)
|
|
531
|
-
when :opt
|
|
532
|
-
rbi_method << RBI::OptParam.new(name, "T.unsafe(nil)")
|
|
533
|
-
when :rest
|
|
534
|
-
rbi_method << RBI::RestParam.new(name)
|
|
535
|
-
when :keyreq
|
|
536
|
-
rbi_method << RBI::KwParam.new(name)
|
|
537
|
-
when :key
|
|
538
|
-
rbi_method << RBI::KwOptParam.new(name, "T.unsafe(nil)")
|
|
539
|
-
when :keyrest
|
|
540
|
-
rbi_method << RBI::KwRestParam.new(name)
|
|
541
|
-
when :block
|
|
542
|
-
rbi_method << RBI::BlockParam.new(name)
|
|
543
|
-
end
|
|
544
|
-
end
|
|
545
|
-
|
|
546
|
-
tree << rbi_method
|
|
547
|
-
end
|
|
548
|
-
|
|
549
|
-
TYPE_PARAMETER_MATCHER = /T\.type_parameter\(:?([[:word:]]+)\)/
|
|
550
|
-
|
|
551
|
-
sig { params(signature: T.untyped, parameters: T::Array[[Symbol, String]]).returns(RBI::Sig) }
|
|
552
|
-
def compile_signature(signature, parameters)
|
|
553
|
-
parameter_types = T.let(signature.arg_types.to_h, T::Hash[Symbol, T::Types::Base])
|
|
554
|
-
parameter_types.merge!(signature.kwarg_types)
|
|
555
|
-
parameter_types[signature.rest_name] = signature.rest_type if signature.has_rest
|
|
556
|
-
parameter_types[signature.keyrest_name] = signature.keyrest_type if signature.has_keyrest
|
|
557
|
-
parameter_types[signature.block_name] = signature.block_type if signature.block_name
|
|
558
|
-
|
|
559
|
-
sig = RBI::Sig.new
|
|
560
|
-
|
|
561
|
-
parameters.each do |_, name|
|
|
562
|
-
type = sanitize_signature_types(parameter_types[name.to_sym].to_s)
|
|
563
|
-
add_to_symbol_queue(type)
|
|
564
|
-
sig << RBI::SigParam.new(name, type)
|
|
565
|
-
end
|
|
566
|
-
|
|
567
|
-
return_type = name_of_type(signature.return_type)
|
|
568
|
-
sig.return_type = sanitize_signature_types(return_type)
|
|
569
|
-
add_to_symbol_queue(sig.return_type)
|
|
570
|
-
|
|
571
|
-
parameter_types.values.join(", ").scan(TYPE_PARAMETER_MATCHER).flatten.uniq.each do |k, _|
|
|
572
|
-
sig.type_params << k
|
|
573
|
-
end
|
|
574
|
-
|
|
575
|
-
case signature.mode
|
|
576
|
-
when "abstract"
|
|
577
|
-
sig.is_abstract = true
|
|
578
|
-
when "override"
|
|
579
|
-
sig.is_override = true
|
|
580
|
-
when "overridable_override"
|
|
581
|
-
sig.is_overridable = true
|
|
582
|
-
sig.is_override = true
|
|
583
|
-
when "overridable"
|
|
584
|
-
sig.is_overridable = true
|
|
585
|
-
end
|
|
586
|
-
|
|
587
|
-
sig
|
|
588
|
-
end
|
|
589
|
-
|
|
590
|
-
sig { params(sig_string: String).returns(String) }
|
|
591
|
-
def sanitize_signature_types(sig_string)
|
|
592
|
-
sig_string
|
|
593
|
-
.gsub(".returns(<VOID>)", ".void")
|
|
594
|
-
.gsub("<VOID>", "void")
|
|
595
|
-
.gsub("<NOT-TYPED>", "T.untyped")
|
|
596
|
-
.gsub(".params()", "")
|
|
597
|
-
end
|
|
598
|
-
|
|
599
|
-
sig { params(symbol_name: String).returns(T::Boolean) }
|
|
600
|
-
def symbol_ignored?(symbol_name)
|
|
601
|
-
SymbolLoader.ignore_symbol?(symbol_name)
|
|
602
|
-
end
|
|
603
|
-
|
|
604
|
-
sig { params(mixin_name: String).returns(T::Boolean) }
|
|
605
|
-
def filtered_mixin?(mixin_name)
|
|
606
|
-
# filter T:: namespace mixins that aren't T::Props
|
|
607
|
-
# T::Props and subconstants have semantic value
|
|
608
|
-
mixin_name.start_with?("T::") && !mixin_name.start_with?("T::Props")
|
|
609
|
-
end
|
|
610
|
-
|
|
611
|
-
SPECIAL_METHOD_NAMES = T.let([
|
|
612
|
-
"!", "~", "+@", "**", "-@", "*", "/", "%", "+", "-", "<<", ">>", "&", "|", "^",
|
|
613
|
-
"<", "<=", "=>", ">", ">=", "==", "===", "!=", "=~", "!~", "<=>", "[]", "[]=", "`"
|
|
614
|
-
], T::Array[String])
|
|
615
|
-
|
|
616
|
-
sig { params(name: String).returns(T::Boolean) }
|
|
617
|
-
def valid_method_name?(name)
|
|
618
|
-
return true if SPECIAL_METHOD_NAMES.include?(name)
|
|
619
|
-
!!name.match(/^[[:word:]]+[?!=]?$/)
|
|
620
|
-
end
|
|
621
|
-
|
|
622
|
-
sig { params(name: String).returns(T::Boolean) }
|
|
623
|
-
def valid_parameter_name?(name)
|
|
624
|
-
name.match?(/^[[[:alnum:]]_]+$/)
|
|
625
|
-
end
|
|
626
|
-
|
|
627
|
-
sig { params(method: UnboundMethod).returns(T::Boolean) }
|
|
628
|
-
def method_in_gem?(method)
|
|
629
|
-
source_location = method.source_location&.first
|
|
630
|
-
return false if source_location.nil?
|
|
631
|
-
|
|
632
|
-
gem.contains_path?(source_location)
|
|
633
|
-
end
|
|
634
|
-
|
|
635
|
-
sig { params(constant: Module, strict: T::Boolean).returns(T::Boolean) }
|
|
636
|
-
def defined_in_gem?(constant, strict: true)
|
|
637
|
-
files = Set.new(get_file_candidates(constant))
|
|
638
|
-
.merge(Tapioca::Trackers::ConstantDefinition.files_for(constant))
|
|
639
|
-
|
|
640
|
-
return !strict if files.empty?
|
|
641
|
-
|
|
642
|
-
files.any? do |file|
|
|
643
|
-
gem.contains_path?(file)
|
|
644
|
-
end
|
|
645
|
-
end
|
|
646
|
-
|
|
647
|
-
sig do
|
|
648
|
-
params(
|
|
649
|
-
mod: Module,
|
|
650
|
-
mixin_type: Trackers::Mixin::Type,
|
|
651
|
-
mixin_locations: T::Hash[Trackers::Mixin::Type, T::Hash[Module, T::Array[String]]]
|
|
652
|
-
).returns(T::Boolean)
|
|
653
|
-
end
|
|
654
|
-
def mixed_in_by_gem?(mod, mixin_type, mixin_locations)
|
|
655
|
-
locations = mixin_locations.dig(mixin_type, mod)
|
|
656
|
-
return true unless locations
|
|
657
|
-
locations.any? { |location| gem.contains_path?(location) }
|
|
658
|
-
end
|
|
659
|
-
|
|
660
|
-
sig { params(constant: Module).returns(T::Array[String]) }
|
|
661
|
-
def get_file_candidates(constant)
|
|
662
|
-
wrapped_module = Pry::WrappedModule.new(constant)
|
|
663
|
-
|
|
664
|
-
wrapped_module.candidates.map(&:file).to_a.compact
|
|
665
|
-
rescue ArgumentError, NameError
|
|
666
|
-
[]
|
|
667
|
-
end
|
|
668
|
-
|
|
669
|
-
sig { params(name: String).void }
|
|
670
|
-
def add_to_alias_namespace(name)
|
|
671
|
-
@alias_namespace.add("#{name}::")
|
|
672
|
-
end
|
|
673
|
-
|
|
674
|
-
sig { params(name: String).returns(T::Boolean) }
|
|
675
|
-
def alias_namespaced?(name)
|
|
676
|
-
@alias_namespace.any? do |namespace|
|
|
677
|
-
name.start_with?(namespace)
|
|
678
|
-
end
|
|
679
|
-
end
|
|
680
|
-
|
|
681
|
-
sig { params(name: String).void }
|
|
682
|
-
def mark_seen(name)
|
|
683
|
-
@seen.add(name)
|
|
684
|
-
end
|
|
685
|
-
|
|
686
|
-
sig { params(name: String).returns(T::Boolean) }
|
|
687
|
-
def seen?(name)
|
|
688
|
-
@seen.include?(name)
|
|
689
|
-
end
|
|
690
|
-
|
|
691
|
-
sig { params(constant: Module).returns(T.nilable(UnboundMethod)) }
|
|
692
|
-
def initialize_method_for(constant)
|
|
693
|
-
constant.instance_method(:initialize)
|
|
694
|
-
rescue
|
|
695
|
-
nil
|
|
696
|
-
end
|
|
697
|
-
|
|
698
|
-
sig { params(constant: Module).returns(T::Array[Module]) }
|
|
699
|
-
def interesting_ancestors_of(constant)
|
|
700
|
-
inherited_ancestors_ids = Set.new(
|
|
701
|
-
inherited_ancestors_of(constant).map { |mod| object_id_of(mod) }
|
|
702
|
-
)
|
|
703
|
-
# TODO: There is actually a bug here where this will drop modules that
|
|
704
|
-
# may be included twice. For example:
|
|
705
|
-
#
|
|
706
|
-
# ```ruby
|
|
707
|
-
# class Foo
|
|
708
|
-
# prepend Kernel
|
|
709
|
-
# end
|
|
710
|
-
# ````
|
|
711
|
-
# would give:
|
|
712
|
-
# ```ruby
|
|
713
|
-
# Foo.ancestors #=> [Kernel, Foo, Object, Kernel, BasicObject]
|
|
714
|
-
# ````
|
|
715
|
-
# but since we drop `Kernel` whenever we match it, we would miss
|
|
716
|
-
# the `prepend Kernel` in the output.
|
|
717
|
-
#
|
|
718
|
-
# Instead, we should only drop the tail matches of the ancestors and
|
|
719
|
-
# inherited ancestors, past the location of the constant itself.
|
|
720
|
-
constant.ancestors.reject do |mod|
|
|
721
|
-
inherited_ancestors_ids.include?(object_id_of(mod))
|
|
722
|
-
end
|
|
723
|
-
end
|
|
724
|
-
|
|
725
|
-
sig { params(constant: Module).returns(T.nilable(String)) }
|
|
726
|
-
def name_of(constant)
|
|
727
|
-
name = name_of_proxy_target(constant, super(class_of(constant)))
|
|
728
|
-
return name if name
|
|
729
|
-
name = super(constant)
|
|
730
|
-
return if name.nil?
|
|
731
|
-
return unless are_equal?(constant, constantize(name, inherit: true))
|
|
732
|
-
name = "Struct" if name =~ /^(::)?Struct::[^:]+$/
|
|
733
|
-
name
|
|
734
|
-
end
|
|
735
|
-
|
|
736
|
-
sig { params(constant: T.all(Module, T::Generic)).returns(String) }
|
|
737
|
-
def generic_name_of(constant)
|
|
738
|
-
type_name = T.must(constant.name)
|
|
739
|
-
return type_name if type_name =~ /\[.*\]$/
|
|
740
|
-
|
|
741
|
-
type_variables = Tapioca::GenericTypeRegistry.lookup_type_variables(constant)
|
|
742
|
-
return type_name unless type_variables
|
|
743
|
-
|
|
744
|
-
type_variable_names = type_variables.map { "T.untyped" }.join(", ")
|
|
745
|
-
|
|
746
|
-
"#{type_name}[#{type_variable_names}]"
|
|
747
|
-
end
|
|
748
|
-
|
|
749
|
-
sig { params(constant: Module, class_name: T.nilable(String)).returns(T.nilable(String)) }
|
|
750
|
-
def name_of_proxy_target(constant, class_name)
|
|
751
|
-
return unless class_name == "ActiveSupport::Deprecation::DeprecatedConstantProxy"
|
|
752
|
-
# We are dealing with a ActiveSupport::Deprecation::DeprecatedConstantProxy
|
|
753
|
-
# so try to get the name of the target class
|
|
754
|
-
begin
|
|
755
|
-
target = constant.__send__(:target)
|
|
756
|
-
rescue NoMethodError
|
|
757
|
-
return
|
|
758
|
-
end
|
|
759
|
-
|
|
760
|
-
name_of(target)
|
|
761
|
-
end
|
|
762
|
-
|
|
763
|
-
sig { params(name: String).returns(T::Array[RBI::Comment]) }
|
|
764
|
-
def documentation_comments(name)
|
|
765
|
-
return [] unless @include_doc
|
|
766
|
-
|
|
767
|
-
yard_docs = YARD::Registry.at(name)
|
|
768
|
-
return [] unless yard_docs
|
|
769
|
-
|
|
770
|
-
docstring = yard_docs.docstring
|
|
771
|
-
return [] if /(copyright|license)/i.match?(docstring)
|
|
772
|
-
|
|
773
|
-
docstring.lines
|
|
774
|
-
.reject { |line| IGNORED_COMMENTS.any? { |comment| line.include?(comment) } }
|
|
775
|
-
.map! { |line| RBI::Comment.new(line) }
|
|
776
|
-
end
|
|
777
|
-
end
|
|
778
|
-
end
|
|
779
|
-
end
|
|
780
|
-
end
|