Refactor ternary handling in Style/RedundantParentheses rule

This commit is contained in:
Sijawusz Pur Rahnama 2022-11-15 00:46:21 +01:00
parent 3340613a6a
commit 935296b041
2 changed files with 30 additions and 15 deletions

View file

@ -55,16 +55,22 @@ module Ameba::Rule::Style
rule.exclude_ternary = false rule.exclude_ternary = false
expect_issue rule, <<-CRYSTAL expect_issue rule, <<-CRYSTAL
(foo > bar) ? true : false (foo.empty?) ? true : false
# ^^^^^^^^^ error: Redundant parentheses # ^^^^^^^^^^ error: Redundant parentheses
CRYSTAL CRYSTAL
expect_no_issues subject, <<-CRYSTAL expect_no_issues subject, <<-CRYSTAL
(foo && bar) ? true : false (foo && bar) ? true : false
CRYSTAL
expect_no_issues subject, <<-CRYSTAL
(foo || bar) ? true : false (foo || bar) ? true : false
(foo = @foo) ? true : false
foo == 42 ? true : false
(foo = 42) ? true : false
(foo > 42) ? true : false
(foo >= 42) ? true : false
(3 >= foo >= 42) ? true : false
(3.in? 0..42) ? true : false
(yield 42) ? true : false
(foo rescue 42) ? true : false
CRYSTAL CRYSTAL
end end
end end

View file

@ -22,35 +22,44 @@ module Ameba::Rule::Style
# ``` # ```
# Style/RedundantParentheses: # Style/RedundantParentheses:
# Enabled: true # Enabled: true
# ExcludeTernary: true # ExcludeTernary: false
# ExcludeAssignments: false # ExcludeAssignments: false
# ``` # ```
class RedundantParentheses < Base class RedundantParentheses < Base
properties do properties do
description "Disallows redundant parentheses around control expressions" description "Disallows redundant parentheses around control expressions"
exclude_ternary true exclude_ternary false
exclude_assignments false exclude_assignments false
end end
MSG = "Redundant parentheses" MSG = "Redundant parentheses"
protected def strip_parentheses?(node, in_ternary) : Bool
case node
when Crystal::BinaryOp, Crystal::ExceptionHandler
!in_ternary
when Crystal::Call
!in_ternary || node.has_parentheses? || node.args.empty?
when Crystal::Yield
!in_ternary || node.has_parentheses? || node.exps.empty?
when Crystal::Assign, Crystal::OpAssign, Crystal::MultiAssign
!in_ternary && !exclude_assignments
else
true
end
end
def test(source, node : Crystal::If | Crystal::Unless | Crystal::Case | Crystal::While | Crystal::Until) def test(source, node : Crystal::If | Crystal::Unless | Crystal::Case | Crystal::While | Crystal::Until)
is_ternary = node.is_a?(Crystal::If) && node.ternary? is_ternary = node.is_a?(Crystal::If) && node.ternary?
return if exclude_ternary && is_ternary return if is_ternary && exclude_ternary
return unless (cond = node.cond).is_a?(Crystal::Expressions) return unless (cond = node.cond).is_a?(Crystal::Expressions)
return unless cond.keyword.paren? return unless cond.keyword.paren?
return unless exp = cond.single_expression? return unless exp = cond.single_expression?
return unless strip_parentheses?(exp, is_ternary)
case exp
when Crystal::BinaryOp
return if is_ternary
when Crystal::Assign, Crystal::OpAssign
return if exclude_assignments
end
issue_for cond, MSG do |corrector| issue_for cond, MSG do |corrector|
corrector.remove_trailing(cond, 1) corrector.remove_trailing(cond, 1)