class RuboCop::Cop::Style::TernaryParentheses
foo = (bar = baz) ? a : b
# bad
@example AllowSafeAssignment: false
foo = (bar = baz) ? a : b
# good
@example AllowSafeAssignment: true (default)
foo = (bar && baz) ? a : b
foo = bar.baz? ? a : b
foo = bar? ? a : b
# good
foo = bar && baz ? a : b
foo = (bar.baz?) ? a : b
foo = (bar?) ? a : b
# bad
@example EnforcedStyle: require_parentheses_when_complex
foo = (bar && baz) ? a : b
foo = (bar.baz?) ? a : b
foo = (bar?) ? a : b
# good
foo = bar && baz ? a : b
foo = bar.baz? ? a : b
foo = bar? ? a : b
# bad
@example EnforcedStyle: require_parentheses
foo = bar && baz ? a : b
foo = bar.baz? ? a : b
foo = bar? ? a : b
# good
foo = (bar && baz) ? a : b
foo = (bar.baz?) ? a : b
foo = (bar?) ? a : b
# bad
@example EnforcedStyle: require_no_parentheses (default)
as a condition. It’s not a mistake.“
an assignment to indicate ”I know I’m using an assignment
By safe assignment we mean putting parentheses around
`AllowSafeAssignment` option for safe assignment.
removing the parentheses won’t cause a different behavior.
parentheses using ‘EnforcedStyle`. Omission is only enforced when
conditions. It is configurable to enforce inclusion or omission of
This cop checks for the presence of parentheses around ternary
def autocorrect(corrector, node)
def autocorrect(corrector, node) condition = node.condition return nil if parenthesized?(condition) && (safe_assignment?(condition) || unsafe_autocorrect?(condition)) if parenthesized?(condition) correct_parenthesized(corrector, condition) else correct_unparenthesized(corrector, condition) end end
def below_ternary_precedence?(child)
def below_ternary_precedence?(child) # Handle English "or", e.g. 'foo or bar ? a : b' (child.or_type? && child.semantic_operator?) || # Handle English "and", e.g. 'foo and bar ? a : b' (child.and_type? && child.semantic_operator?) || # Handle English "not", e.g. 'not foo ? a : b' (child.send_type? && child.prefix_not?) end
def complex_condition?(condition)
If the condition is parenthesized we recurse and check for any
def complex_condition?(condition) if condition.begin_type? condition.to_a.any? { |x| complex_condition?(x) } else !non_complex_expression?(condition) end end
def correct_parenthesized(corrector, condition)
def correct_parenthesized(corrector, condition) corrector.remove(condition.loc.begin) corrector.remove(condition.loc.end) # Ruby allows no space between the question mark and parentheses. # If we remove the parentheses, we need to add a space or we'll # generate invalid code. corrector.insert_after(condition.loc.end, ' ') unless whitespace_after?(condition) end
def correct_unparenthesized(corrector, condition)
def correct_unparenthesized(corrector, condition) corrector.wrap(condition, '(', ')') end
def infinite_loop?
`RedundantParentheses` cop is enabled, it will cause an infinite loop
When this cop is configured to enforce parentheses and the
def infinite_loop? (require_parentheses? || require_parentheses_when_complex?) && redundant_parentheses_enabled? end
def message(node)
def message(node) if require_parentheses_when_complex? command = parenthesized?(node.condition) ? 'Only use' : 'Use' format(MSG_COMPLEX, command: command) else command = require_parentheses? ? 'Use' : 'Omit' format(MSG, command: command) end end
def non_complex_expression?(condition)
Anything that is not a variable, constant, or method/.method call
def non_complex_expression?(condition) NON_COMPLEX_TYPES.include?(condition.type) || non_complex_send?(condition) end
def non_complex_send?(node)
def non_complex_send?(node) return false unless node.call_type? !node.operator_method? || node.method?(:[]) end
def offense?(node)
def offense?(node) condition = node.condition if safe_assignment?(condition) !safe_assignment_allowed? else parens = parenthesized?(condition) case style when :require_parentheses_when_complex complex_condition?(condition) ? !parens : parens else require_parentheses? ? !parens : parens end end end
def on_if(node)
def on_if(node) return if only_closing_parenthesis_is_last_line?(node.condition) return unless node.ternary? && !infinite_loop? && offense?(node) message = message(node) add_offense(node.source_range, message: message) do |corrector| autocorrect(corrector, node) end end
def only_closing_parenthesis_is_last_line?(condition)
def only_closing_parenthesis_is_last_line?(condition) condition.source.split("\n").last == ')' end
def parenthesized?(node)
def parenthesized?(node) node.begin_type? end
def redundant_parentheses_enabled?
def redundant_parentheses_enabled? @config.for_cop('Style/RedundantParentheses').fetch('Enabled') end
def require_parentheses?
def require_parentheses? style == :require_parentheses end
def require_parentheses_when_complex?
def require_parentheses_when_complex? style == :require_parentheses_when_complex end
def unparenthesized_method_call?(child)
def unparenthesized_method_call?(child) /^[a-z]/i.match?(method_name(child)) && !child.parenthesized? end
def unsafe_autocorrect?(condition)
def unsafe_autocorrect?(condition) condition.children.any? do |child| unparenthesized_method_call?(child) || below_ternary_precedence?(child) end end
def whitespace_after?(node)
def whitespace_after?(node) last_token = processed_source.last_token_of(node) last_token.space_after? end