lib/rubocop/cop/style/guard_clause.rb in rubocop-1.37.1 vs lib/rubocop/cop/style/guard_clause.rb in rubocop-1.38.0
- old
+ new
@@ -8,10 +8,13 @@
#
# A condition with an `elsif` or `else` branch is allowed unless
# one of `return`, `break`, `next`, `raise`, or `fail` is used
# in the body of the conditional expression.
#
+ # NOTE: Autocorrect works in most cases except with if-else statements
+ # that contain logical operators such as `foo || raise('exception')`
+ #
# @example
# # bad
# def test
# if something
# work
@@ -88,10 +91,11 @@
# work
# end
# end
#
class GuardClause < Base
+ extend AutoCorrector
include MinBodyLength
include StatementModifier
MSG = 'Use a guard clause (`%<example>s`) instead of wrapping the ' \
'code inside a conditional expression.'
@@ -99,73 +103,110 @@
def on_def(node)
body = node.body
return unless body
- if body.if_type?
- check_ending_if(body)
- elsif body.begin_type?
- final_expression = body.children.last
- check_ending_if(final_expression) if final_expression&.if_type?
- end
+ check_ending_body(body)
end
alias on_defs on_def
def on_if(node)
return if accepted_form?(node)
- guard_clause_in_if = node.if_branch&.guard_clause?
- guard_clause_in_else = node.else_branch&.guard_clause?
- guard_clause = guard_clause_in_if || guard_clause_in_else
- return unless guard_clause
+ if (guard_clause = node.if_branch&.guard_clause?)
+ kw = node.loc.keyword.source
+ guard = :if
+ elsif (guard_clause = node.else_branch&.guard_clause?)
+ kw = node.inverse_keyword
+ guard = :else
+ else
+ return
+ end
- kw = if guard_clause_in_if
- node.loc.keyword.source
- else
- node.inverse_keyword
- end
+ guard = nil if and_or_guard_clause?(guard_clause)
- register_offense(node, guard_clause_source(guard_clause), kw)
+ register_offense(node, guard_clause_source(guard_clause), kw, guard)
end
private
+ def check_ending_body(body)
+ return if body.nil?
+
+ if body.if_type?
+ check_ending_if(body)
+ elsif body.begin_type?
+ final_expression = body.children.last
+ check_ending_if(final_expression) if final_expression&.if_type?
+ end
+ end
+
def check_ending_if(node)
return if accepted_form?(node, ending: true) || !min_body_length?(node)
return if allowed_consecutive_conditionals? &&
consecutive_conditionals?(node.parent, node)
register_offense(node, 'return', node.inverse_keyword)
+
+ check_ending_body(node.if_branch)
end
def consecutive_conditionals?(parent, node)
parent.each_child_node.inject(false) do |if_type, child|
break if_type if node == child
child.if_type?
end
end
- def register_offense(node, scope_exiting_keyword, conditional_keyword)
+ def register_offense(node, scope_exiting_keyword, conditional_keyword, guard = nil)
condition, = node.node_parts
example = [scope_exiting_keyword, conditional_keyword, condition.source].join(' ')
if too_long_for_single_line?(node, example)
return if trivial?(node)
example = "#{conditional_keyword} #{condition.source}; #{scope_exiting_keyword}; end"
+ replacement = <<~RUBY.chomp
+ #{conditional_keyword} #{condition.source}
+ #{scope_exiting_keyword}
+ end
+ RUBY
end
- add_offense(node.loc.keyword, message: format(MSG, example: example))
+ add_offense(node.loc.keyword, message: format(MSG, example: example)) do |corrector|
+ next if node.else? && guard.nil?
+
+ autocorrect(corrector, node, condition, replacement || example, guard)
+ end
end
- def guard_clause_source(guard_clause)
- parent = guard_clause.parent
+ def autocorrect(corrector, node, condition, replacement, guard)
+ corrector.replace(node.loc.keyword.join(condition.loc.expression), replacement)
+ corrector.remove(node.loc.end)
+ return unless node.else?
- if parent.and_type? || parent.or_type?
+ corrector.remove(node.loc.else)
+ corrector.remove(branch_to_remove(node, guard))
+ end
+
+ def branch_to_remove(node, guard)
+ case guard
+ when :if then node.if_branch
+ when :else then node.else_branch
+ end
+ end
+
+ def guard_clause_source(guard_clause)
+ if and_or_guard_clause?(guard_clause)
guard_clause.parent.source
else
guard_clause.source
end
+ end
+
+ def and_or_guard_clause?(guard_clause)
+ parent = guard_clause.parent
+ parent.and_type? || parent.or_type?
end
def too_long_for_single_line?(node, example)
max = max_line_length
max && node.source_range.column + example.length > max