lib/rubocop/cop/lint/disjunctive_assignment_in_constructor.rb
# frozen_string_literal: true
module RuboCop
module Cop
module Lint
# Checks constructors for disjunctive assignments (`||=`) that should
# be plain assignments.
#
# So far, this cop is only concerned with disjunctive assignment of
# instance variables.
#
# In ruby, an instance variable is nil until a value is assigned, so the
# disjunction is unnecessary. A plain assignment has the same effect.
#
# @safety
# This cop is unsafe because it can register a false positive when a
# method is redefined in a subclass that calls super. For example:
#
# [source,ruby]
# ----
# class Base
# def initialize
# @config ||= 'base'
# end
# end
#
# class Derived < Base
# def initialize
# @config = 'derived'
# super
# end
# end
# ----
#
# Without the disjunctive assignment, `Derived` will be unable to override
# the value for `@config`.
#
# @example
# # bad
# def initialize
# @x ||= 1
# end
#
# # good
# def initialize
# @x = 1
# end
class DisjunctiveAssignmentInConstructor < Base
extend AutoCorrector
MSG = 'Unnecessary disjunctive assignment. Use plain assignment.'
def on_def(node)
check(node)
end
private
# @param [DefNode] node a constructor definition
def check(node)
return unless node.method?(:initialize)
check_body(node.body)
end
def check_body(body)
return if body.nil?
case body.type
when :begin
check_body_lines(body.child_nodes)
else
check_body_lines([body])
end
end
# @param [Array] lines the logical lines of the constructor
def check_body_lines(lines)
lines.each do |line|
case line.type
when :or_asgn
check_disjunctive_assignment(line)
else
# Once we encounter something other than a disjunctive
# assignment, we cease our investigation, because we can't be
# certain that any future disjunctive assignments are offensive.
# You're off the case, detective!
break
end
end
end
# Add an offense if the LHS of the given disjunctive assignment is
# an instance variable.
#
# For now, we only care about assignments to instance variables.
#
# @param [Node] node a disjunctive assignment
def check_disjunctive_assignment(node)
lhs = node.child_nodes.first
return unless lhs.ivasgn_type?
add_offense(node.loc.operator) do |corrector|
corrector.replace(node.loc.operator, '=')
end
end
end
end
end
end