sorbet-runtime 0.4.4442 → 0.4.4443

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 640c933effd2dc03efc78babf1ae00ddec93c6cf
4
- data.tar.gz: c336afa4260d72745c65e65793f9d00938ebbaf8
3
+ metadata.gz: f71f4ecf6f6c24f0dd4ba065d1f911e5ca4f3477
4
+ data.tar.gz: 2992a0f425abcba10f414389cfb23469af9eca1d
5
5
  SHA512:
6
- metadata.gz: 38cf3b500f073f98d5148e36294f83b33be9fa32d8876f2c277344493df76867f4a387bc424643cb562d9b22af791f6b4dcea0f94158f04330f93fbaac2f3107
7
- data.tar.gz: e73a0f9f99a5e17c2e1a6ff598c2b4bbae31155f219c195c4dc8a76795b1c19612b1c3d348ab1b916bab24f73b25b4ab0ecc7075e3bff4c18adf46cc0bb57c10
6
+ metadata.gz: 4bcdd49035d1e279c21225ad88becc369ad63a5ce046e05587ecba1b6c0f8c4424e28eb2060726248168d6a0c6daee042dd84388e9321682f72e7a003d83f41a
7
+ data.tar.gz: f5c7844214396806c60bcbee562e72c87b5cb73a352b5593e8758d7b8b8c4fcfb91787475518bab58b28e03ef5b4c19a393c408ffcbb693e730237ea7384900e
@@ -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
  #
@@ -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)
@@ -163,6 +163,7 @@ module T::Private::Methods::CallValidation
163
163
  end
164
164
 
165
165
  def self.create_validator_method(mod, original_method, method_sig, original_visibility)
166
+ T::Private::DeclState.current.skip_next_on_method_added = true
166
167
  has_fixed_arity = method_sig.kwarg_types.empty? && !method_sig.has_rest && !method_sig.has_keyrest &&
167
168
  original_method.parameters.all? {|(kind, _name)| kind == :req}
168
169
  all_args_are_simple = method_sig.arg_types.all? {|_name, type| type.is_a?(T::Types::Simple)}
data/lib/types/sig.rb CHANGED
@@ -7,15 +7,15 @@ 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
11
 
12
12
  original_verbose = $VERBOSE
13
13
  $VERBOSE = false
14
14
 
15
15
  # At runtime, does nothing, but statically it is treated exactly the same
16
16
  # as T::Sig#sig. Only use it in cases where you can't use T::Sig#sig.
17
- T::Sig::WithoutRuntime.sig {params(blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void}
18
- 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
19
 
20
20
  $VERBOSE = original_verbose
21
21
  end
@@ -23,8 +23,8 @@ module T::Sig
23
23
  # Declares a method with type signatures and/or
24
24
  # abstract/override/... helpers. See the documentation URL on
25
25
  # {T::Helpers}
26
- T::Sig::WithoutRuntime.sig {params(blk: T.proc.bind(T::Private::Methods::DeclBuilder).void).void}
27
- def sig(&blk) # rubocop:disable PrisonGuard/BanBuiltinMethodOverride
28
- 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)
29
29
  end
30
30
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sorbet-runtime
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.4442
4
+ version: 0.4.4443
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stripe