tapioca 0.4.20 → 0.4.24

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ class String
5
+ extend T::Sig
6
+
7
+ sig { returns(String) }
8
+ def underscore
9
+ return self unless /[A-Z-]|::/.match?(self)
10
+
11
+ word = to_s.gsub("::", "/")
12
+ word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
13
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
14
+ word.tr!("-", "_")
15
+ word.downcase!
16
+ word
17
+ end
18
+ end
@@ -108,8 +108,14 @@ module Tapioca
108
108
 
109
109
  sig { returns(T::Array[Pathname]) }
110
110
  def files
111
- @spec.full_require_paths.flat_map do |path|
112
- Pathname.glob((Pathname.new(path) / "**/*.rb").to_s)
111
+ if default_gem?
112
+ @spec.files.map do |file|
113
+ ruby_lib_dir.join(file)
114
+ end
115
+ else
116
+ @spec.full_require_paths.flat_map do |path|
117
+ Pathname.glob((Pathname.new(path) / "**/*.rb").to_s)
118
+ end
113
119
  end
114
120
  end
115
121
 
@@ -125,11 +131,25 @@ module Tapioca
125
131
 
126
132
  sig { params(path: String).returns(T::Boolean) }
127
133
  def contains_path?(path)
128
- to_realpath(path).start_with?(full_gem_path) || has_parent_gemspec?(path)
134
+ if default_gem?
135
+ files.any? { |file| file.to_s == to_realpath(path) }
136
+ else
137
+ to_realpath(path).start_with?(full_gem_path) || has_parent_gemspec?(path)
138
+ end
129
139
  end
130
140
 
131
141
  private
132
142
 
143
+ sig { returns(T::Boolean) }
144
+ def default_gem?
145
+ @spec.respond_to?(:default_gem?) && @spec.default_gem?
146
+ end
147
+
148
+ sig { returns(Pathname) }
149
+ def ruby_lib_dir
150
+ Pathname.new(RbConfig::CONFIG["rubylibdir"])
151
+ end
152
+
133
153
  sig { returns(String) }
134
154
  def version_string
135
155
  version = @spec.version.to_s
@@ -176,7 +196,7 @@ module Tapioca
176
196
 
177
197
  sig { returns(T::Boolean) }
178
198
  def gem_in_bundle_path?
179
- full_gem_path.start_with?(Bundler.bundle_path.to_s)
199
+ full_gem_path.start_with?(Bundler.bundle_path.to_s, Bundler.app_cache.to_s)
180
200
  end
181
201
  end
182
202
  end
@@ -3,6 +3,7 @@
3
3
 
4
4
  require 'pathname'
5
5
  require 'thor'
6
+ require "tapioca/core_ext/string"
6
7
 
7
8
  module Tapioca
8
9
  class Generator < ::Thor::Shell::Color
@@ -145,8 +146,6 @@ module Tapioca
145
146
  }
146
147
  )
147
148
 
148
- constant_lookup = {}
149
-
150
149
  compiler.run do |constant, contents|
151
150
  constant_name = Module.instance_method(:name).bind(constant).call
152
151
 
@@ -159,13 +158,12 @@ module Tapioca
159
158
 
160
159
  if filename
161
160
  rbi_files_to_purge.delete(filename)
162
- constant_lookup[filename.relative_path_from(outpath)] = constant_name
163
161
  end
164
162
  end
165
163
  say("")
166
164
 
167
165
  if should_verify
168
- perform_dsl_verification(outpath, constant_lookup)
166
+ perform_dsl_verification(outpath)
169
167
  else
170
168
  purge_stale_dsl_rbi_files(rbi_files_to_purge)
171
169
 
@@ -288,7 +286,7 @@ module Tapioca
288
286
  def constantize(constant_names)
289
287
  constant_map = constant_names.map do |name|
290
288
  begin
291
- [name, name.constantize]
289
+ [name, Object.const_get(name)]
292
290
  rescue NameError
293
291
  [name, nil]
294
292
  end
@@ -596,17 +594,16 @@ module Tapioca
596
594
  end.sort
597
595
  end
598
596
 
599
- sig { params(dir: Pathname, constant_lookup: T::Hash[String, String]).void }
600
- def perform_dsl_verification(dir, constant_lookup)
597
+ sig { params(dir: Pathname).void }
598
+ def perform_dsl_verification(dir)
601
599
  diff = verify_dsl_rbi(tmp_dir: dir)
602
600
 
603
601
  if diff.empty?
604
602
  say("Nothing to do, all RBIs are up-to-date.")
605
603
  else
606
- constants = T.unsafe(constant_lookup).values_at(*diff.keys).join(" ")
607
-
608
- say("RBI files are out-of-date, please run:")
609
- say(" `#{Config::DEFAULT_COMMAND} dsl #{constants}`")
604
+ say("RBI files are out-of-date. In your development environment, please run:", :green)
605
+ say(" `#{Config::DEFAULT_COMMAND} dsl`", [:green, :bold])
606
+ say("Once it is complete, be sure to commit and push any changes", :green)
610
607
 
611
608
  say("")
612
609
 
@@ -100,7 +100,7 @@ module Tapioca
100
100
  # the generic class `Foo[Bar]` is still a `Foo`. That is:
101
101
  # `Foo[Bar].new.is_a?(Foo)` should be true, which isn't the case
102
102
  # if we just clone the class. But subclassing works just fine.
103
- create_sealed_safe_subclass(constant)
103
+ create_safe_subclass(constant)
104
104
  else
105
105
  # This can only be a module and it is fine to just clone modules
106
106
  # since they can't have instances and will not have `is_a?` relationships.
@@ -151,28 +151,31 @@ module Tapioca
151
151
  end
152
152
 
153
153
  sig { params(constant: Class).returns(Class) }
154
- def create_sealed_safe_subclass(constant)
155
- # If the constant is not sealed let's just bail early.
156
- # We just return a subclass of the constant.
157
- return Class.new(constant) unless T::Private::Sealed.sealed_module?(constant)
158
-
159
- # Since sealed classes can normally not be subclassed, we need to trick
160
- # sealed classes into thinking that the generic type we are
161
- # creating by subclassing is actually safe for sealed types.
162
- #
163
- # Get the filename the sealed class was declared in
164
- decl_file = constant.instance_variable_get(:@sorbet_sealed_module_decl_file)
154
+ def create_safe_subclass(constant)
155
+ # Lookup the "inherited" class method
156
+ inherited_method = constant.method(:inherited)
157
+ # and the module that defines it
158
+ owner = inherited_method.owner
159
+
160
+ # If no one has overriden the inherited method yet, just subclass
161
+ return Class.new(constant) if Class == owner
162
+
165
163
  begin
166
- # Clear the current declaration filename on the class
167
- constant.remove_instance_variable(:@sorbet_sealed_module_decl_file)
168
- # Make this file be the declaration filename so that Sorbet runtime
169
- # does not shout at us for an invalid subclassing.
170
- T.cast(constant, T::Helpers).sealed!
164
+ # Otherwise, some inherited method could be preventing us
165
+ # from creating subclasses, so let's override it and rescue
166
+ owner.send(:define_method, :inherited) do |s|
167
+ begin
168
+ inherited_method.call(s)
169
+ rescue
170
+ # Ignoring errors
171
+ end
172
+ end
173
+
171
174
  # return a subclass
172
175
  Class.new(constant)
173
176
  ensure
174
- # Reinstate the original declaration filename
175
- constant.instance_variable_set(:@sorbet_sealed_module_decl_file, decl_file)
177
+ # Reinstate the original inherited method back.
178
+ owner.send(:define_method, :inherited, inherited_method)
176
179
  end
177
180
  end
178
181
 
@@ -6,12 +6,20 @@ require "tapioca/loader"
6
6
  require "tapioca/constant_locator"
7
7
  require "tapioca/generic_type_registry"
8
8
  require "tapioca/sorbet_ext/generic_name_patch"
9
+ require "tapioca/sorbet_ext/fixed_hash_patch"
9
10
  require "tapioca/config"
10
11
  require "tapioca/config_builder"
11
12
  require "tapioca/generator"
12
13
  require "tapioca/cli"
13
14
  require "tapioca/cli/main"
14
15
  require "tapioca/gemfile"
16
+ require "tapioca/rbi/model"
17
+ require "tapioca/rbi/visitor"
18
+ require "tapioca/rbi/rewriters/nest_singleton_methods"
19
+ require "tapioca/rbi/rewriters/nest_non_public_methods"
20
+ require "tapioca/rbi/rewriters/group_nodes"
21
+ require "tapioca/rbi/rewriters/sort_nodes"
22
+ require "tapioca/rbi/printer"
15
23
  require "tapioca/compilers/sorbet"
16
24
  require "tapioca/compilers/requires_compiler"
17
25
  require "tapioca/compilers/symbol_table_compiler"
@@ -0,0 +1,405 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module Tapioca
5
+ module RBI
6
+ class Node
7
+ extend T::Sig
8
+ extend T::Helpers
9
+
10
+ abstract!
11
+
12
+ sig { returns(T.nilable(Tree)) }
13
+ attr_accessor :parent_tree
14
+
15
+ sig { void }
16
+ def initialize
17
+ @parent_tree = nil
18
+ end
19
+
20
+ sig { void }
21
+ def detach
22
+ tree = parent_tree
23
+ return unless tree
24
+ tree.nodes.delete(self)
25
+ self.parent_tree = nil
26
+ end
27
+ end
28
+
29
+ class Tree < Node
30
+ extend T::Sig
31
+
32
+ sig { returns(T::Array[Node]) }
33
+ attr_reader :nodes
34
+
35
+ sig { void }
36
+ def initialize
37
+ super()
38
+ @nodes = T.let([], T::Array[Node])
39
+ end
40
+
41
+ sig { params(node: Node).void }
42
+ def <<(node)
43
+ node.parent_tree = self
44
+ @nodes << node
45
+ end
46
+
47
+ sig { returns(T::Boolean) }
48
+ def empty?
49
+ nodes.empty?
50
+ end
51
+ end
52
+
53
+ # Scopes
54
+
55
+ class Scope < Tree
56
+ extend T::Helpers
57
+
58
+ abstract!
59
+ end
60
+
61
+ class Module < Scope
62
+ extend T::Sig
63
+
64
+ sig { returns(String) }
65
+ attr_accessor :name
66
+
67
+ sig { params(name: String).void }
68
+ def initialize(name)
69
+ super()
70
+ @name = name
71
+ end
72
+ end
73
+
74
+ class Class < Scope
75
+ extend T::Sig
76
+
77
+ sig { returns(String) }
78
+ attr_accessor :name
79
+
80
+ sig { returns(T.nilable(String)) }
81
+ attr_accessor :superclass_name
82
+
83
+ sig { params(name: String, superclass_name: T.nilable(String)).void }
84
+ def initialize(name, superclass_name: nil)
85
+ super()
86
+ @name = name
87
+ @superclass_name = superclass_name
88
+ end
89
+ end
90
+
91
+ class SingletonClass < Scope
92
+ extend T::Sig
93
+
94
+ sig { void }
95
+ def initialize
96
+ super()
97
+ end
98
+ end
99
+
100
+ # Consts
101
+
102
+ class Const < Node
103
+ extend T::Sig
104
+
105
+ sig { returns(String) }
106
+ attr_reader :name, :value
107
+
108
+ sig { params(name: String, value: String).void }
109
+ def initialize(name, value)
110
+ super()
111
+ @name = name
112
+ @value = value
113
+ end
114
+ end
115
+
116
+ # Methods and args
117
+
118
+ class Method < Node
119
+ extend T::Sig
120
+
121
+ sig { returns(String) }
122
+ attr_accessor :name
123
+
124
+ sig { returns(T::Array[Param]) }
125
+ attr_reader :params
126
+
127
+ sig { returns(T::Boolean) }
128
+ attr_accessor :is_singleton
129
+
130
+ sig { returns(Visibility) }
131
+ attr_accessor :visibility
132
+
133
+ sig { returns(T::Array[Sig]) }
134
+ attr_accessor :sigs
135
+
136
+ sig do
137
+ params(
138
+ name: String,
139
+ params: T::Array[Param],
140
+ is_singleton: T::Boolean,
141
+ visibility: Visibility,
142
+ sigs: T::Array[Sig]
143
+ ).void
144
+ end
145
+ def initialize(name, params: [], is_singleton: false, visibility: Visibility::Public, sigs: [])
146
+ super()
147
+ @name = name
148
+ @params = params
149
+ @is_singleton = is_singleton
150
+ @visibility = visibility
151
+ @sigs = sigs
152
+ end
153
+
154
+ sig { params(param: Param).void }
155
+ def <<(param)
156
+ @params << param
157
+ end
158
+ end
159
+
160
+ class Param < Node
161
+ extend T::Sig
162
+
163
+ sig { returns(String) }
164
+ attr_reader :name
165
+
166
+ sig { params(name: String).void }
167
+ def initialize(name)
168
+ super()
169
+ @name = name
170
+ end
171
+ end
172
+
173
+ class OptParam < Param
174
+ extend T::Sig
175
+
176
+ sig { returns(String) }
177
+ attr_reader :value
178
+
179
+ sig { params(name: String, value: String).void }
180
+ def initialize(name, value)
181
+ super(name)
182
+ @value = value
183
+ end
184
+ end
185
+
186
+ class RestParam < Param; end
187
+ class KwParam < Param; end
188
+ class KwOptParam < OptParam; end
189
+ class KwRestParam < Param; end
190
+ class BlockParam < Param; end
191
+
192
+ # Mixins
193
+
194
+ class Mixin < Node
195
+ extend T::Sig
196
+ extend T::Helpers
197
+
198
+ abstract!
199
+
200
+ sig { returns(String) }
201
+ attr_reader :name
202
+
203
+ sig { params(name: String).void }
204
+ def initialize(name)
205
+ super()
206
+ @name = name
207
+ end
208
+ end
209
+
210
+ class Include < Mixin; end
211
+ class Extend < Mixin; end
212
+
213
+ # Visibility
214
+
215
+ class Visibility < Node
216
+ extend T::Sig
217
+ extend T::Helpers
218
+
219
+ abstract!
220
+
221
+ sig { returns(Symbol) }
222
+ attr_reader :visibility
223
+
224
+ sig { params(visibility: Symbol).void }
225
+ def initialize(visibility)
226
+ super()
227
+ @visibility = visibility
228
+ end
229
+
230
+ sig { returns(T::Boolean) }
231
+ def public?
232
+ visibility == :public
233
+ end
234
+
235
+ Public = T.let(Visibility.new(:public), Visibility)
236
+ Protected = T.let(Visibility.new(:protected), Visibility)
237
+ Private = T.let(Visibility.new(:private), Visibility)
238
+ end
239
+
240
+ # Sorbet's sigs
241
+
242
+ class Sig < Node
243
+ extend T::Sig
244
+
245
+ sig { returns(T::Array[SigParam]) }
246
+ attr_reader :params
247
+
248
+ sig { returns(T.nilable(String)) }
249
+ attr_accessor :return_type
250
+
251
+ sig { returns(T::Boolean) }
252
+ attr_accessor :is_abstract, :is_override, :is_overridable
253
+
254
+ sig { returns(T::Array[String]) }
255
+ attr_reader :type_params
256
+
257
+ sig do
258
+ params(
259
+ params: T::Array[SigParam],
260
+ return_type: T.nilable(String),
261
+ is_abstract: T::Boolean,
262
+ is_override: T::Boolean,
263
+ is_overridable: T::Boolean,
264
+ type_params: T::Array[String]
265
+ ).void
266
+ end
267
+ def initialize(
268
+ params: [],
269
+ return_type: nil,
270
+ is_abstract: false,
271
+ is_override: false,
272
+ is_overridable: false,
273
+ type_params: []
274
+ )
275
+ super()
276
+ @params = params
277
+ @return_type = return_type
278
+ @is_abstract = is_abstract
279
+ @is_override = is_override
280
+ @is_overridable = is_overridable
281
+ @type_params = type_params
282
+ end
283
+
284
+ sig { params(param: SigParam).void }
285
+ def <<(param)
286
+ @params << param
287
+ end
288
+ end
289
+
290
+ class SigParam < Node
291
+ extend T::Sig
292
+
293
+ sig { returns(String) }
294
+ attr_reader :name, :type
295
+
296
+ sig { params(name: String, type: String).void }
297
+ def initialize(name, type)
298
+ super()
299
+ @name = name
300
+ @type = type
301
+ end
302
+ end
303
+
304
+ # Sorbet's T::Struct
305
+
306
+ class TStruct < Class
307
+ extend T::Sig
308
+
309
+ sig { params(name: String).void }
310
+ def initialize(name)
311
+ super(name, superclass_name: "::T::Struct")
312
+ end
313
+ end
314
+
315
+ class TStructField < Node
316
+ extend T::Sig
317
+ extend T::Helpers
318
+
319
+ abstract!
320
+
321
+ sig { returns(String) }
322
+ attr_accessor :name, :type
323
+
324
+ sig { returns(T.nilable(String)) }
325
+ attr_accessor :default
326
+
327
+ sig do
328
+ params(
329
+ name: String,
330
+ type: String,
331
+ default: T.nilable(String)
332
+ ).void
333
+ end
334
+ def initialize(name, type, default: nil)
335
+ super()
336
+ @name = name
337
+ @type = type
338
+ @default = default
339
+ end
340
+ end
341
+
342
+ class TStructProp < TStructField; end
343
+ class TStructConst < TStructField; end
344
+
345
+ # Sorbet's T::Enum
346
+
347
+ class TEnum < Class
348
+ extend T::Sig
349
+
350
+ sig { params(name: String).void }
351
+ def initialize(name)
352
+ super(name, superclass_name: "::T::Enum")
353
+ end
354
+ end
355
+
356
+ class TEnumBlock < Node
357
+ extend T::Sig
358
+
359
+ sig { returns(T::Array[String]) }
360
+ attr_reader :names
361
+
362
+ sig { params(names: T::Array[String]).void }
363
+ def initialize(names = [])
364
+ super()
365
+ @names = names
366
+ end
367
+
368
+ sig { returns(T::Boolean) }
369
+ def empty?
370
+ names.empty?
371
+ end
372
+ end
373
+
374
+ # Sorbet's misc.
375
+
376
+ class Helper < Node
377
+ extend T::Helpers
378
+
379
+ sig { returns(String) }
380
+ attr_reader :name
381
+
382
+ sig { params(name: String).void }
383
+ def initialize(name)
384
+ super()
385
+ @name = name
386
+ end
387
+ end
388
+
389
+ class TypeMember < Node
390
+ extend T::Sig
391
+
392
+ sig { returns(String) }
393
+ attr_reader :name, :value
394
+
395
+ sig { params(name: String, value: String).void }
396
+ def initialize(name, value)
397
+ super()
398
+ @name = name
399
+ @value = value
400
+ end
401
+ end
402
+
403
+ class MixesInClassMethods < Mixin; end
404
+ end
405
+ end