Class: T::Private::Methods::Signature
- Inherits:
-
Object
- Object
- T::Private::Methods::Signature
- Defined in:
- lib/types/private/methods/signature.rb
Overview
typed: true
Constant Summary collapse
- EMPTY_HASH =
{}.freeze
Instance Attribute Summary collapse
-
#arg_types ⇒ Object
readonly
Returns the value of attribute arg_types.
-
#bind ⇒ Object
readonly
Returns the value of attribute bind.
-
#block_name ⇒ Object
readonly
Returns the value of attribute block_name.
-
#block_type ⇒ Object
readonly
Returns the value of attribute block_type.
-
#check_level ⇒ Object
readonly
Returns the value of attribute check_level.
-
#defined_raw ⇒ Object
readonly
Returns the value of attribute defined_raw.
-
#has_keyrest ⇒ Object
readonly
Returns the value of attribute has_keyrest.
-
#has_rest ⇒ Object
readonly
Returns the value of attribute has_rest.
-
#keyrest_name ⇒ Object
readonly
Returns the value of attribute keyrest_name.
-
#keyrest_type ⇒ Object
readonly
Returns the value of attribute keyrest_type.
-
#kwarg_types ⇒ Object
readonly
Returns the value of attribute kwarg_types.
-
#method ⇒ Object
readonly
Returns the value of attribute method.
-
#method_name ⇒ Object
readonly
Returns the value of attribute method_name.
-
#mode ⇒ Object
readonly
Returns the value of attribute mode.
-
#on_failure ⇒ Object
readonly
Returns the value of attribute on_failure.
-
#override_allow_incompatible ⇒ Object
readonly
Returns the value of attribute override_allow_incompatible.
-
#parameters ⇒ Object
readonly
Returns the value of attribute parameters.
-
#req_arg_count ⇒ Object
readonly
Returns the value of attribute req_arg_count.
-
#req_kwarg_names ⇒ Object
readonly
Returns the value of attribute req_kwarg_names.
-
#rest_name ⇒ Object
readonly
Returns the value of attribute rest_name.
-
#rest_type ⇒ Object
readonly
Returns the value of attribute rest_type.
-
#return_type ⇒ Object
readonly
Returns the value of attribute return_type.
Class Method Summary collapse
Instance Method Summary collapse
- #arg_count ⇒ Object
- #as_alias(alias_name) ⇒ Object
- #dsl_method ⇒ Object
-
#each_args_value_type(args) ⇒ Hash
A mapping like ‘[val, type], …`, for only those args actually present.
-
#initialize(method:, method_name:, raw_arg_types:, raw_return_type:, bind:, mode:, check_level:, on_failure:, parameters: method.parameters, override_allow_incompatible: false, defined_raw: false) ⇒ Signature
constructor
A new instance of Signature.
- #kwarg_names ⇒ Object
- #method_desc ⇒ Object
- #owner ⇒ Object
Constructor Details
#initialize(method:, method_name:, raw_arg_types:, raw_return_type:, bind:, mode:, check_level:, on_failure:, parameters: method.parameters, override_allow_incompatible: false, defined_raw: false) ⇒ Signature
Returns a new instance of Signature.
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 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 88 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 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 |
# File 'lib/types/private/methods/signature.rb', line 36 def initialize(method:, method_name:, raw_arg_types:, raw_return_type:, bind:, mode:, check_level:, on_failure:, parameters: method.parameters, override_allow_incompatible: false, defined_raw: false) @method = method @method_name = method_name @arg_types = [] @kwarg_types = {} @block_type = nil @block_name = nil @rest_type = nil @rest_name = nil @keyrest_type = nil @keyrest_name = nil @return_type = T::Utils.coerce(raw_return_type) @bind = bind ? T::Utils.coerce(bind) : bind @mode = mode @check_level = check_level @req_arg_count = 0 @req_kwarg_names = [] @has_rest = false @has_keyrest = false @parameters = parameters @on_failure = on_failure @override_allow_incompatible = override_allow_incompatible @defined_raw = defined_raw declared_param_names = raw_arg_types.keys # If sig params are declared but there is a single parameter with a missing name # **and** the method ends with a "=", assume it is a writer method generated # by attr_writer or attr_accessor writer_method = declared_param_names != [nil] && parameters == [[:req]] && method_name[-1] == "=" # For writer methods, map the single parameter to the method name without the "=" at the end parameters = [[:req, method_name[0...-1].to_sym]] if writer_method param_names = parameters.map {|_, name| name} missing_names = param_names - declared_param_names extra_names = declared_param_names - param_names if !missing_names.empty? raise "The declaration for `#{method.name}` is missing parameter(s): #{missing_names.join(', ')}" end if !extra_names.empty? raise "The declaration for `#{method.name}` has extra parameter(s): #{extra_names.join(', ')}" end if parameters.size != raw_arg_types.size raise "The declaration for `#{method.name}` has arguments with duplicate names" end parameters.zip(raw_arg_types) do |(param_kind, param_name), (type_name, raw_type)| if type_name != param_name hint = "" # Ruby reorders params so that required keyword arguments # always precede optional keyword arguments. We can't tell # whether the culprit is the Ruby reordering or user error, so # we error but include a note if param_kind == :keyreq && parameters.any? {|k, _| k == :key} hint = "\n\nNote: Any required keyword arguments must precede any optional keyword " \ "arguments. If your method declaration matches your `def`, try reordering any " \ "optional keyword parameters to the end of the method list." end raise "Parameter `#{type_name}` is declared out of order (declared as arg number " \ "#{declared_param_names.index(type_name) + 1}, defined in the method as arg number " \ "#{param_names.index(type_name) + 1}).#{hint}\nMethod: #{method_desc}" end type = T::Utils.coerce(raw_type) case param_kind when :req if @arg_types.length > @req_arg_count # Note that this is actually is supported by Ruby, but it would add complexity to # support it here, and I'm happy to discourage its use anyway. # # If you are seeing this error and surprised by it, it's possible that you have # overridden the method described in the error message. For example, Rails defines # def self.update!(id = :all, attributes) # on AR models. If you have also defined `self.update!` on an AR model you might # see this error. The simplest resolution is to rename your method. raise "Required params after optional params are not supported in method declarations. Method: #{method_desc}" end @arg_types << [param_name, type] @req_arg_count += 1 when :opt @arg_types << [param_name, type] when :key, :keyreq @kwarg_types[param_name] = type if param_kind == :keyreq @req_kwarg_names << param_name end when :block @block_name = param_name @block_type = type when :rest @has_rest = true @rest_name = param_name @rest_type = type when :keyrest @has_keyrest = true @keyrest_name = param_name @keyrest_type = type else raise "Unexpected param_kind: `#{param_kind}`. Method: #{method_desc}" end end end |
Instance Attribute Details
#arg_types ⇒ Object (readonly)
Returns the value of attribute arg_types.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def arg_types @arg_types end |
#bind ⇒ Object (readonly)
Returns the value of attribute bind.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def bind @bind end |
#block_name ⇒ Object (readonly)
Returns the value of attribute block_name.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def block_name @block_name end |
#block_type ⇒ Object (readonly)
Returns the value of attribute block_type.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def block_type @block_type end |
#check_level ⇒ Object (readonly)
Returns the value of attribute check_level.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def check_level @check_level end |
#defined_raw ⇒ Object (readonly)
Returns the value of attribute defined_raw.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def defined_raw @defined_raw end |
#has_keyrest ⇒ Object (readonly)
Returns the value of attribute has_keyrest.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def has_keyrest @has_keyrest end |
#has_rest ⇒ Object (readonly)
Returns the value of attribute has_rest.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def has_rest @has_rest end |
#keyrest_name ⇒ Object (readonly)
Returns the value of attribute keyrest_name.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def keyrest_name @keyrest_name end |
#keyrest_type ⇒ Object (readonly)
Returns the value of attribute keyrest_type.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def keyrest_type @keyrest_type end |
#kwarg_types ⇒ Object (readonly)
Returns the value of attribute kwarg_types.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def kwarg_types @kwarg_types end |
#method ⇒ Object (readonly)
Returns the value of attribute method.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def method @method end |
#method_name ⇒ Object
Returns the value of attribute method_name.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def method_name @method_name end |
#mode ⇒ Object (readonly)
Returns the value of attribute mode.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def mode @mode end |
#on_failure ⇒ Object (readonly)
Returns the value of attribute on_failure.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def on_failure @on_failure end |
#override_allow_incompatible ⇒ Object (readonly)
Returns the value of attribute override_allow_incompatible.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def override_allow_incompatible @override_allow_incompatible end |
#parameters ⇒ Object (readonly)
Returns the value of attribute parameters.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def parameters @parameters end |
#req_arg_count ⇒ Object (readonly)
Returns the value of attribute req_arg_count.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def req_arg_count @req_arg_count end |
#req_kwarg_names ⇒ Object (readonly)
Returns the value of attribute req_kwarg_names.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def req_kwarg_names @req_kwarg_names end |
#rest_name ⇒ Object (readonly)
Returns the value of attribute rest_name.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def rest_name @rest_name end |
#rest_type ⇒ Object (readonly)
Returns the value of attribute rest_type.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def rest_type @rest_type end |
#return_type ⇒ Object (readonly)
Returns the value of attribute return_type.
5 6 7 |
# File 'lib/types/private/methods/signature.rb', line 5 def return_type @return_type end |
Class Method Details
.new_untyped(method:, mode: T::Private::Methods::Modes.untyped, parameters: method.parameters) ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/types/private/methods/signature.rb', line 11 def self.new_untyped(method:, mode: T::Private::Methods::Modes.untyped, parameters: method.parameters) # Using `Untyped` ensures we'll get an error if we ever try validation on these. not_typed = T::Private::Types::NotTyped.new raw_return_type = not_typed # Map missing parameter names to "argN" positionally parameters = parameters.each_with_index.map do |(param_kind, param_name), index| [param_kind, param_name || "arg#{index}"] end raw_arg_types = parameters.map do |_param_kind, param_name| [param_name, not_typed] end.to_h self.new( method: method, method_name: method.name, raw_arg_types: raw_arg_types, raw_return_type: raw_return_type, bind: nil, mode: mode, check_level: :never, parameters: parameters, on_failure: nil, ) end |
Instance Method Details
#arg_count ⇒ Object
149 150 151 |
# File 'lib/types/private/methods/signature.rb', line 149 def arg_count @arg_types.length end |
#as_alias(alias_name) ⇒ Object
143 144 145 146 147 |
# File 'lib/types/private/methods/signature.rb', line 143 def as_alias(alias_name) new_sig = clone new_sig.method_name = alias_name new_sig end |
#dsl_method ⇒ Object
161 162 163 |
# File 'lib/types/private/methods/signature.rb', line 161 def dsl_method "#{@mode}_method" end |
#each_args_value_type(args) ⇒ Hash
Returns a mapping like ‘[val, type], …`, for only those args actually present.
166 167 168 169 170 171 172 173 174 175 176 177 178 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 |
# File 'lib/types/private/methods/signature.rb', line 166 def each_args_value_type(args) # Manually split out args and kwargs based on ruby's behavior. Do not try to implement this by # getting ruby to determine the kwargs for you (e.g., by defining this method to take *args and # **kwargs). That won't work, because ruby's behavior for determining kwargs is dependent on the # the other parameters in the method definition, and our method definition here doesn't (and # can't) match the definition of the method we're validating. In addition, Ruby has a bug that # causes forwarding **kwargs to do the wrong thing: see https://bugs.ruby-lang.org/issues/10708 # and https://bugs.ruby-lang.org/issues/11860. args_length = args.length if (args_length > @req_arg_count) && (!@kwarg_types.empty? || @has_keyrest) && args[-1].is_a?(Hash) kwargs = args[-1] args_length -= 1 else kwargs = EMPTY_HASH end arg_types = @arg_types if @has_rest rest_count = args_length - @arg_types.length rest_count = 0 if rest_count.negative? arg_types += [[@rest_name, @rest_type]] * rest_count elsif (args_length < @req_arg_count) || (args_length > @arg_types.length) expected_str = @req_arg_count.to_s if @arg_types.length != @req_arg_count expected_str += "..#{@arg_types.length}" end raise ArgumentError.new("wrong number of arguments (given #{args_length}, expected #{expected_str})") end begin it = 0 while it < args_length yield arg_types[it][0], args[it], arg_types[it][1] it += 1 end end kwargs.each do |name, val| type = @kwarg_types[name] if !type && @has_keyrest type = @keyrest_type end yield name, val, type if type end end |
#kwarg_names ⇒ Object
153 154 155 |
# File 'lib/types/private/methods/signature.rb', line 153 def kwarg_names @kwarg_types.keys end |
#method_desc ⇒ Object
215 216 217 218 219 220 221 222 |
# File 'lib/types/private/methods/signature.rb', line 215 def method_desc loc = if @method.source_location @method.source_location.join(':') else "<unknown location>" end "#{@method} at #{loc}" end |
#owner ⇒ Object
157 158 159 |
# File 'lib/types/private/methods/signature.rb', line 157 def owner @method.owner end |