123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::Style::RedundantParentheses

Relationships & Source Files
Super Chains via Extension / Inclusion / Inheritance
Class Chain:
self, ::RuboCop::Cop::AutoCorrector, ::RuboCop::Cop::Base, ::RuboCop::ExcludeLimit, NodePattern::Macros, RuboCop::AST::Sexp
Instance Chain:
Inherits: RuboCop::Cop::Base
Defined in: lib/rubocop/cop/style/redundant_parentheses.rb

Overview

Checks for redundant parentheses.

Examples:

# bad
(x) if ((y.z).nil?)

# good
x if y.z.nil?

Constant Summary

::RuboCop::Cop::Base - Inherited

EMPTY_OFFENSES, RESTRICT_ON_SEND

Class Attribute Summary

::RuboCop::Cop::AutoCorrector - Extended

::RuboCop::Cop::Base - Inherited

.gem_requirements, .lint?,
.support_autocorrect?

Returns if class supports autocorrect.

.support_multiple_source?

Override if your cop should be called repeatedly for multiple investigations Between calls to on_new_investigation and on_investigation_end, the result of processed_source will remain constant.

Class Method Summary

::RuboCop::Cop::Base - Inherited

.autocorrect_incompatible_with

List of cops that should not try to autocorrect at the same time as this cop.

.badge

Naming.

.callbacks_needed, .cop_name, .department,
.documentation_url

Returns a url to view this cops documentation online.

.exclude_from_registry

Call for abstract Cop classes.

.inherited,
.joining_forces

Override and return the Force class(es) you need to join.

.match?

Returns true if the cop name or the cop namespace matches any of the given names.

.new,
.requires_gem

Register a version requirement for the given gem name.

.restrict_on_send

::RuboCop::ExcludeLimit - Extended

exclude_limit

Sets up a configuration option to have an exclude limit tracked.

transform

Instance Attribute Summary

Instance Method Summary

::RuboCop::Cop::Parentheses - Included

::RuboCop::Cop::Base - Inherited

#add_global_offense

Adds an offense that has no particular location.

#add_offense

Adds an offense on the specified range (or node with an expression) Unless that offense is disabled for this range, a corrector will be yielded to provide the cop the opportunity to autocorrect the offense.

#begin_investigation

Called before any investigation.

#callbacks_needed,
#cop_config

Configuration Helpers.

#cop_name, #excluded_file?,
#external_dependency_checksum

This method should be overridden when a cop’s behavior depends on state that lives outside of these locations:

#inspect,
#message

Gets called if no message is specified when calling add_offense or add_global_offense Cops are discouraged to override this; instead pass your message directly.

#name

Alias for Base#cop_name.

#offenses,
#on_investigation_end

Called after all on_…​

#on_new_investigation

Called before all on_…​

#on_other_file

Called instead of all on_…​

#parse

There should be very limited reasons for a Cop to do it’s own parsing.

#parser_engine,
#ready

Called between investigations.

#relevant_file?,
#target_gem_version

Returns a gems locked versions (i.e.

#target_rails_version, #target_ruby_version, #annotate, #apply_correction, #attempt_correction,
#callback_argument

Reserved for Cop::Cop.

#complete_investigation

Called to complete an investigation.

#correct, #current_corrector,
#current_offense_locations

Reserved for Commissioner:

#current_offenses, #currently_disabled_lines, #custom_severity, #default_severity, #disable_uncorrectable, #enabled_line?, #file_name_matches_any?, #find_message, #find_severity, #range_for_original, #range_from_node_or_range,
#reset_investigation

Actually private methods.

#use_corrector

::RuboCop::Cop::AutocorrectLogic - Included

::RuboCop::Cop::IgnoredNode - Included

Constructor Details

This class inherits a constructor from RuboCop::Cop::Base

Instance Attribute Details

#allow_in_multiline_conditions?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 168

def allow_in_multiline_conditions?
  !!config.for_enabled_cop('Style/ParenthesesAroundCondition')['AllowInMultilineConditions']
end

#ternary_parentheses_required?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 84

def ternary_parentheses_required?
  config = @config.for_cop('Style/TernaryParentheses')
  allowed_styles = %w[require_parentheses require_parentheses_when_complex]

  config.fetch('Enabled') && allowed_styles.include?(config['EnforcedStyle'])
end

Instance Method Details

#allowed_ancestor?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 64

def allowed_ancestor?(node)
  # Don't flag `break(1)`, etc
  keyword_ancestor?(node) && parens_required?(node)
end

#allowed_expression?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 57

def allowed_expression?(node)
  allowed_ancestor?(node) ||
    allowed_multiple_expression?(node) ||
    allowed_ternary?(node) ||
    node.parent&.range_type?
end

#allowed_multiple_expression?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 69

def allowed_multiple_expression?(node)
  return false if node.children.one?

  ancestor = node.ancestors.first
  return false unless ancestor

  !ancestor.begin_type? && !ancestor.def_type? && !ancestor.block_type?
end

#allowed_pin_operator?(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 32

def_node_matcher :allowed_pin_operator?, '^(pin (begin !{lvar ivar cvar gvar}))'

#allowed_ternary?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 78

def allowed_ternary?(node)
  return false unless node&.parent&.if_type?

  node.parent.ternary? && ternary_parentheses_required?
end

#call_chain_starts_with_int?(begin_node, send_node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 272

def call_chain_starts_with_int?(begin_node, send_node)
  recv = first_part_of_call_chain(send_node)
  recv&.int_type? && (parent = begin_node.parent) &&
    parent.send_type? && (parent.method?(:-@) || parent.method?(:+@))
end

#check(begin_node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 127

def check(begin_node)
  node = begin_node.children.first

  if (message = find_offense_message(begin_node, node))
    return offense(begin_node, message)
  end

  check_send(begin_node, node) if node.call_type?
end

#check_send(begin_node, node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 172

def check_send(begin_node, node)
  return check_unary(begin_node, node) if node.unary_operation?

  return unless method_call_with_redundant_parentheses?(node)
  return if call_chain_starts_with_int?(begin_node, node) ||
            do_end_block_in_method_chain?(begin_node, node)

  offense(begin_node, 'a method call')
end

#check_unary(begin_node, node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 182

def check_unary(begin_node, node)
  return if begin_node.chained?

  node = node.children.first while suspect_unary?(node)

  return if node.send_type? && !method_call_with_redundant_parentheses?(node)

  offense(begin_node, 'a unary operation')
end

#disallowed_literal?(begin_node, node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 206

def disallowed_literal?(begin_node, node)
  node.literal? && !node.range_type? && !raised_to_power_negative_numeric?(begin_node, node)
end

#do_end_block_in_method_chain?(begin_node, node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 278

def do_end_block_in_method_chain?(begin_node, node)
  return false unless (block = node.each_descendant(:block, :numblock).first)

  block.keywords? && begin_node.each_ancestor(:send, :csend).any?
end

#empty_parentheses?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 105

def empty_parentheses?(node)
  # Don't flag `()`
  node.children.empty?
end

#find_offense_message(begin_node, node) (private)

Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 138

def find_offense_message(begin_node, node)
  return 'a keyword' if keyword_with_redundant_parentheses?(node)
  return 'a literal' if disallowed_literal?(begin_node, node)
  return 'a variable' if node.variable?
  return 'a constant' if node.const_type?
  if node.lambda_or_proc? && (node.braces? || node.send_node.lambda_literal?)
    return 'an expression'
  end
  return 'an interpolated expression' if interpolation?(begin_node)

  return if begin_node.chained?

  if node.operator_keyword?
    return if node.semantic_operator? && begin_node.parent
    return if node.multiline? && allow_in_multiline_conditions?
    return if ALLOWED_NODE_TYPES.include?(begin_node.parent&.type)
    return if begin_node.parent&.if_type? && begin_node.parent.ternary?

    'a logical expression'
  elsif node.respond_to?(:comparison_method?) && node.comparison_method?
    return unless begin_node.parent.nil?

    'a comparison expression'
  end
end

#first_arg_begins_with_hash_literal?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 110

def first_arg_begins_with_hash_literal?(node)
  # Don't flag `method ({key: value})` or `method ({key: value}.method)`
  hash_literal = method_chain_begins_with_hash_literal(node.children.first)
  if (root_method = node.each_ancestor(:send).to_a.last)
    parenthesized = root_method.parenthesized_call?
  end
  hash_literal && first_argument?(node) && !parentheses?(hash_literal) && !parenthesized
end

#first_argument?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 247

def first_argument?(node)
  if first_send_argument?(node) ||
     first_super_argument?(node) ||
     first_yield_argument?(node)
    return true
  end

  node.each_ancestor.any? { |ancestor| first_argument?(ancestor) }
end

#first_send_argument?(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 258

def_node_matcher :first_send_argument?, <<~PATTERN
  ^(send _ _ equal?(%0) ...)
PATTERN

#first_super_argument?(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 263

def_node_matcher :first_super_argument?, <<~PATTERN
  ^(super equal?(%0) ...)
PATTERN

#first_yield_argument?(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 268

def_node_matcher :first_yield_argument?, <<~PATTERN
  ^(yield equal?(%0) ...)
PATTERN

#ignore_syntax?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 50

def ignore_syntax?(node)
  return false unless (parent = node.parent)

  parent.while_post_type? || parent.until_post_type? || parent.match_with_lvasgn_type? ||
    like_method_argument_parentheses?(parent) || multiline_control_flow_statements?(node)
end

#interpolation?(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 166

def_node_matcher :interpolation?, '[^begin ^^dstr]'

#keyword_ancestor?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 202

def keyword_ancestor?(node)
  node.parent&.keyword?
end

#keyword_with_redundant_parentheses?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 221

def keyword_with_redundant_parentheses?(node)
  return false unless node.keyword?
  return true if node.special_keyword?

  args = *node

  if only_begin_arg?(args)
    parentheses?(args.first)
  else
    args.empty? || parentheses?(node)
  end
end

#like_method_argument_parentheses?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 91

def like_method_argument_parentheses?(node)
  return false if !node.send_type? && !node.super_type? && !node.yield_type?

  node.arguments.one? && !node.parenthesized? &&
    !node.arithmetic_operation? && node.first_argument.begin_type?
end

#method_call_with_redundant_parentheses?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 234

def method_call_with_redundant_parentheses?(node)
  return false unless node.call_type?
  return false if node.prefix_not?

  send_node, args = method_node_and_args(node)

  args.empty? || parentheses?(send_node) || square_brackets?(send_node)
end

#method_chain_begins_with_hash_literal(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 119

def method_chain_begins_with_hash_literal(node)
  return if node.nil?
  return node if node.hash_type?
  return unless node.send_type?

  method_chain_begins_with_hash_literal(node.children.first)
end

#method_node_and_args(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 26

def_node_matcher :method_node_and_args, '$(call _recv _msg $...)'

#multiline_control_flow_statements?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 98

def multiline_control_flow_statements?(node)
  return false unless (parent = node.parent)
  return false if parent.single_line?

  parent.return_type? || parent.next_type? || parent.break_type?
end

#offense(node, msg) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 192

def offense(node, msg)
  add_offense(node, message: "Don't use parentheses around #{msg}.") do |corrector|
    ParenthesesCorrector.correct(corrector, node)
  end
end

#on_begin(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 34

def on_begin(node)
  return if !parentheses?(node) || parens_allowed?(node) || ignore_syntax?(node)

  check(node)
end

#only_begin_arg?(args) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 243

def only_begin_arg?(args)
  args.one? && args.first&.begin_type?
end

#parens_allowed?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 42

def parens_allowed?(node)
  empty_parentheses?(node) ||
    first_arg_begins_with_hash_literal?(node) ||
    rescue?(node) ||
    allowed_pin_operator?(node) ||
    allowed_expression?(node)
end

#raised_to_power_negative_numeric?(begin_node, node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 210

def raised_to_power_negative_numeric?(begin_node, node)
  return false unless node.numeric_type?

  next_sibling = begin_node.right_sibling
  return false unless next_sibling

  base_value = node.children.first

  base_value.negative? && next_sibling == :**
end

#rescue?(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 29

def_node_matcher :rescue?, '{^resbody ^^resbody}'

#square_brackets?(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 23

def_node_matcher :square_brackets?, '(send {(send _recv _msg) str array hash} :[] ...)'

#suspect_unary?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/redundant_parentheses.rb', line 198

def suspect_unary?(node)
  node.send_type? && node.unary_operation? && !node.prefix_not?
end