sorbet-runtime 0.6.12449 → 0.6.12872

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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sorbet-runtime.rb +1 -1
  3. data/lib/types/_types.rb +12 -0
  4. data/lib/types/configuration.rb +2 -19
  5. data/lib/types/enum.rb +1 -1
  6. data/lib/types/private/abstract/data.rb +1 -1
  7. data/lib/types/private/methods/_methods.rb +36 -51
  8. data/lib/types/private/methods/call_validation.rb +10 -14
  9. data/lib/types/private/methods/signature_validation.rb +9 -3
  10. data/lib/types/private/sealed.rb +1 -1
  11. data/lib/types/props/_props.rb +5 -8
  12. data/lib/types/props/custom_type.rb +1 -1
  13. data/lib/types/props/decorator.rb +27 -48
  14. data/lib/types/props/has_lazily_specialized_methods.rb +1 -1
  15. data/lib/types/props/pretty_printable.rb +5 -5
  16. data/lib/types/props/private/apply_default.rb +4 -4
  17. data/lib/types/props/private/deserializer_generator.rb +10 -6
  18. data/lib/types/props/private/serde_transform.rb +4 -4
  19. data/lib/types/props/private/serializer_generator.rb +9 -5
  20. data/lib/types/props/private/setter_factory.rb +9 -9
  21. data/lib/types/props/serializable.rb +4 -9
  22. data/lib/types/props/type_validation.rb +2 -2
  23. data/lib/types/types/class_of.rb +1 -1
  24. data/lib/types/types/simple.rb +4 -4
  25. data/lib/types/types/t_enum.rb +2 -0
  26. data/lib/types/types/typed_array.rb +2 -2
  27. data/lib/types/types/typed_class.rb +1 -1
  28. data/lib/types/types/typed_enumerable.rb +6 -12
  29. data/lib/types/types/typed_enumerator.rb +2 -2
  30. data/lib/types/types/typed_enumerator_chain.rb +2 -2
  31. data/lib/types/types/typed_enumerator_lazy.rb +2 -2
  32. data/lib/types/types/typed_hash.rb +2 -2
  33. data/lib/types/types/typed_module.rb +102 -0
  34. data/lib/types/types/typed_range.rb +2 -2
  35. data/lib/types/types/typed_set.rb +2 -5
  36. metadata +5 -6
  37. data/lib/types/non_forcing_constants.rb +0 -55
  38. data/lib/types/private/methods/call_validation_2_6.rb +0 -1729
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4ff0f5980373a88353853602b3a4189fa30606d521590a275a5e7a9c1975f0ad
4
- data.tar.gz: a78f95a6c6d6e62cebb983a636b652a6d91a3914679305f32bd74b8866684759
3
+ metadata.gz: f1df82030d7eef7b4581bd982da33dafd6667515045b5e32e36d95d2cd96e876
4
+ data.tar.gz: c6b5c03495c971851b599f2465ce86a983a23b7455b9766755b77da9916b2d38
5
5
  SHA512:
6
- metadata.gz: b426d793bdb378528590f2d3d68e4ca0458ae81d9c5ea126ea8a7c6b5a8e295b094f85f9c0151ff58cdc577d04fee8cc7880b45390071dda46d8492e07f0204b
7
- data.tar.gz: e651470c0acf174f8789cce1bea7fe857326c751f1055ea97c6e952799d0355d7dc3a219fa2594327c58bf895ae291ae3b0f82167ef6d5c194395b986f9e4e3a
6
+ metadata.gz: 1491c37794b1e008c76728181aa3fdeb4fa7ac16c995d9c9d8477d7b5a6bd557c921781097d07f060b378de14648050cd773b35735ad46129fe074b888495fe4
7
+ data.tar.gz: 84d80bada939f468b9580708a11a63f3b08ab61c411b79d202afe294beab896f886213aa044d718170fcabced529be555ee08083be70b6f85f66ea61421ac6c2
@@ -85,6 +85,7 @@ require_relative 'types/boolean'
85
85
 
86
86
  # Depends on types/utils
87
87
  require_relative 'types/types/typed_array'
88
+ require_relative 'types/types/typed_module'
88
89
  require_relative 'types/types/typed_class'
89
90
 
90
91
  # Props dependencies
@@ -114,6 +115,5 @@ require_relative 'types/props/private/parser'
114
115
  require_relative 'types/props/generated_code_validation'
115
116
 
116
117
  require_relative 'types/struct'
117
- require_relative 'types/non_forcing_constants'
118
118
 
119
119
  require_relative 'types/compatibility_patches'
data/lib/types/_types.rb CHANGED
@@ -367,4 +367,16 @@ module T
367
367
  end
368
368
  end
369
369
  end
370
+
371
+ module Module
372
+ def self.[](type)
373
+ if type.is_a?(T::Types::Untyped)
374
+ T::Types::TypedModule::Untyped::Private::INSTANCE
375
+ elsif type.is_a?(T::Types::Anything)
376
+ T::Types::TypedModule::Anything::Private::INSTANCE
377
+ else
378
+ T::Types::TypedModule::Private::Pool.type_for_module(type)
379
+ end
380
+ end
381
+ end
370
382
  end
@@ -2,9 +2,6 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module T::Configuration
5
- # Cache this comparisonn to avoid two allocations all over the place.
6
- AT_LEAST_RUBY_2_7 = Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('2.7')
7
-
8
5
  # Announces to Sorbet that we are currently in a test environment, so it
9
6
  # should treat any sigs which are marked `.checked(:tests)` as if they were
10
7
  # just a normal sig.
@@ -448,11 +445,7 @@ module T::Configuration
448
445
  MODULE_NAME = Module.instance_method(:name)
449
446
  private_constant :MODULE_NAME
450
447
 
451
- @default_module_name_mangler = if T::Configuration::AT_LEAST_RUBY_2_7
452
- ->(type) { MODULE_NAME.bind_call(type) }
453
- else
454
- ->(type) { MODULE_NAME.bind(type).call } # rubocop:disable Performance/BindCall
455
- end
448
+ @default_module_name_mangler = ->(type) { MODULE_NAME.bind_call(type) }
456
449
 
457
450
  @module_name_mangler = nil
458
451
 
@@ -462,7 +455,7 @@ module T::Configuration
462
455
 
463
456
  # Set to override the default behavior for converting types
464
457
  # to names in generated code. Used by the runtime implementation
465
- # associated with `--stripe-packages` mode.
458
+ # associated with `--sorbet-packages` mode.
466
459
  #
467
460
  # @param [Lambda, Proc, nil] handler Proc that converts a type (Class/Module)
468
461
  # to a String (pass nil to reset to default behavior)
@@ -546,16 +539,6 @@ module T::Configuration
546
539
  @legacy_t_enum_migration_mode || false
547
540
  end
548
541
 
549
- @prop_freeze_handler = ->(instance, prop_name) {}
550
-
551
- def self.prop_freeze_handler=(handler)
552
- @prop_freeze_handler = handler
553
- end
554
-
555
- def self.prop_freeze_handler
556
- @prop_freeze_handler
557
- end
558
-
559
542
  @sealed_violation_whitelist = nil
560
543
  # @param [Array] sealed_violation_whitelist An array of Regexp to validate
561
544
  # whether inheriting /including a sealed module outside the defining module
data/lib/types/enum.rb CHANGED
@@ -273,7 +273,7 @@ class T::Enum
273
273
 
274
274
  ### Private implementation ###
275
275
 
276
- UNSET = T.let(Module.new.freeze, Module)
276
+ UNSET = T.let(Module.new.freeze, T::Module[T.anything])
277
277
  private_constant :UNSET
278
278
 
279
279
  sig { params(serialized_val: SerializedVal).void }
@@ -12,7 +12,7 @@
12
12
  # modules that override the `hash` method with something completely broken.
13
13
  module T::Private::Abstract::Data
14
14
  def self.get(mod, key)
15
- mod.instance_variable_get("@opus_abstract__#{key}") if key?(mod, key)
15
+ mod.instance_variable_get("@opus_abstract__#{key}")
16
16
  end
17
17
 
18
18
  def self.set(mod, key, value)
@@ -104,24 +104,6 @@ module T::Private::Methods
104
104
  T::Types::Proc.new(decl.params, decl.returns)
105
105
  end
106
106
 
107
- # Returns the signature for a method whose definition was preceded by `sig`.
108
- #
109
- # @param method [UnboundMethod]
110
- # @return [T::Private::Methods::Signature]
111
- def self.signature_for_method(method)
112
- signature_for_key(method_to_key(method))
113
- end
114
-
115
- private_class_method def self.signature_for_key(key)
116
- maybe_run_sig_block_for_key(key)
117
-
118
- # If a subclass Sub inherits a method `foo` from Base, then
119
- # Sub.instance_method(:foo) != Base.instance_method(:foo) even though they resolve to the
120
- # same method. Similarly, Foo.method(:bar) != Foo.singleton_class.instance_method(:bar).
121
- # So, we always do the look up by the method on the owner (Base in this example).
122
- @signatures_by_method[key]
123
- end
124
-
125
107
  # Fetch the directory name of the file that defines the `T::Private` constant and
126
108
  # add a trailing slash to allow us to match it as a directory prefix.
127
109
  SORBET_RUNTIME_LIB_PATH = File.dirname(T.const_source_location(:Private).first) + File::SEPARATOR
@@ -275,10 +257,8 @@ module T::Private::Methods
275
257
  # make sure to keep changes in sync.
276
258
  elsif method_sig.check_level == :always || (method_sig.check_level == :tests && T::Private::RuntimeLevels.check_tests?)
277
259
  CallValidation.validate_call(self, original_method, method_sig, args, blk)
278
- elsif T::Configuration::AT_LEAST_RUBY_2_7
279
- original_method.bind_call(self, *args, &blk)
280
260
  else
281
- original_method.bind(self).call(*args, &blk) # rubocop:disable Performance/BindCall
261
+ original_method.bind_call(self, *args, &blk)
282
262
  end
283
263
  end
284
264
  end
@@ -407,6 +387,19 @@ module T::Private::Methods
407
387
  end
408
388
  end
409
389
 
390
+ # Returns the signature for a method whose definition was preceded by `sig`.
391
+ #
392
+ # @param method [UnboundMethod]
393
+ # @return [T::Private::Methods::Signature]
394
+ def self.signature_for_method(method)
395
+ signature_for_key(method_to_key(method))
396
+ end
397
+
398
+ private_class_method def self.signature_for_key(key)
399
+ maybe_run_sig_block_for_key(key)
400
+ @signatures_by_method[key]
401
+ end
402
+
410
403
  def self.unwrap_method(mod, signature, original_method)
411
404
  maybe_wrapped_method = CallValidation.wrap_method_if_needed(mod, signature, original_method)
412
405
  @signatures_by_method[method_to_key(maybe_wrapped_method)] = signature
@@ -433,6 +426,25 @@ module T::Private::Methods
433
426
  run_sig_block_for_key(method_to_key(method))
434
427
  end
435
428
 
429
+ # use this directly if you don't want/need to box up the method into an object to pass to method_to_key.
430
+ private_class_method def self.method_owner_and_name_to_key(owner, name)
431
+ "#{owner.object_id}##{name}"
432
+ end
433
+
434
+ private_class_method def self.method_to_key(method)
435
+ # If a subclass Sub inherits a method `foo` from Base, then
436
+ # Sub.instance_method(:foo) != Base.instance_method(:foo) even though they resolve to the
437
+ # same method. Similarly, Foo.method(:bar) != Foo.singleton_class.instance_method(:bar).
438
+ # So, we always do the look up by the method on the owner (Base in this example).
439
+ method_owner_and_name_to_key(method.owner, method.name)
440
+ end
441
+
442
+ private_class_method def self.key_to_method(key)
443
+ id, name = key.split("#")
444
+ obj = ObjectSpace._id2ref(id.to_i)
445
+ obj.instance_method(name)
446
+ end
447
+
436
448
  private_class_method def self.run_sig_block_for_key(key, force_type_init: false)
437
449
  blk = @sig_wrappers[key]
438
450
  if !blk
@@ -516,27 +528,15 @@ module T::Private::Methods
516
528
  @old_hooks = nil
517
529
  else
518
530
  old_included = T::Private::ClassUtils.replace_method(Module, :included, true) do |arg|
519
- if T::Configuration::AT_LEAST_RUBY_2_7
520
- old_included.bind_call(self, arg)
521
- else
522
- old_included.bind(self).call(arg) # rubocop:disable Performance/BindCall
523
- end
531
+ old_included.bind_call(self, arg)
524
532
  ::T::Private::Methods._hook_impl(arg, false, self)
525
533
  end
526
534
  old_extended = T::Private::ClassUtils.replace_method(Module, :extended, true) do |arg|
527
- if T::Configuration::AT_LEAST_RUBY_2_7
528
- old_extended.bind_call(self, arg)
529
- else
530
- old_extended.bind(self).call(arg) # rubocop:disable Performance/BindCall
531
- end
535
+ old_extended.bind_call(self, arg)
532
536
  ::T::Private::Methods._hook_impl(arg, true, self)
533
537
  end
534
538
  old_inherited = T::Private::ClassUtils.replace_method(Class, :inherited, true) do |arg|
535
- if T::Configuration::AT_LEAST_RUBY_2_7
536
- old_inherited.bind_call(self, arg)
537
- else
538
- old_inherited.bind(self).call(arg) # rubocop:disable Performance/BindCall
539
- end
539
+ old_inherited.bind_call(self, arg)
540
540
  ::T::Private::Methods._hook_impl(arg, false, self)
541
541
  end
542
542
  @old_hooks = [old_included, old_extended, old_inherited]
@@ -604,21 +604,6 @@ module T::Private::Methods
604
604
  mod.method(name)
605
605
  end
606
606
  end
607
-
608
- # use this directly if you don't want/need to box up the method into an object to pass to method_to_key.
609
- private_class_method def self.method_owner_and_name_to_key(owner, name)
610
- "#{owner.object_id}##{name}"
611
- end
612
-
613
- private_class_method def self.method_to_key(method)
614
- method_owner_and_name_to_key(method.owner, method.name)
615
- end
616
-
617
- private_class_method def self.key_to_method(key)
618
- id, name = key.split("#")
619
- obj = ObjectSpace._id2ref(id.to_i)
620
- obj.instance_method(name)
621
- end
622
607
  end
623
608
 
624
609
  # This has to be here, and can't be nested inside `T::Private::Methods`,
@@ -79,13 +79,13 @@ module T::Private::Methods::CallValidation
79
79
 
80
80
  # All the types for which valid? unconditionally returns `true`
81
81
  return_is_ignorable =
82
- (method_sig.return_type.equal?(T::Types::Untyped::Private::INSTANCE) ||
83
- method_sig.return_type.equal?(T::Types::Anything::Private::INSTANCE) ||
84
- method_sig.return_type.equal?(T::Types::AttachedClassType::Private::INSTANCE) ||
85
- method_sig.return_type.equal?(T::Types::SelfType::Private::INSTANCE) ||
86
- method_sig.return_type.is_a?(T::Types::TypeParameter) ||
87
- method_sig.return_type.is_a?(T::Types::TypeVariable) ||
88
- (method_sig.return_type.is_a?(T::Types::Simple) && method_sig.return_type.raw_type.equal?(BasicObject)))
82
+ method_sig.return_type.equal?(T::Types::Untyped::Private::INSTANCE) ||
83
+ method_sig.return_type.equal?(T::Types::Anything::Private::INSTANCE) ||
84
+ method_sig.return_type.equal?(T::Types::AttachedClassType::Private::INSTANCE) ||
85
+ method_sig.return_type.equal?(T::Types::SelfType::Private::INSTANCE) ||
86
+ method_sig.return_type.is_a?(T::Types::TypeParameter) ||
87
+ method_sig.return_type.is_a?(T::Types::TypeVariable) ||
88
+ (method_sig.return_type.is_a?(T::Types::Simple) && method_sig.return_type.raw_type.equal?(BasicObject))
89
89
 
90
90
  returns_anything_method = all_args_are_simple && return_is_ignorable
91
91
 
@@ -176,7 +176,7 @@ module T::Private::Methods::CallValidation
176
176
  # this code is sig validation code.
177
177
  # Please issue `finish` to step out of it
178
178
 
179
- return_value = T::Configuration::AT_LEAST_RUBY_2_7 ? original_method.bind_call(instance, *args, &blk) : original_method.bind(instance).call(*args, &blk)
179
+ return_value = original_method.bind_call(instance, *args, &blk)
180
180
 
181
181
  # The only type that is allowed to change the return value is `.void`.
182
182
  # It ignores what you returned and changes it to be a private singleton.
@@ -279,7 +279,7 @@ module T::Private::Methods::CallValidation
279
279
  # this code is sig validation code.
280
280
  # Please issue `finish` to step out of it
281
281
 
282
- return_value = T::Configuration::AT_LEAST_RUBY_2_7 ? original_method.bind_call(instance, *args, &blk) : original_method.bind(instance).call(*args, &blk)
282
+ return_value = original_method.bind_call(instance, *args, &blk)
283
283
 
284
284
  # The only type that is allowed to change the return value is `.void`.
285
285
  # It ignores what you returned and changes it to be a private singleton.
@@ -332,8 +332,4 @@ module T::Private::Methods::CallValidation
332
332
  end
333
333
  end
334
334
 
335
- if T::Configuration::AT_LEAST_RUBY_2_7
336
- require_relative './call_validation_2_7'
337
- else
338
- require_relative './call_validation_2_6'
339
- end
335
+ require_relative './call_validation_2_7'
@@ -277,6 +277,9 @@ module T::Private::Methods::SignatureValidation
277
277
  end
278
278
  end
279
279
 
280
+ ALLOW_INCOMPATIBLE_VISIBILITY = [:visibility, true].freeze
281
+ private_constant :ALLOW_INCOMPATIBLE_VISIBILITY
282
+
280
283
  def self.validate_override_visibility(signature, super_signature)
281
284
  return if super_signature.mode == Modes.untyped
282
285
  # This departs from the behavior of other `validate_override_whatever` functions in that it
@@ -284,8 +287,8 @@ module T::Private::Methods::SignatureValidation
284
287
  # done because the primary method for silencing these errors (`allow_incompatible: :visibility`)
285
288
  # requires an `override` node to attach to. Once we have static override checking for implicitly
286
289
  # overridden methods, we can remove this.
287
- return unless [Modes.override, Modes.overridable_override].include?(signature.mode)
288
- return if [:visibility, true].include?(signature.override_allow_incompatible)
290
+ return unless Modes::OVERRIDE_MODES.include?(signature.mode)
291
+ return if ALLOW_INCOMPATIBLE_VISIBILITY.include?(signature.override_allow_incompatible)
289
292
  method = signature.method
290
293
  super_method = super_signature.method
291
294
  mode_noun = super_signature.mode == Modes.abstract ? 'implementation' : 'override'
@@ -305,8 +308,11 @@ module T::Private::Methods::SignatureValidation
305
308
  end
306
309
 
307
310
  # Higher = more restrictive.
311
+ METHOD_VISIBILITIES = %i[public protected private].freeze
312
+ private_constant :METHOD_VISIBILITIES
313
+
308
314
  private_class_method def self.visibility_strength(vis)
309
- %i[public protected private].find_index(vis)
315
+ METHOD_VISIBILITIES.find_index(vis)
310
316
  end
311
317
 
312
318
  private_class_method def self.base_override_loc_str(signature, super_signature)
@@ -70,7 +70,7 @@ module T::Private::Sealed
70
70
 
71
71
  def self.validate_inheritance(caller_loc, parent, child, verb)
72
72
  this_file = caller_loc&.path
73
- decl_file = parent.instance_variable_get(:@sorbet_sealed_module_decl_file) if sealed_module?(parent)
73
+ decl_file = parent.instance_variable_get(:@sorbet_sealed_module_decl_file)
74
74
 
75
75
  if !this_file
76
76
  raise "Could not use backtrace to determine file for #{verb} child #{child}"
@@ -132,17 +132,14 @@ module T::Props
132
132
  end
133
133
 
134
134
  # Shorthand helper to define a `prop` with `immutable => true`
135
- sig { params(name: Symbol, cls_or_args: T.untyped, args: T.untyped).void }
136
- def const(name, cls_or_args, **args)
137
- if (cls_or_args.is_a?(Hash) && cls_or_args.key?(:immutable)) || args.key?(:immutable)
135
+ sig { params(name: Symbol, cls: T.untyped, rules: T.untyped).void }
136
+ def const(name, cls, **rules)
137
+ if rules.key?(:immutable)
138
138
  Kernel.raise ArgumentError.new("Cannot pass 'immutable' argument when using 'const' keyword to define a prop")
139
139
  end
140
140
 
141
- if cls_or_args.is_a?(Hash)
142
- self.prop(name, **cls_or_args.merge(immutable: true))
143
- else
144
- self.prop(name, cls_or_args, **args.merge(immutable: true))
145
- end
141
+ rules[:immutable] = true
142
+ self.prop(name, cls, **rules)
146
143
  end
147
144
 
148
145
  def included(child)
@@ -50,7 +50,7 @@ module T::Props
50
50
  sig { abstract.params(scalar: T.untyped).returns(T.untyped).checked(:never) }
51
51
  def deserialize(scalar); end
52
52
 
53
- sig { override.params(_base: Module).void }
53
+ sig { override.params(_base: T::Module[T.anything]).void }
54
54
  def self.included(_base)
55
55
  super
56
56
 
@@ -13,8 +13,10 @@ class T::Props::Decorator
13
13
  Rules = T.type_alias { T::Hash[Symbol, T.untyped] }
14
14
  DecoratedInstance = T.type_alias { Object } # Would be T::Props, but that produces circular reference errors in some circumstances
15
15
  PropType = T.type_alias { T::Types::Base }
16
- PropTypeOrClass = T.type_alias { T.any(PropType, Module) }
16
+ PropTypeOrClass = T.type_alias { T.any(PropType, T::Module[T.anything]) }
17
17
  OverrideRules = T.type_alias { T::Hash[Symbol, {allow_incompatible: T::Boolean}] }
18
+ DecoratedClassType = T.type_alias { T.all(T::Module[T.anything], T::Props::ClassMethods) }
19
+ private_constant :DecoratedClassType
18
20
 
19
21
  class NoRulesError < StandardError; end
20
22
 
@@ -38,7 +40,7 @@ class T::Props::Decorator
38
40
 
39
41
  sig { params(klass: T.untyped).void.checked(:never) }
40
42
  def initialize(klass)
41
- @class = T.let(klass, T.all(Module, T::Props::ClassMethods))
43
+ @class = T.let(klass, DecoratedClassType)
42
44
  @class.plugins.each do |mod|
43
45
  T::Props::Plugin::Private.apply_decorator_methods(mod, self)
44
46
  end
@@ -102,7 +104,7 @@ class T::Props::Decorator
102
104
  end
103
105
 
104
106
  # checked(:never) - O(prop accesses)
105
- sig { returns(T.all(Module, T::Props::ClassMethods)).checked(:never) }
107
+ sig { returns(DecoratedClassType).checked(:never) }
106
108
  def decorated_class
107
109
  @class
108
110
  end
@@ -174,7 +176,9 @@ class T::Props::Decorator
174
176
  .checked(:never)
175
177
  end
176
178
  def prop_get(instance, prop, rules=prop_rules(prop))
177
- val = instance.instance_variable_get(rules[:accessor_key]) if instance.instance_variable_defined?(rules[:accessor_key])
179
+ # `instance_variable_get` will return nil if the variable doesn't exist
180
+ # which is what we want to have happen for the logic below.
181
+ val = instance.instance_variable_get(rules[:accessor_key])
178
182
  if !val.nil?
179
183
  val
180
184
  elsif (d = rules[:ifunset])
@@ -194,7 +198,9 @@ class T::Props::Decorator
194
198
  .checked(:never)
195
199
  end
196
200
  def prop_get_if_set(instance, prop, rules=prop_rules(prop))
197
- instance.instance_variable_get(rules[:accessor_key]) if instance.instance_variable_defined?(rules[:accessor_key])
201
+ # `instance_variable_get` will return nil if the variable doesn't exist
202
+ # which is what we want to have happen for the return value here.
203
+ instance.instance_variable_get(rules[:accessor_key])
198
204
  end
199
205
  alias_method :get, :prop_get_if_set # Alias for backwards compatibility
200
206
 
@@ -203,7 +209,7 @@ class T::Props::Decorator
203
209
  params(
204
210
  instance: DecoratedInstance,
205
211
  prop: Symbol,
206
- foreign_class: Module,
212
+ foreign_class: T::Module[T.anything],
207
213
  rules: Rules,
208
214
  opts: T::Hash[Symbol, T.untyped],
209
215
  )
@@ -222,7 +228,7 @@ class T::Props::Decorator
222
228
  sig do
223
229
  params(
224
230
  name: Symbol,
225
- cls: Module,
231
+ cls: T::Module[T.anything],
226
232
  rules: Rules,
227
233
  type: PropTypeOrClass
228
234
  )
@@ -238,7 +244,9 @@ class T::Props::Decorator
238
244
  end
239
245
 
240
246
  if rules.keys.any? { |k| !valid_rule_key?(k) }
241
- raise ArgumentError.new("At least one invalid prop arg supplied in #{self}: #{rules.keys.inspect}")
247
+ invalid_keys = rules.keys.reject { |k| valid_rule_key?(k) }
248
+ suffix = invalid_keys.size == 1 ? "" : "s"
249
+ raise ArgumentError.new("Invalid prop arg#{suffix} supplied in #{self}: #{invalid_keys.inspect}")
242
250
  end
243
251
 
244
252
  if !rules[:clobber_existing_method!] && !rules[:without_accessors] && BANNED_METHOD_NAMES.include?(name.to_sym)
@@ -258,7 +266,9 @@ class T::Props::Decorator
258
266
  nil
259
267
  end
260
268
 
261
- SAFE_NAME = T.let(/\A[A-Za-z_][A-Za-z0-9_-]*\z/.freeze, Regexp, checked: false)
269
+ SAFE_NAME = T.let(/\A[A-Za-z_][A-Za-z0-9_-]*\z/, Regexp, checked: false)
270
+ # Should be exactly the same as `SAFE_NAME`, but with a leading `@`.
271
+ SAFE_ACCESSOR_KEY_NAME = T.let(/\A@[A-Za-z_][A-Za-z0-9_-]*\z/, Regexp, checked: false)
262
272
 
263
273
  # Used to validate both prop names and serialized forms
264
274
  sig { params(name: T.any(Symbol, String)).void.checked(:never) }
@@ -269,7 +279,7 @@ class T::Props::Decorator
269
279
  end
270
280
 
271
281
  # This converts the type from a T::Type to a regular old ruby class.
272
- sig { params(type: T::Types::Base).returns(Module).checked(:never) }
282
+ sig { params(type: T::Types::Base).returns(T::Module[T.anything]).checked(:never) }
273
283
  private def convert_type_to_class(type)
274
284
  case type
275
285
  when T::Types::TypedArray, T::Types::FixedArray
@@ -321,7 +331,7 @@ class T::Props::Decorator
321
331
  (@class.method_defined?(name) || @class.private_method_defined?(name)) &&
322
332
  # Unfortunately, older versions of ruby don't allow the second parameter on
323
333
  # `private_method_defined?`.
324
- (!@class.method_defined?(name, false) && !@class.private_method_defined?(name, false))
334
+ !@class.method_defined?(name, false) && !@class.private_method_defined?(name, false)
325
335
  end
326
336
 
327
337
  sig(:final) { params(name: Symbol, rules: Rules).void.checked(:never) }
@@ -402,8 +412,6 @@ class T::Props::Decorator
402
412
 
403
413
  # extra arbitrary metadata attached by the code defining this property
404
414
 
405
- validate_not_missing_sensitivity(name, rules)
406
-
407
415
  # for backcompat (the `:array` key is deprecated but because the name is
408
416
  # so generic it's really hard to be sure it's not being relied on anymore)
409
417
  if type.is_a?(T::Types::TypedArray)
@@ -479,35 +487,6 @@ class T::Props::Decorator
479
487
  end
480
488
  end
481
489
 
482
- # checked(:never) - Rules hash is expensive to check
483
- sig { params(prop_name: Symbol, rules: Rules).void.checked(:never) }
484
- private def validate_not_missing_sensitivity(prop_name, rules)
485
- if rules[:sensitivity].nil?
486
- if rules[:redaction]
487
- T::Configuration.hard_assert_handler(
488
- "#{@class}##{prop_name} has a 'redaction:' annotation but no " \
489
- "'sensitivity:' annotation. This is probably wrong, because if a " \
490
- "prop needs redaction then it is probably sensitive. Add a " \
491
- "sensitivity annotation like 'sensitivity: Opus::Sensitivity::PII." \
492
- "whatever', or explicitly override this check with 'sensitivity: []'."
493
- )
494
- end
495
- # TODO(PRIVACYENG-982) Ideally we'd also check for 'password' and possibly
496
- # other terms, but this interacts badly with ProtoDefinedDocument because
497
- # the proto syntax currently can't declare "sensitivity: []"
498
- if /\bsecret\b/.match?(prop_name)
499
- T::Configuration.hard_assert_handler(
500
- "#{@class}##{prop_name} has the word 'secret' in its name, but no " \
501
- "'sensitivity:' annotation. This is probably wrong, because if a " \
502
- "prop is named 'secret' then it is probably sensitive. Add a " \
503
- "sensitivity annotation like 'sensitivity: Opus::Sensitivity::NonPII." \
504
- "security_token', or explicitly override this check with " \
505
- "'sensitivity: []'."
506
- )
507
- end
508
- end
509
- end
510
-
511
490
  # Create `#{prop_name}_redacted` method
512
491
  sig do
513
492
  params(
@@ -609,7 +588,7 @@ class T::Props::Decorator
609
588
  sig do
610
589
  params(
611
590
  prop_name: Symbol,
612
- prop_cls: Module,
591
+ prop_cls: T::Module[T.anything],
613
592
  rules: Rules,
614
593
  foreign: T.untyped,
615
594
  )
@@ -652,10 +631,10 @@ class T::Props::Decorator
652
631
  #
653
632
  # This gets called when a module or class that extends T::Props gets included, extended,
654
633
  # prepended, or inherited.
655
- sig { params(child: Module).void.checked(:never) }
634
+ sig { params(child: T::Module[T.anything]).void.checked(:never) }
656
635
  def model_inherited(child)
657
636
  child.extend(T::Props::ClassMethods)
658
- child = T.cast(child, T.all(Module, T::Props::ClassMethods))
637
+ child = T.cast(child, DecoratedClassType)
659
638
 
660
639
  child.plugins.concat(decorated_class.plugins)
661
640
  decorated_class.plugins.each do |mod|
@@ -742,19 +721,19 @@ class T::Props::Decorator
742
721
  result
743
722
  end
744
723
 
745
- sig { params(child: T.all(Module, T::Props::ClassMethods), prop: Symbol).returns(T::Boolean).checked(:never) }
724
+ sig { params(child: DecoratedClassType, prop: Symbol).returns(T::Boolean).checked(:never) }
746
725
  private def clobber_getter?(child, prop)
747
726
  !!(child.decorator.method(:prop_get).owner != method(:prop_get).owner &&
748
727
  child.instance_method(prop).source_location&.first == __FILE__)
749
728
  end
750
729
 
751
- sig { params(child: T.all(Module, T::Props::ClassMethods), prop: Symbol).returns(T::Boolean).checked(:never) }
730
+ sig { params(child: DecoratedClassType, prop: Symbol).returns(T::Boolean).checked(:never) }
752
731
  private def clobber_setter?(child, prop)
753
732
  !!(child.decorator.method(:prop_set).owner != method(:prop_set).owner &&
754
733
  child.instance_method("#{prop}=").source_location&.first == __FILE__)
755
734
  end
756
735
 
757
- sig { params(mod: Module).void.checked(:never) }
736
+ sig { params(mod: T::Module[T.anything]).void.checked(:never) }
758
737
  def plugin(mod)
759
738
  decorated_class.plugins << mod
760
739
  T::Props::Plugin::Private.apply_class_methods(mod, decorated_class)
@@ -37,7 +37,7 @@ module T::Props
37
37
 
38
38
  sig { returns(T::Boolean) }
39
39
  def self.lazy_evaluation_enabled?
40
- !defined?(@lazy_evaluation_disabled) || !@lazy_evaluation_disabled
40
+ !@lazy_evaluation_disabled
41
41
  end
42
42
 
43
43
  module DecoratorMethods
@@ -12,8 +12,8 @@ module T::Props::PrettyPrintable
12
12
  pp.group(1, "<#{klass.inspect_class_with_decoration(self)}", ">") do
13
13
  klass.all_props.sort.each do |prop|
14
14
  pp.breakable
15
- val = klass.get(self, prop)
16
15
  rules = klass.prop_rules(prop)
16
+ val = klass.get(self, prop, rules)
17
17
  pp.text("#{prop}=")
18
18
  if (custom_inspect = rules[:inspect])
19
19
  inspected = if T::Utils.arity(custom_inspect) == 1
@@ -22,8 +22,8 @@ module T::Props::PrettyPrintable
22
22
  custom_inspect.call(val, {multiline: multiline})
23
23
  end
24
24
  pp.text(inspected.nil? ? "nil" : inspected)
25
- elsif rules[:sensitivity] && !rules[:sensitivity].empty? && !val.nil?
26
- pp.text("<REDACTED #{rules[:sensitivity].join(', ')}>")
25
+ elsif (sensitivity = rules[:sensitivity]) && !sensitivity.empty? && !val.nil?
26
+ pp.text("<REDACTED #{sensitivity.join(', ')}>")
27
27
  else
28
28
  val.pretty_print(pp)
29
29
  end
@@ -56,14 +56,14 @@ module T::Props::PrettyPrintable
56
56
 
57
57
  # Overridable method to specify how the first part of a `pretty_print`d object's class should look like
58
58
  # NOTE: This is just to support Stripe's `PrettyPrintableModel` case, and not recommended to be overridden
59
- sig { params(instance: T::Props::PrettyPrintable).returns(String) }
59
+ sig { params(instance: T::Props::PrettyPrintable).returns(String).checked(:never) }
60
60
  def inspect_class_with_decoration(instance)
61
61
  T.unsafe(instance).class.to_s
62
62
  end
63
63
 
64
64
  # Overridable method to add anything that is not a prop
65
65
  # NOTE: This is to support cases like Serializable's `@_extra_props`, and Stripe's `PrettyPrintableModel#@_deleted`
66
- sig { params(instance: T::Props::PrettyPrintable, pp: T.any(PrettyPrint, PP::SingleLine)).void }
66
+ sig { params(instance: T::Props::PrettyPrintable, pp: T.any(PrettyPrint, PP::SingleLine)).void.checked(:never) }
67
67
  def pretty_print_extra(instance, pp); end
68
68
  end
69
69
  end
@@ -27,10 +27,10 @@ module T::Props
27
27
  sig { abstract.params(instance: T.all(T::Props::Optional, Object)).void.checked(:never) }
28
28
  def set_default(instance); end
29
29
 
30
- NO_CLONE_TYPES = T.let([TrueClass, FalseClass, NilClass, Symbol, Numeric, T::Enum].freeze, T::Array[Module])
30
+ NO_CLONE_TYPES = T.let([TrueClass, FalseClass, NilClass, Symbol, Numeric, T::Enum].freeze, T::Array[T::Module[T.anything]])
31
31
 
32
32
  # checked(:never) - Rules hash is expensive to check
33
- sig { params(cls: Module, rules: T::Hash[Symbol, T.untyped]).returns(T.nilable(ApplyDefault)).checked(:never) }
33
+ sig { params(cls: T::Module[T.anything], rules: T::Hash[Symbol, T.untyped]).returns(T.nilable(ApplyDefault)).checked(:never) }
34
34
  def self.for(cls, rules)
35
35
  accessor_key = rules.fetch(:accessor_key)
36
36
  setter = rules.fetch(:setter_proc)
@@ -138,7 +138,7 @@ module T::Props
138
138
  # checked(:never) - We do this with `T.let` instead
139
139
  sig do
140
140
  params(
141
- cls: Module,
141
+ cls: T::Module[T.anything],
142
142
  factory: T.any(Proc, Method),
143
143
  accessor_key: Symbol,
144
144
  setter_proc: SetterFactory::SetterProc,
@@ -147,7 +147,7 @@ module T::Props
147
147
  .checked(:never)
148
148
  end
149
149
  def initialize(cls, factory, accessor_key, setter_proc)
150
- @class = T.let(cls, Module)
150
+ @class = T.let(cls, T::Module[T.anything])
151
151
  @factory = T.let(factory, T.any(Proc, Method))
152
152
  super(accessor_key, setter_proc)
153
153
  end