sorbet-runtime 0.4.4398 → 0.4.4446

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: af4d1867e5caafedef289561e2ad9413bffa3fad
4
- data.tar.gz: 5652f7e1c9d2d54b253fcc6d2e5e5330c9c34691
3
+ metadata.gz: 4a800d5e7728111e31a78225eaf32a47c00e3fed
4
+ data.tar.gz: e8546195bf3b79b86484089e66a78966d8cbc9ef
5
5
  SHA512:
6
- metadata.gz: 430f858624d11168a7a5a7109115fc02d26bb919e4d14aa1bb94018abf88276d143a7550e23f320ce135be24d283dc70198d250a568339da9cb2a02ad94dfbbb
7
- data.tar.gz: 1f1e4cdaba1209536d9cc5da0ef766f841c80a50963146f11dcda2374beaa62835dd79ce5d8c2d6093f354d176bbac42f34d03067b9adf23d391f50e553e329e
6
+ metadata.gz: c2cc3a61f53c8073bcbc091bb5a244059714de8ef38d4f101f5d4d626ab928cfd546a504ce6ef77d1a972b824d0fbfaffdbf11f6fd7e85b6ad8f238131fee3d2
7
+ data.tar.gz: 8db326b0147134aabba33d5500065d5438ed2d362fc86f90ac36c8d53318c228439ace3c4ccb194ec35d8d42991a26a63d0c2ee433867ddbd98ad9710f8abcf6
@@ -17,6 +17,31 @@ module T::Configuration
17
17
  T::Private::RuntimeLevels.enable_checking_in_tests
18
18
  end
19
19
 
20
+ # Announce to Sorbet that we would like the final checks to be enabled when
21
+ # including and extending modules. Iff this is not called, then the following
22
+ # example will not raise an error.
23
+ #
24
+ # ```ruby
25
+ # module M
26
+ # extend T::Sig
27
+ # sig(:final) {void}
28
+ # def foo; end
29
+ # end
30
+ # class C
31
+ # include M
32
+ # def foo; end
33
+ # end
34
+ # ```
35
+ def self.enable_final_checks_for_include_extend
36
+ T::Private::Methods.set_final_checks_for_include_extend(true)
37
+ end
38
+
39
+ # Undo the effects of a previous call to
40
+ # `enable_final_checks_for_include_extend`.
41
+ def self.reset_final_checks_for_include_extend
42
+ T::Private::Methods.set_final_checks_for_include_extend(false)
43
+ end
44
+
20
45
  # Configure the default checked level for a sig with no explicit `.checked`
21
46
  # builder. When unset, the default checked level is `:always`.
22
47
  #
@@ -333,6 +358,26 @@ module T::Configuration
333
358
  @scalar_types || @default_scalar_types
334
359
  end
335
360
 
361
+ # Temporarily disable ruby warnings while executing the given block. This is
362
+ # useful when doing something that would normally cause a warning to be
363
+ # emitted in Ruby verbose mode ($VERBOSE = true).
364
+ #
365
+ # @yield
366
+ #
367
+ def self.without_ruby_warnings
368
+ if $VERBOSE
369
+ begin
370
+ original_verbose = $VERBOSE
371
+ $VERBOSE = false
372
+ yield
373
+ ensure
374
+ $VERBOSE = original_verbose
375
+ end
376
+ else
377
+ yield
378
+ end
379
+ end
380
+
336
381
 
337
382
  private_class_method def self.validate_lambda_given!(value)
338
383
  if !value.nil? && !value.respond_to?(:call)
data/lib/types/generic.rb CHANGED
@@ -13,11 +13,11 @@ module T::Generic
13
13
  self
14
14
  end
15
15
 
16
- def type_member(variance=:invariant, fixed: nil)
16
+ def type_member(variance=:invariant, fixed: nil, lower: T.untyped, upper: BasicObject)
17
17
  T::Types::TypeMember.new(variance) # rubocop:disable PrisonGuard/UseOpusTypesShortcut
18
18
  end
19
19
 
20
- def type_template(variance=:invariant, fixed: nil)
20
+ def type_template(variance=:invariant, fixed: nil, lower: T.untyped, upper: BasicObject)
21
21
  T::Types::TypeTemplate.new(variance) # rubocop:disable PrisonGuard/UseOpusTypesShortcut
22
22
  end
23
23
  end
@@ -28,6 +28,9 @@ module T::Private::Abstract::Declare
28
28
  raise "You must call `abstract!` *before* defining an initialize method"
29
29
  end
30
30
 
31
+ # Don't need to silence warnings via without_ruby_warnings when calling
32
+ # define_method because of the guard above
33
+
31
34
  mod.send(:define_method, :initialize) do |*args, &blk|
32
35
  if self.class == mod
33
36
  raise "#{mod} is declared as abstract; it cannot be instantiated"
@@ -32,7 +32,9 @@ module T::Private::ClassUtils
32
32
 
33
33
  if @overwritten
34
34
  # The original method was overwritten. Overwrite again to restore it.
35
- @mod.send(:define_method, @old_method.name, @old_method) # rubocop:disable PrisonGuard/UsePublicSend
35
+ T::Configuration.without_ruby_warnings do
36
+ @mod.send(:define_method, @old_method.name, @old_method) # rubocop:disable PrisonGuard/UsePublicSend
37
+ end
36
38
  else
37
39
  # The original method was in an ancestor. Restore it by removing the overriding method.
38
40
  @mod.send(:remove_method, @old_method.name) # rubocop:disable PrisonGuard/UsePublicSend
@@ -93,8 +95,10 @@ module T::Private::ClassUtils
93
95
  end
94
96
 
95
97
  overwritten = original_owner == mod
96
- mod.send(:define_method, name, &blk) # rubocop:disable PrisonGuard/UsePublicSend
97
- mod.send(original_visibility, name) # rubocop:disable PrisonGuard/UsePublicSend
98
+ T::Configuration.without_ruby_warnings do
99
+ mod.send(:define_method, name, &blk) # rubocop:disable PrisonGuard/UsePublicSend
100
+ mod.send(original_visibility, name) # rubocop:disable PrisonGuard/UsePublicSend
101
+ end
98
102
  new_method = mod.instance_method(name)
99
103
 
100
104
  ReplacedMethod.new(mod, original_method, new_method, overwritten, original_visibility)
@@ -11,6 +11,7 @@ class T::Private::DeclState
11
11
  end
12
12
 
13
13
  attr_accessor :active_declaration
14
+ attr_accessor :skip_next_on_method_added
14
15
 
15
16
  def reset!
16
17
  self.active_declaration = nil
@@ -6,23 +6,36 @@ module T::Private::Methods
6
6
  @signatures_by_method = {}
7
7
  @sig_wrappers = {}
8
8
  @sigs_that_raised = {}
9
+ # the info about whether a method is final is not stored in a DeclBuilder nor a Signature, but instead right here.
10
+ # this is because final checks are special:
11
+ # - they are done possibly before any sig block has run.
12
+ # - they are done even if the method being defined doesn't have a sig.
13
+ @final_methods = Set.new
14
+ # if a module directly defines a final method, or includes, extends, or inherits from a module which does so, then it
15
+ # should be in this set.
16
+ @modules_with_final = Set.new
17
+ @old_included_extended = nil
9
18
 
10
19
  ARG_NOT_PROVIDED = Object.new
11
20
  PROC_TYPE = Object.new
12
21
 
13
- DeclarationBlock = Struct.new(:mod, :loc, :blk)
22
+ DeclarationBlock = Struct.new(:mod, :loc, :blk, :final)
14
23
 
15
- def self.declare_sig(mod, &blk)
24
+ def self.declare_sig(mod, arg, &blk)
16
25
  install_hooks(mod)
17
26
 
18
27
  if T::Private::DeclState.current.active_declaration
19
- T::Private::DeclState.current.active_declaration = nil
28
+ T::Private::DeclState.current.reset!
20
29
  raise "You called sig twice without declaring a method inbetween"
21
30
  end
22
31
 
32
+ if !arg.nil? && arg != :final
33
+ raise "Invalid argument to `sig`: #{arg}"
34
+ end
35
+
23
36
  loc = caller_locations(2, 1).first
24
37
 
25
- T::Private::DeclState.current.active_declaration = DeclarationBlock.new(mod, loc, blk)
38
+ T::Private::DeclState.current.active_declaration = DeclarationBlock.new(mod, loc, blk, arg == :final)
26
39
 
27
40
  nil
28
41
  end
@@ -106,15 +119,60 @@ module T::Private::Methods
106
119
  @signatures_by_method[key]
107
120
  end
108
121
 
122
+ # when target includes a module with instance methods source_method_names, ensure there is zero intersection between
123
+ # the final instance methods of target and source_method_names. so, for every m in source_method_names, check if there
124
+ # is already a method defined on one of target_ancestors with the same name that is final.
125
+ def self._check_final_ancestors(target, target_ancestors, source_method_names)
126
+ # use reverse_each to check farther-up ancestors first, for better error messages. we could avoid this if we were on
127
+ # the version of ruby that adds the optional argument to method_defined? that allows you to exclude ancestors.
128
+ target_ancestors.reverse_each do |ancestor|
129
+ source_method_names.each do |method_name|
130
+ # the usage of method_owner_and_name_to_key(ancestor, method_name) instead of
131
+ # method_to_key(ancestor.instance_method(method_name)) is not (just) an optimization, but also required for
132
+ # correctness, since ancestor.method_defined?(method_name) may return true even if method_name is not defined
133
+ # directly on ancestor but instead an ancestor of ancestor.
134
+ if ancestor.method_defined?(method_name) && final_method?(method_owner_and_name_to_key(ancestor, method_name))
135
+ raise(
136
+ "`#{ancestor.name}##{method_name}` was declared as final and cannot be " +
137
+ (target == ancestor ? "redefined" : "overridden in `#{target.name}`")
138
+ )
139
+ end
140
+ end
141
+ end
142
+ end
143
+
144
+ private_class_method def self.add_final_method(method_key)
145
+ @final_methods.add(method_key)
146
+ end
147
+
148
+ private_class_method def self.final_method?(method_key)
149
+ @final_methods.include?(method_key)
150
+ end
151
+
152
+ def self.add_module_with_final(mod)
153
+ @modules_with_final.add(mod)
154
+ end
155
+
156
+ private_class_method def self.module_with_final?(mod)
157
+ @modules_with_final.include?(mod)
158
+ end
159
+
109
160
  # Only public because it needs to get called below inside the replace_method blocks below.
110
161
  def self._on_method_added(hook_mod, method_name, is_singleton_method: false)
111
- current_declaration = T::Private::DeclState.current.active_declaration
112
- return if !current_declaration
113
- T::Private::DeclState.current.reset!
162
+ if T::Private::DeclState.current.skip_next_on_method_added
163
+ T::Private::DeclState.current.skip_next_on_method_added = false
164
+ return
165
+ end
114
166
 
167
+ current_declaration = T::Private::DeclState.current.active_declaration
115
168
  mod = is_singleton_method ? hook_mod.singleton_class : hook_mod
116
169
  original_method = mod.instance_method(method_name)
117
170
 
171
+ _check_final_ancestors(mod, mod.ancestors, [method_name])
172
+
173
+ return if current_declaration.nil?
174
+ T::Private::DeclState.current.reset!
175
+
118
176
  sig_block = lambda do
119
177
  T::Private::Methods.run_sig(hook_mod, method_name, original_method, current_declaration)
120
178
  end
@@ -161,15 +219,16 @@ module T::Private::Methods
161
219
  end
162
220
 
163
221
  new_method = mod.instance_method(method_name)
164
- @sig_wrappers[method_to_key(new_method)] = sig_block
222
+ key = method_to_key(new_method)
223
+ @sig_wrappers[key] = sig_block
224
+ if current_declaration.final
225
+ add_final_method(key)
226
+ add_module_with_final(mod)
227
+ end
165
228
  end
166
229
 
167
230
  def self.sig_error(loc, message)
168
- raise(
169
- ArgumentError.new(
170
- "#{loc.path}:#{loc.lineno}: Error interpreting `sig`:\n #{message}\n\n"
171
- )
172
- )
231
+ raise(ArgumentError.new("#{loc.path}:#{loc.lineno}: Error interpreting `sig`:\n #{message}\n\n"))
173
232
  end
174
233
 
175
234
  # Executes the `sig` block, and converts the resulting Declaration
@@ -300,6 +359,38 @@ module T::Private::Methods
300
359
  end
301
360
  end
302
361
 
362
+ # the module target is adding the methods from the module source to itself. we need to check that for all instance
363
+ # methods M on source, M is not defined on any of target's ancestors.
364
+ def self._included_extended_impl(target, target_ancestors, source)
365
+ if !module_with_final?(target) && !module_with_final?(source)
366
+ return
367
+ end
368
+ add_module_with_final(target)
369
+ install_hooks(target)
370
+ _check_final_ancestors(target, target_ancestors - source.ancestors, source.instance_methods)
371
+ end
372
+
373
+ def self.set_final_checks_for_include_extend(enable)
374
+ is_enabled = @old_included_extended != nil
375
+ if enable == is_enabled
376
+ return
377
+ end
378
+ if is_enabled
379
+ @old_included_extended.each(&:restore)
380
+ @old_included_extended = nil
381
+ else
382
+ old_included = T::Private::ClassUtils.replace_method(Module, :included) do |arg|
383
+ old_included.bind(self).call(arg)
384
+ ::T::Private::Methods._included_extended_impl(arg, arg.ancestors, self)
385
+ end
386
+ old_extended = T::Private::ClassUtils.replace_method(Module, :extended) do |arg|
387
+ old_extended.bind(self).call(arg)
388
+ ::T::Private::Methods._included_extended_impl(arg, arg.singleton_class.ancestors, self)
389
+ end
390
+ @old_included_extended = [old_included, old_extended]
391
+ end
392
+ end
393
+
303
394
  def self.install_hooks(mod)
304
395
  return if @installed_hooks.include?(mod)
305
396
  @installed_hooks << mod
@@ -330,8 +421,13 @@ module T::Private::Methods
330
421
  original_singleton_method.bind(attached).call(:singleton_method_added)
331
422
  end
332
423
 
424
+ # use this directly if you don't want/need to box up the method into an object to pass to method_to_key.
425
+ private_class_method def self.method_owner_and_name_to_key(owner, name)
426
+ "#{owner.object_id}##{name}"
427
+ end
428
+
333
429
  private_class_method def self.method_to_key(method)
334
- "#{method.owner.object_id}##{method.name}"
430
+ method_owner_and_name_to_key(method.owner, method.name)
335
431
  end
336
432
 
337
433
  private_class_method def self.key_to_method(key)
@@ -35,9 +35,11 @@ module T::Private::Methods::CallValidation
35
35
  elsif method_sig.check_level == :always || (method_sig.check_level == :tests && T::Private::RuntimeLevels.check_tests?)
36
36
  create_validator_method(mod, original_method, method_sig, original_visibility)
37
37
  else
38
- # get all the shims out of the way and put back the original method
39
- mod.send(:define_method, method_sig.method_name, original_method)
40
- mod.send(original_visibility, method_sig.method_name)
38
+ T::Configuration.without_ruby_warnings do
39
+ # get all the shims out of the way and put back the original method
40
+ mod.send(:define_method, method_sig.method_name, original_method)
41
+ mod.send(original_visibility, method_sig.method_name)
42
+ end
41
43
  end
42
44
  # Return the newly created method (or the original one if we didn't replace it)
43
45
  mod.instance_method(method_sig.method_name)
@@ -161,18 +163,21 @@ module T::Private::Methods::CallValidation
161
163
  end
162
164
 
163
165
  def self.create_validator_method(mod, original_method, method_sig, original_visibility)
166
+ T::Private::DeclState.current.skip_next_on_method_added = true
164
167
  has_fixed_arity = method_sig.kwarg_types.empty? && !method_sig.has_rest && !method_sig.has_keyrest &&
165
168
  original_method.parameters.all? {|(kind, _name)| kind == :req}
166
169
  all_args_are_simple = method_sig.arg_types.all? {|_name, type| type.is_a?(T::Types::Simple)}
167
170
  has_simple_method_types = all_args_are_simple && method_sig.return_type.is_a?(T::Types::Simple)
168
171
  has_simple_procedure_types = all_args_are_simple && method_sig.return_type.is_a?(T::Private::Types::Void)
169
172
 
170
- if has_fixed_arity && has_simple_method_types && method_sig.arg_types.length < 5 && is_allowed_to_have_fast_path
171
- create_validator_method_fast(mod, original_method, method_sig)
172
- elsif has_fixed_arity && has_simple_procedure_types && method_sig.arg_types.length < 5 && is_allowed_to_have_fast_path
173
- create_validator_procedure_fast(mod, original_method, method_sig)
174
- else
175
- create_validator_slow(mod, original_method, method_sig)
173
+ T::Configuration.without_ruby_warnings do
174
+ if has_fixed_arity && has_simple_method_types && method_sig.arg_types.length < 5 && is_allowed_to_have_fast_path
175
+ create_validator_method_fast(mod, original_method, method_sig)
176
+ elsif has_fixed_arity && has_simple_procedure_types && method_sig.arg_types.length < 5 && is_allowed_to_have_fast_path
177
+ create_validator_procedure_fast(mod, original_method, method_sig)
178
+ else
179
+ create_validator_slow(mod, original_method, method_sig)
180
+ end
176
181
  end
177
182
  mod.send(original_visibility, method_sig.method_name)
178
183
  end
@@ -485,18 +485,20 @@ class T::Props::Decorator
485
485
 
486
486
  sig {params(name: Symbol, rules: Rules).void}
487
487
  private def define_getter_and_setter(name, rules)
488
- if rules[:immutable]
489
- @class.send(:define_method, "#{name}=") do |_x|
490
- raise T::Props::ImmutableProp.new("#{self.class}##{name} cannot be modified after creation.")
491
- end
492
- else
493
- @class.send(:define_method, "#{name}=") do |x|
494
- self.class.decorator.prop_set(self, name, x, rules)
488
+ T::Configuration.without_ruby_warnings do
489
+ if rules[:immutable]
490
+ @class.send(:define_method, "#{name}=") do |_x|
491
+ raise T::Props::ImmutableProp.new("#{self.class}##{name} cannot be modified after creation.")
492
+ end
493
+ else
494
+ @class.send(:define_method, "#{name}=") do |x|
495
+ self.class.decorator.prop_set(self, name, x, rules)
496
+ end
495
497
  end
496
- end
497
498
 
498
- @class.send(:define_method, name) do
499
- self.class.decorator.prop_get(self, name, rules)
499
+ @class.send(:define_method, name) do
500
+ self.class.decorator.prop_get(self, name, rules)
501
+ end
500
502
  end
501
503
  end
502
504
 
data/lib/types/sig.rb CHANGED
@@ -7,19 +7,24 @@ module T::Sig
7
7
  module WithoutRuntime
8
8
  # At runtime, does nothing, but statically it is treated exactly the same
9
9
  # as T::Sig#sig. Only use it in cases where you can't use T::Sig#sig.
10
- def self.sig(&blk); end # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
10
+ def self.sig(arg=nil, &blk); end # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
11
+
12
+ original_verbose = $VERBOSE
13
+ $VERBOSE = false
11
14
 
12
15
  # At runtime, does nothing, but statically it is treated exactly the same
13
16
  # as T::Sig#sig. Only use it in cases where you can't use T::Sig#sig.
14
- T::Sig::WithoutRuntime.sig {params(blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void}
15
- def self.sig(&blk); end # rubocop:disable PrisonGuard/BanBuiltinMethodOverride, Lint/DuplicateMethods
17
+ T::Sig::WithoutRuntime.sig {params(arg: T.nilable(Symbol), blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void}
18
+ def self.sig(arg=nil, &blk); end # rubocop:disable PrisonGuard/BanBuiltinMethodOverride, Lint/DuplicateMethods
19
+
20
+ $VERBOSE = original_verbose
16
21
  end
17
22
 
18
23
  # Declares a method with type signatures and/or
19
24
  # abstract/override/... helpers. See the documentation URL on
20
25
  # {T::Helpers}
21
- T::Sig::WithoutRuntime.sig {params(blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void}
22
- def sig(&blk) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
23
- T::Private::Methods.declare_sig(self, &blk)
26
+ T::Sig::WithoutRuntime.sig {params(arg: T.nilable(Symbol), blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void}
27
+ def sig(arg=nil, &blk) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
28
+ T::Private::Methods.declare_sig(self, arg, &blk)
24
29
  end
25
30
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sorbet-runtime
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.4398
4
+ version: 0.4.4446
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stripe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-07-08 00:00:00.000000000 Z
11
+ date: 2019-07-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest