Module: T::Private::Methods::SignatureValidation
- Defined in:
- lib/types/private/methods/signature_validation.rb
Overview
typed: true
Constant Summary collapse
Class Method Summary collapse
- .validate(signature) ⇒ Object
- .validate_non_override_mode(signature) ⇒ Object
- .validate_override_mode(signature, super_signature) ⇒ Object
- .validate_override_shape(signature, super_signature) ⇒ Object
- .validate_override_types(signature, super_signature) ⇒ Object
Class Method Details
.validate(signature) ⇒ Object
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/types/private/methods/signature_validation.rb', line 8 def self.validate(signature) if signature.method_name == :initialize && signature.method.owner.is_a?(Class) # Constructors are special. They look like overrides in terms of a super_method existing, # but in practice, you never call them polymorphically. Conceptually, they're standard # methods (this is consistent with how they're treated in other languages, e.g. Java) if signature.mode != Modes.standard raise "`initialize` should not use `.abstract` or `.implementation` or any other inheritance modifiers." end return end super_method = signature.method.super_method if super_method && super_method.owner != signature.method.owner Methods.maybe_run_sig_block_for_method(super_method) super_signature = Methods.signature_for_method(super_method) # If the super_method has any kwargs we can't build a # Signature for it, so we'll just skip validation in that case. if !super_signature && !super_method.parameters.select {|kind, _| kind == :rest || kind == :kwrest}.empty? nil else # super_signature can be nil when we're overriding a method (perhaps a builtin) that didn't use # one of the method signature helpers. Use an untyped signature so we can still validate # everything but types. # # We treat these signatures as overridable, that way people can use `.override` with # overrides of builtins. In the future we could try to distinguish when the method is a # builtin and treat non-builtins as non-overridable (so you'd be forced to declare them with # `.overridable`). # super_signature ||= Methods::Signature.new_untyped(method: super_method) validate_override_mode(signature, super_signature) validate_override_shape(signature, super_signature) validate_override_types(signature, super_signature) end else validate_non_override_mode(signature) end end |
.validate_non_override_mode(signature) ⇒ Object
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/types/private/methods/signature_validation.rb', line 89 def self.validate_non_override_mode(signature) case signature.mode when Modes.override raise "You marked `#{signature.method_name}` as #{pretty_mode(signature)}, but that method doesn't already exist in this class/module to be overriden.\n" \ " Either check for typos and for missing includes or super classes to make the parent method shows up\n" \ " ... or remove #{pretty_mode(signature)} here: #{method_loc_str(signature.method)}\n" when Modes.standard, *Modes::NON_OVERRIDE_MODES # Peaceful nil when *Modes::IMPLEMENT_MODES raise "You marked `#{signature.method_name}` as #{pretty_mode(signature)}, but it doesn't match up with a corresponding abstract method.\n" \ " Either check for typos and for missing includes or super classes to make the parent method shows up\n" \ " ... or remove #{pretty_mode(signature)} here: #{method_loc_str(signature.method)}\n" else raise "Unexpected mode: #{signature.mode}. Please report to #dev-productivity." end owner = signature.method.owner if (signature.mode == Modes.abstract || Modes::OVERRIDABLE_MODES.include?(signature.mode)) && owner.singleton_class? # Given a singleton class, we can check if it belongs to a # module by looking at its superclass; given `module M`, # `M.singleton_class.superclass == Module`, which is not true # for any class. if owner.superclass == Module raise "Defining an overridable class method (via #{pretty_mode(signature)}) " \ "on a module is not allowed. Class methods on " \ "modules do not get inherited and thus cannot be overridden. For help, ask in " \ "#dev-productivity." end end end |
.validate_override_mode(signature, super_signature) ⇒ Object
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/types/private/methods/signature_validation.rb', line 58 def self.validate_override_mode(signature, super_signature) case signature.mode when *Modes::OVERRIDE_MODES if !Modes::OVERRIDABLE_MODES.include?(super_signature.mode) raise "You declared `#{signature.method_name}` as #{pretty_mode(signature)}, but the method it overrides is not declared as `overridable`.\n" \ " Parent definition: #{method_loc_str(super_signature.method)}\n" \ " Child definition: #{method_loc_str(signature.method)}\n" end when *Modes::IMPLEMENT_MODES if super_signature.mode != Modes.abstract raise "You declared `#{signature.method_name}` as #{pretty_mode(signature)}, but the method it overrides is not declared as abstract.\n" \ " Either mark #{super_signature.method_name} as `abstract.` in the parent: #{method_loc_str(super_signature.method)}\n" \ " ... or mark #{signature.method_name} as `override.` in the child: #{method_loc_str(signature.method)}\n" end when *Modes::NON_OVERRIDE_MODES if super_signature.mode == Modes.standard # Peaceful elsif super_signature.mode == Modes.abstract raise "You must use `.implementation` when overriding the abstract method `#{signature.method_name}`.\n" \ " Abstract definition: #{method_loc_str(super_signature.method)}\n" \ " Implementation definition: #{method_loc_str(signature.method)}\n" elsif super_signature.mode != Modes.untyped raise "You must use `.override` when overriding the existing method `#{signature.method_name}`.\n" \ " Parent definition: #{method_loc_str(super_signature.method)}\n" \ " Child definition: #{method_loc_str(signature.method)}\n" end else raise "Unexpected mode: #{signature.mode}. Please report to #dev-productivity." end end |
.validate_override_shape(signature, super_signature) ⇒ Object
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/types/private/methods/signature_validation.rb', line 122 def self.validate_override_shape(signature, super_signature) return if signature.override_allow_incompatible return if super_signature.mode == Modes.untyped method_name = signature.method_name mode_verb = super_signature.mode == Modes.abstract ? 'implements' : 'overrides' if !signature.has_rest && signature.arg_count < super_signature.arg_count raise "Your definition of `#{method_name}` must accept at least #{super_signature.arg_count} " \ "positional arguments to be compatible with the method it #{mode_verb}: " \ "#{base_override_loc_str(signature, super_signature)}" end if !signature.has_rest && super_signature.has_rest raise "Your definition of `#{method_name}` must have `*#{super_signature.rest_name}` " \ "to be compatible with the method it #{mode_verb}: " \ "#{base_override_loc_str(signature, super_signature)}" end if signature.req_arg_count > super_signature.req_arg_count raise "Your definition of `#{method_name}` must have no more than #{super_signature.req_arg_count} " \ "required argument(s) to be compatible with the method it #{mode_verb}: " \ "#{base_override_loc_str(signature, super_signature)}" end if !signature.has_keyrest # O(nm), but n and m are tiny here missing_kwargs = super_signature.kwarg_names - signature.kwarg_names if !missing_kwargs.empty? raise "Your definition of `#{method_name}` is missing these keyword arg(s): #{missing_kwargs} " \ "which are defined in the method it #{mode_verb}: " \ "#{base_override_loc_str(signature, super_signature)}" end end if !signature.has_keyrest && super_signature.has_keyrest raise "Your definition of `#{method_name}` must have `**#{super_signature.keyrest_name}` " \ "to be compatible with the method it #{mode_verb}: " \ "#{base_override_loc_str(signature, super_signature)}" end # O(nm), but n and m are tiny here extra_req_kwargs = signature.req_kwarg_names - super_signature.req_kwarg_names if !extra_req_kwargs.empty? raise "Your definition of `#{method_name}` has extra required keyword arg(s) " \ "#{extra_req_kwargs} relative to the method it #{mode_verb}, making it incompatible: " \ "#{base_override_loc_str(signature, super_signature)}" end if super_signature.block_name && !signature.block_name raise "Your definition of `#{method_name}` must accept a block parameter to be compatible " \ "with the method it #{mode_verb}: " \ "#{base_override_loc_str(signature, super_signature)}" end end |
.validate_override_types(signature, super_signature) ⇒ Object
179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
# File 'lib/types/private/methods/signature_validation.rb', line 179 def self.validate_override_types(signature, super_signature) return if signature.override_allow_incompatible return if super_signature.mode == Modes.untyped return unless [signature, super_signature].all? do |sig| sig.check_level == :always || (sig.check_level == :tests && T::Private::RuntimeLevels.check_tests?) end mode_noun = super_signature.mode == Modes.abstract ? 'implementation' : 'override' # arg types must be contravariant super_signature.arg_types.zip(signature.arg_types).each_with_index do |((_super_name, super_type), (name, type)), index| if !super_type.subtype_of?(type) raise "Incompatible type for arg ##{index + 1} (`#{name}`) in #{mode_noun} of method " \ "`#{signature.method_name}`:\n" \ "* Base: `#{super_type}` (in #{method_loc_str(super_signature.method)})\n" \ "* #{mode_noun.capitalize}: `#{type}` (in #{method_loc_str(signature.method)})\n" \ "(The types must be contravariant.)" end end # kwarg types must be contravariant super_signature.kwarg_types.each do |name, super_type| type = signature.kwarg_types[name] if !super_type.subtype_of?(type) raise "Incompatible type for arg `#{name}` in #{mode_noun} of method `#{signature.method_name}`:\n" \ "* Base: `#{super_type}` (in #{method_loc_str(super_signature.method)})\n" \ "* #{mode_noun.capitalize}: `#{type}` (in #{method_loc_str(signature.method)})\n" \ "(The types must be contravariant.)" end end # return types must be covariant if !signature.return_type.subtype_of?(super_signature.return_type) raise "Incompatible return type in #{mode_noun} of method `#{signature.method_name}`:\n" \ "* Base: `#{super_signature.return_type}` (in #{method_loc_str(super_signature.method)})\n" \ "* #{mode_noun.capitalize}: `#{signature.return_type}` (in #{method_loc_str(signature.method)})\n" \ "(The types must be covariant.)" end end |