class RuboCop::Cop::Lint::RedundantTypeConversion
Checks for redundant uses of ‘to_s`, `to_sym`, `to_i`, `to_f`, `to_d`, `to_r`, `to_c`, `to_a`, `to_h`, and `to_set`.
When one of these methods is called on an object of the same type, that object is returned, making the call unnecessary. The cop detects conversion methods called on object literals, class constructors, class ‘[]` methods, and the `Kernel` methods `String()`, `Integer()`, `Float()`, BigDecimal(), `Rational()`, `Complex()`, and `Array()`.
Specifically, these cases are detected for each conversion method:
-
‘to_s` when called on a string literal, interpolated string, heredoc, or with `String.new` or `String()`.
-
‘to_sym` when called on a symbol literal or interpolated symbol.
-
‘to_i` when called on an integer literal or with `Integer()`.
-
‘to_f` when called on a float literal of with `Float()`.
-
‘to_r` when called on a rational literal or with `Rational()`.
-
‘to_c` when called on a complex literal of with `Complex()`.
-
‘to_a` when called on an array literal, or with `Array.new`, `Array()` or `Array[]`.
-
‘to_h` when called on a hash literal, or with `Hash.new`, `Hash()` or `Hash[]`.
-
‘to_set` when called on `Set.new` or `Set[]`.
In all cases, chaining one same ‘to_*` conversion methods listed above is redundant.
The cop can also register an offense for chaining conversion methods on methods that are expected to return a specific type regardless of receiver (eg. ‘foo.inspect.to_s` and `foo.to_json.to_s`).
@example
# bad "text".to_s :sym.to_sym 42.to_i 8.5.to_f 12r.to_r 1i.to_c [].to_a {}.to_h Set.new.to_set # good "text" :sym 42 8.5 12r 1i [] {} Set.new # bad Integer(var).to_i # good Integer(var) # good - chaining to a type constructor with exceptions suppressed # in this case, `Integer()` could return `nil` Integer(var, exception: false).to_i # bad - chaining the same conversion foo.to_s.to_s # good foo.to_s # bad - chaining a conversion to a method that is expected to return the same type foo.inspect.to_s foo.to_json.to_s # good foo.inspect foo.to_json
Constants
- CONSTRUCTOR_MAPPING
-
Maps each conversion method to the pattern matcher for that type’s constructors Not every type has a constructor, for instance Symbol.
- CONVERSION_METHODS
- LITERAL_NODE_TYPES
-
Maps conversion methods to the node types for the literals of that type
- MSG
- RESTRICT_ON_SEND
- TYPED_METHODS
-
Methods that already are expected to return a given type, which makes a further conversion redundant.
Public Instance Methods
Source
# File lib/rubocop/cop/lint/redundant_type_conversion.rb, line 189 def on_send(node) return if node.arguments.any? || hash_or_set_with_block?(node) receiver = find_receiver(node) return unless literal_receiver?(node, receiver) || constructor?(node, receiver) || chained_conversion?(node, receiver) || chained_to_typed_method?(node, receiver) message = format(MSG, method: node.method_name) add_offense(node.loc.selector, message: message) do |corrector| corrector.remove(node.loc.dot.join(node.loc.end || node.loc.selector)) end end
rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
Private Instance Methods
Source
# File lib/rubocop/cop/lint/redundant_type_conversion.rb, line 247 def chained_conversion?(node, receiver) return false unless receiver&.call_type? receiver.method?(node.method_name) end
Source
# File lib/rubocop/cop/lint/redundant_type_conversion.rb, line 253 def chained_to_typed_method?(node, receiver) return false unless receiver&.call_type? TYPED_METHODS.fetch(node.method_name, []).include?(receiver.method_name) end
Source
# File lib/rubocop/cop/lint/redundant_type_conversion.rb, line 234 def constructor?(node, receiver) matcher = CONSTRUCTOR_MAPPING[node.method_name] return false unless matcher public_send(matcher, receiver) && !constructor_suppresses_exceptions?(receiver) end
Source
# File lib/rubocop/cop/lint/redundant_type_conversion.rb, line 241 def constructor_suppresses_exceptions?(receiver) # If the constructor suppresses exceptions with `exception: false`, it is possible # it could return `nil`, and therefore a chained conversion is not redundant. receiver.arguments.any? { |arg| exception_false_keyword_argument?(arg) } end
Source
# File lib/rubocop/cop/lint/redundant_type_conversion.rb, line 215 def find_receiver(node) receiver = node.receiver return unless receiver while receiver.begin_type? break unless receiver.children.one? receiver = receiver.children.first end receiver end
Source
# File lib/rubocop/cop/lint/redundant_type_conversion.rb, line 209 def hash_or_set_with_block?(node) return false if !node.method?(:to_h) && !node.method?(:to_set) node.parent&.any_block_type? || node.last_argument&.block_pass_type? end
Source
# File lib/rubocop/cop/lint/redundant_type_conversion.rb, line 228 def literal_receiver?(node, receiver) return false unless receiver receiver.type?(*LITERAL_NODE_TYPES[node.method_name]) end