123456789_123456789_123456789_123456789_123456789_

Module: RuboCop::Cop::Style::MethodCallWithArgsParentheses::OmitParentheses

Relationships & Source Files
Extension / Inclusion / Inheritance Descendants
Included In:
Super Chains via Extension / Inclusion / Inheritance
Instance Chain:
Defined in: lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb

Overview

Style omit_parentheses rubocop:disable-next Metrics/ModuleLength, Metrics/CyclomaticComplexity

Constant Summary

::RuboCop::Cop::RangeHelp - Included

BYTE_ORDER_MARK, NOT_GIVEN

::RuboCop::Cop::ReparsedEquivalence - Included

MAX_VERIFICATION_FRAGMENT_SIZE

Instance Method Summary

::RuboCop::Cop::ReparsedEquivalence - Included

#corrected_scope_fragment

The corrections' edits are all contained within the scope, so the corrected fragment can be cut out of the corrected source by adjusting for the edits' length delta.

#correction_parses?

Whether the exact correction for item produces source that still parses, without requiring an equivalent AST.

#corrections_verify?, #item_range,
#normalize_reparsed_ast

Hook: loosen strict tree equality for corrections that are equivalence-preserving beyond parse identity.

#normalized_original,
#parses_equivalently?

Both sides are parsed with the original path so that FILE resolves identically.

#preprocess_reparsed_source

Hook: rewrite both sides before parsing.

#reparse_scope

The innermost scope that both contains range and parses standalone.

#scope_groups, #verification_too_large?,
#verified_by_reparse

Returns the items whose corrections are verified.

#verified_subset

Verifies a group with a single reparse and bisects it when that fails, so a few significant items among many cost a logarithmic number of reparses rather than one each.

::RuboCop::Cop::RangeHelp - Included

#add_range,
#arguments_range

A range containing the first to the last argument of a method call or method definition.

#column_offset_between,
#contents_range

A range containing only the contents of a literal with delimiters (e.g.

#directions,
#effective_column

Returns the column attribute of the range, except if the range is on the first line and there’s a byte order mark at the beginning of that line, in which case 1 is subtracted from the column value.

#final_pos

rubocop:disable-next Metrics/ParameterLists.

#move_pos, #move_pos_str, #range_between, #range_by_whole_lines, #range_with_comments, #range_with_comments_and_lines, #range_with_surrounding_comma, #range_with_surrounding_space, #source_range

Instance Method Details

#allowed_camel_case_method_call?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 100

def allowed_camel_case_method_call?(node)
  node.camel_case_method? &&
    (node.arguments.none? || cop_config['AllowParenthesesInCamelCaseMethod'])
end

#allowed_chained_call_with_parentheses?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 235

def allowed_chained_call_with_parentheses?(node)
  return false unless cop_config['AllowParenthesesInChaining']

  previous = node.descendants.first
  return false unless previous&.send_type?

  previous.parenthesized? || allowed_chained_call_with_parentheses?(previous)
end

#allowed_multiline_call_with_parentheses?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 231

def allowed_multiline_call_with_parentheses?(node)
  cop_config['AllowParenthesesInMultilineCall'] && node.multiline?
end

#allowed_string_interpolation_method_call?(node) ⇒ Boolean (private)

[ GitHub ]

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

def allowed_string_interpolation_method_call?(node)
  cop_config['AllowParenthesesInStringInterpolation'] &&
    inside_string_interpolation?(node)
end

#ambiguous_literal?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 244

def ambiguous_literal?(node)
  splat?(node) || ternary_if?(node) || regexp_slash_literal?(node) || unary_literal?(node)
end

#ambiguous_range_argument?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 224

def ambiguous_range_argument?(node)
  return true if (first_arg = node.first_argument)&.range_type? && first_arg.begin.nil?
  return true if (last_arg = node.last_argument)&.range_type? && last_arg.end.nil?

  false
end

#apply_reparse_correction(corrector, node) (private)

[ GitHub ]

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

def apply_reparse_correction(corrector, node)
  autocorrect(corrector, node)
end

#assigned_before?(node, target) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 283

def assigned_before?(node, target)
  node.assignment? && node.loc.operator.begin < target.source_range.begin
end

#assignment_in_condition?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 291

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

  grandparent = parent.parent
  return false unless grandparent

  parent.assignment? && (grandparent.conditional? || grandparent.when_type?)
end

#autocorrect(corrector, node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 54

def autocorrect(corrector, node)
  range = args_begin(node)
  if parentheses_at_the_end_of_multiline_call?(node)
    # Whitespace after line continuation (`\ `) is a syntax error
    with_whitespace = range_with_surrounding_space(range, side: :right, newlines: false)
    corrector.replace(with_whitespace, ' \\')
  else
    corrector.replace(range, ' ')
  end
  corrector.remove(node.loc.end)
end

#call_as_argument_or_chain?(node) ⇒ Boolean (private)

[ GitHub ]

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

def call_as_argument_or_chain?(node)
  node.parent&.type?(:call, :super, :yield) &&
    !assigned_before?(node.parent, node)
end

#call_in_argument_with_block?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 199

def call_in_argument_with_block?(node)
  parent = node.parent&.any_block_type? && node.parent.parent
  return false unless parent

  parent.type?(:call, :super, :yield)
end

#call_in_literals?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 151

def call_in_literals?(node)
  parent = node.parent&.any_block_type? ? node.parent.parent : node.parent
  return false unless parent

  parent.type?(:pair, :array, :range) ||
    splat?(parent) ||
    ternary_if?(parent)
end

#call_in_logical_operators?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 160

def call_in_logical_operators?(node)
  parent = node.parent&.any_block_type? ? node.parent.parent : node.parent
  return false unless parent

  if logical_operator?(parent) || command_assignment_before_keyword_operator?(parent)
    return true
  end

  parent.send_type? && parent.arguments.any? do |argument|
    logical_operator?(argument)
  end
end

#call_in_match_pattern?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 211

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

  parent.any_match_pattern_type?
end

#call_in_optional_arguments?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 173

def call_in_optional_arguments?(node)
  node.parent&.type?(:optarg, :kwoptarg)
end

#call_in_single_line_inheritance?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 177

def call_in_single_line_inheritance?(node)
  node.parent&.class_type? && node.parent.single_line?
end

#call_with_ambiguous_arguments?(node) ⇒ Boolean (private)

rubocop:disable-next Metrics/PerceivedComplexity

[ GitHub ]

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

def call_with_ambiguous_arguments?(node)
  call_with_braced_block?(node) ||
    call_in_argument_with_block?(node) ||
    call_as_argument_or_chain?(node) ||
    call_in_match_pattern?(node) ||
    hash_literal_in_arguments?(node) ||
    ambiguous_range_argument?(node) ||
    node.descendants.any? do |n|
      n.type?(:forwarded_args, :any_block) ||
        ambiguous_literal?(n) || logical_operator?(n)
    end
end

#call_with_braced_block?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 195

def call_with_braced_block?(node)
  node.type?(:call, :super) && node.block_node&.braces?
end

#code_after_closing_parenthesis_on_its_own_line?(node) ⇒ Boolean (private)

[ GitHub ]

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

def code_after_closing_parenthesis_on_its_own_line?(node)
  closing = node.loc.end
  return false if node.arguments.none? || node.last_argument.last_line == closing.line

  rest = closing.source_line[(closing.column + 1)..].strip

  !rest.empty? && !rest.start_with?('#')
end

#command_assignment_before_keyword_operator?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 260

def command_assignment_before_keyword_operator?(node)
  return false unless node.assignment?

  node = node.parent while node.parent&.assignment?
  return false unless (operator = node.parent)

  operator.operator_keyword? && operator.semantic_operator?
end

#command_assignment_in_expression?(node) ⇒ Boolean (private)

rubocop:disable-next Metrics/PerceivedComplexity

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 302

def command_assignment_in_expression?(node)
  value = node.parent&.type?(:any_block, :rescue) ? node.parent : node
  assignment = outermost_assignment(value)
  return false if assignment.equal?(value)

  call_in_literals?(assignment) ||
    call_in_logical_operators?(assignment) ||
    call_in_optional_arguments?(assignment) ||
    call_as_argument_or_chain?(assignment) ||
    assignment.parent&.type?(:masgn, :return, :next, :break, :defined?)
end

#comment_before_first_argument?(node) ⇒ Boolean (private)

Without the parentheses, a comment before the first argument would end the call there, and a line continuation can’t carry the call past a comment.

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 121

def comment_before_first_argument?(node)
  return false unless (first_argument = node.first_argument)

  lines = node.loc.begin.line...first_argument.first_line
  processed_source.each_comment_in_lines(lines).any?
end

#forwards_anonymous_rest_arguments?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 322

def forwards_anonymous_rest_arguments?(node)
  node.arguments.any? do |argument|
    next true if argument.forwarded_restarg_type?

    argument.hash_type? && argument.children.any?(&:forwarded_kwrestarg_type?)
  end
end

#hash_literal?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 269

def hash_literal?(node)
  node.hash_type? && node.braces?
end

#hash_literal_in_arguments?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 217

def hash_literal_in_arguments?(node)
  node.arguments.any? do |n|
    hash_literal?(n) ||
      (n.send_type? && node.descendants.any? { |descendant| hash_literal?(descendant) })
  end
end

#inside_endless_method_def?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 70

def inside_endless_method_def?(node)
  # parens are required around arguments inside an endless method
  node.each_ancestor(:any_def).any?(&:endless?) && node.arguments.any?
end

#inside_string_interpolation?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 287

def inside_string_interpolation?(node)
  node.ancestors.drop_while { |a| !a.begin_type? }.any?(&:dstr_type?)
end

#last_expression?(node) ⇒ Boolean (private)

Require hash value omission be enclosed in parentheses to prevent the following issue: https://bugs.ruby-lang.org/issues/18396.

[ GitHub ]

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

def last_expression?(node)
  !(node.parent&.assignment? ? node.parent.right_sibling : node.right_sibling)
end

#legitimate_call_with_parentheses?(node) ⇒ Boolean (private)

Metrics/PerceivedComplexity

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 136

def legitimate_call_with_parentheses?(node) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
  call_in_literals?(node) ||
    node.parent&.when_type? ||
    call_with_ambiguous_arguments?(node) ||
    call_in_logical_operators?(node) ||
    call_in_optional_arguments?(node) ||
    call_in_single_line_inheritance?(node) ||
    allowed_multiline_call_with_parentheses?(node) ||
    allowed_chained_call_with_parentheses?(node) ||
    assignment_in_condition?(node) ||
    command_assignment_in_expression?(node) ||
    forwards_anonymous_rest_arguments?(node) ||
    trailing_comma?(node)
end

#logical_operator?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 256

def logical_operator?(node)
  node.operator_keyword? && node.logical_operator?
end

#method_call_before_constant_resolution?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 92

def method_call_before_constant_resolution?(node)
  node.parent&.const_type?
end

#offense_range(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 66

def offense_range(node)
  node.loc.begin.join(node.loc.end)
end

#omit_parentheses(node) (private)

Metrics/PerceivedComplexity

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 33

def omit_parentheses(node) # rubocop:disable Metrics/AbcSize, Metrics/PerceivedComplexity
  return unless node.parenthesized?
  return if inside_endless_method_def?(node)
  return if require_parentheses_for_hash_value_omission?(node)
  return if syntax_like_method_call?(node)
  return if method_call_before_constant_resolution?(node)
  return if super_call_without_arguments?(node)
  return if legitimate_call_with_parentheses?(node)
  return if allowed_camel_case_method_call?(node)
  return if allowed_string_interpolation_method_call?(node)
  return if code_after_closing_parenthesis_on_its_own_line?(node)
  return if comment_before_first_argument?(node)
  return if args_parenthesized?(node)

  (@pending_omit_offenses ||= []) << node
end

#on_investigation_end

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 17

def on_investigation_end
  # Each candidate's exact correction is verified by reparsing before
  # the offense is registered, so an omission that would change how
  # the code parses is never reported or offered.
  verified_by_reparse(@pending_omit_offenses || []).each do |node|
    add_offense(offense_range(node), message: OMIT_MSG) do |corrector|
      autocorrect(corrector, node)
    end
  end
  @pending_omit_offenses = []

  super
end

#outermost_assignment(value) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 314

def outermost_assignment(value)
  parent = value.parent
  return value if !parent&.assignment? || parent.masgn_type?
  return value unless assigned_before?(parent, value)

  outermost_assignment(parent)
end

#parentheses_at_the_end_of_multiline_call?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 128

def parentheses_at_the_end_of_multiline_call?(node)
  return false unless node.multiline?

  opening = node.loc.begin

  opening.source_line[(opening.column + 1)..].gsub(TRAILING_WHITESPACE_REGEX, '').empty?
end

#regexp_slash_literal?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 273

def regexp_slash_literal?(node)
  node.regexp_type? && node.loc.begin.source == '/'
end

#require_parentheses_for_hash_value_omission?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 75

def require_parentheses_for_hash_value_omission?(node) # rubocop:disable Metrics/PerceivedComplexity
  return false unless (last_argument = node.last_argument)
  return false if !last_argument.hash_type? || !last_argument.pairs.last&.value_omission?

  node.parent&.conditional? || node.parent&.single_line? || !last_expression?(node)
end

#splat?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 248

def splat?(node)
  node.type?(:splat, :kwsplat, :block_pass)
end

#super_call_without_arguments?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 96

def super_call_without_arguments?(node)
  node.super_type? && node.arguments.none?
end

#syntax_like_method_call?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 88

def syntax_like_method_call?(node)
  node.implicit_call? || node.operator_method?
end

#ternary_if?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 252

def ternary_if?(node)
  node.if_type? && node.ternary?
end

#trailing_comma?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 330

def trailing_comma?(node)
  return false unless (last_argument = node.last_argument)

  range = last_argument.source_range.end.join(node.loc.end.begin)

  processed_source.tokens_within(range).any?(&:comma?)
end

#unary_literal?(node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb', line 277

def unary_literal?(node)
  return true if node.numeric_type? && node.sign?

  node.parent&.send_type? && node.parent.unary_operation?
end