Class: RuboCop::Cop::DevDoc::Style::StringSymbolComparison

Inherits:
Base
  • Object
show all
Defined in:
lib/rubocop/cop/dev_doc/style/string_symbol_comparison.rb

Overview

Flag direct ‘==` / `!=` comparisons between a known-string source and a symbol literal. Such comparisons are silently always false (`’draft’ == :draft` is ‘false` in Ruby), so they signal that the caller forgot to convert at the boundary.

## Rationale Strings and symbols are not equal in Ruby, and string-typed values arrive from boundaries the developer doesn’t control: HTTP params, request headers, ENV. Comparing one of these directly against a symbol literal is a guaranteed bug. Convert at the boundary instead — see ‘best_practices/backend/en/01a_defensive_programming.md` item 7.

❌
if params[:status] == :draft   # always false

✔ Convert at the boundary, then compare
@filter_status = params[:status]&.to_sym
if @filter_status == :draft

## Sources detected

## Limitations The cop only catches the direct comparison form. Once the value flows into a local or instance variable, type information is lost and Rubocop cannot trace it back to the original string source. Those cases are caught by review.

Examples:

# bad
params[:status] == :draft
:production == ENV['MODE']
request.headers['X-Mode'] != :debug

# good
params[:status]&.to_sym == :draft
ENV['MODE'] == 'production'

Constant Summary collapse

MSG =
'Comparing string-typed `%<source>s` to a symbol literal is always false. ' \
'Convert at the boundary with `&.to_sym` or compare against a string instead ' \
'(see backend/01a_defensive_programming.md item 7).'.freeze
RESTRICT_ON_SEND =
%i[== !=].freeze

Instance Method Summary collapse

Instance Method Details

#on_send(node) ⇒ Object



63
64
65
66
67
68
69
70
71
72
# File 'lib/rubocop/cop/dev_doc/style/string_symbol_comparison.rb', line 63

def on_send(node)
  lhs = node.receiver
  rhs = node.first_argument
  return unless lhs && rhs

  source_node = pick_string_source(lhs, rhs)
  return unless source_node

  add_offense(node, message: format(MSG, source: source_node.source))
end