class RuboCop::Cop::Lint::NumberConversion

Warns the usage of unsafe number conversions. Unsafe number conversion can cause unexpected error if auto type conversion fails. Cop prefer parsing with number class instead.

Conversion with ‘Integer`, `Float`, etc. will raise an `ArgumentError` if given input that is not numeric (eg. an empty string), whereas `to_i`, etc. will try to convert regardless of input (`”.to_i => 0`). As such, this cop is disabled by default because it’s not necessarily always correct to raise if a value is not numeric.

NOTE: Some values cannot be converted properly using one of the ‘Kernel` method (for instance, `Time` and `DateTime` values are allowed by this cop by default). Similarly, Rails’ duration methods do not work well with ‘Integer()` and can be allowed with `AllowedMethods`. By default, there are no methods to allowed.

@safety

Autocorrection is unsafe because it is not guaranteed that the
replacement `Kernel` methods are able to properly handle the
input if it is not a standard class.

@example

# bad

'10'.to_i
'10.2'.to_f
'10'.to_c
'1/3'.to_r
['1', '2', '3'].map(&:to_i)
foo.try(:to_f)
bar.send(:to_c)

# good

Integer('10', 10)
Float('10.2')
Complex('10')
Rational('1/3')
['1', '2', '3'].map { |i| Integer(i, 10) }
foo.try { |i| Float(i) }
bar.send { |i| Complex(i) }

@example AllowedMethods: [] (default)

# bad
10.minutes.to_i

@example AllowedMethods: [minutes]

# good
10.minutes.to_i

@example AllowedPatterns: [] (default)

# bad
10.minutes.to_i

@example AllowedPatterns: [‘min*’]

# good
10.minutes.to_i

@example IgnoredClasses: [Time, DateTime] (default)

# good
Time.now.to_datetime.to_i

Constants

CONVERSION_METHODS
CONVERSION_METHOD_CLASS_MAPPING
METHODS
MSG

Public Instance Methods

on_send(node) click to toggle source
# File lib/rubocop/cop/lint/number_conversion.rb, line 102
def on_send(node)
  handle_conversion_method(node)
  handle_as_symbol(node)
end

Private Instance Methods

allow_receiver?(receiver) click to toggle source
# File lib/rubocop/cop/lint/number_conversion.rb, line 155
def allow_receiver?(receiver)
  if receiver.numeric_type? || (receiver.send_type? &&
    (conversion_method?(receiver.method_name) ||
    allowed_method_name?(receiver.method_name)))
    true
  elsif (receiver = top_receiver(receiver))
    receiver.const_type? && ignored_class?(receiver.const_name)
  else
    false
  end
end
allowed_method_name?(name) click to toggle source
# File lib/rubocop/cop/lint/number_conversion.rb, line 167
def allowed_method_name?(name)
  allowed_method?(name) || matches_allowed_pattern?(name)
end
conversion_method?(method_name) click to toggle source
# File lib/rubocop/cop/lint/number_conversion.rb, line 177
def conversion_method?(method_name)
  CONVERSION_METHODS.include?(method_name)
end
correct_method(node, receiver) click to toggle source
# File lib/rubocop/cop/lint/number_conversion.rb, line 141
def correct_method(node, receiver)
  format(CONVERSION_METHOD_CLASS_MAPPING[node.method_name], number_object: receiver.source)
end
correct_sym_method(to_method) click to toggle source
# File lib/rubocop/cop/lint/number_conversion.rb, line 145
def correct_sym_method(to_method)
  body = format(CONVERSION_METHOD_CLASS_MAPPING[to_method], number_object: 'i')
  "{ |i| #{body} }"
end
handle_as_symbol(node) click to toggle source
# File lib/rubocop/cop/lint/number_conversion.rb, line 124
def handle_as_symbol(node)
  to_method_symbol(node) do |receiver, sym_node, to_method|
    next if receiver.nil? || !node.arguments.one?

    message = format(
      MSG,
      current: sym_node.source,
      corrected_method: correct_sym_method(to_method)
    )
    add_offense(node, message: message) do |corrector|
      remove_parentheses(corrector, node) if node.parenthesized?

      corrector.replace(sym_node, correct_sym_method(to_method))
    end
  end
end
handle_conversion_method(node) click to toggle source
# File lib/rubocop/cop/lint/number_conversion.rb, line 109
def handle_conversion_method(node)
  to_method(node) do |receiver, to_method|
    next if receiver.nil? || allow_receiver?(receiver)

    message = format(
      MSG,
      current: "#{receiver.source}.#{to_method}",
      corrected_method: correct_method(node, receiver)
    )
    add_offense(node, message: message) do |corrector|
      corrector.replace(node, correct_method(node, node.receiver))
    end
  end
end
ignored_class?(name) click to toggle source
# File lib/rubocop/cop/lint/number_conversion.rb, line 185
def ignored_class?(name)
  ignored_classes.include?(name.to_s)
end
ignored_classes() click to toggle source
# File lib/rubocop/cop/lint/number_conversion.rb, line 181
def ignored_classes
  cop_config.fetch('IgnoredClasses', [])
end
remove_parentheses(corrector, node) click to toggle source
# File lib/rubocop/cop/lint/number_conversion.rb, line 150
def remove_parentheses(corrector, node)
  corrector.replace(node.loc.begin, ' ')
  corrector.remove(node.loc.end)
end
top_receiver(node) click to toggle source
# File lib/rubocop/cop/lint/number_conversion.rb, line 171
def top_receiver(node)
  receiver = node
  receiver = receiver.receiver until receiver.receiver.nil?
  receiver
end