123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::Layout::MultilineMethodCallIndentation

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/layout/multiline_method_call_indentation.rb

Overview

Checks the indentation of the method name part in method calls that span more than one line.

Examples:

EnforcedStyle: aligned (default)

# bad
while myvariable
.b
  # do something
end

# good
while myvariable
      .b
  # do something
end

# good
Thing.a
     .b
     .c

EnforcedStyle: indented

# good
while myvariable
  .b

  # do something
end

EnforcedStyle: indented_relative_to_receiver

# good
while myvariable
        .a
        .b

  # do something
end

# good
myvariable = Thing
               .a
               .b
               .c

Constant Summary

::RuboCop::Cop::Base - Inherited

EMPTY_OFFENSES, RESTRICT_ON_SEND

::RuboCop::Cop::ConfigurableEnforcedStyle - Included

SYMBOL_TO_STRING_CACHE

::RuboCop::Cop::Alignment - Included

SPACE

::RuboCop::Cop::MultilineExpressionIndentation - Included

ASSIGNMENT_MESSAGE_TAIL, DEFAULT_MESSAGE_TAIL, KEYWORD_ANCESTOR_TYPES, KEYWORD_MESSAGE_TAIL, UNALIGNED_RHS_TYPES

::RuboCop::Cop::RangeHelp - Included

BYTE_ORDER_MARK, NOT_GIVEN

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::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, #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

::RuboCop::Cop::MultilineExpressionIndentation - Included

#on_csend
#on_send,
#argument_in_method_call

rubocop:todo Metrics/CyclomaticComplexity.

#assignment_rhs, #check,
#correct_indentation

The correct indentation of node is usually IndentationWidth, with one exception: prefix keywords.

#disqualified_rhs?, #grouped_expression?, #incorrect_style_detected, #indentation, #indented_keyword_expression, #inside_arg_list_parentheses?, #keyword_message_tail, #kw_node_with_special_indentation,
#left_hand_side

In a chain of method calls, we regard the top call node as the base for indentation of all lines following the first.

#not_for_this_cop?, #operation_description, #part_of_assignment_rhs, #part_of_block_body?,
#postfix_conditional?

Returns true if node is a conditional whose body and condition begin on the same line.

#valid_method_rhs_candidate?

The []= operator and setters (a.b = c) are parsed as :send nodes.

#valid_rhs?, #valid_rhs_candidate?, #within_node?

::RuboCop::Cop::Alignment - Included

::RuboCop::Cop::ConfigurableEnforcedStyle - 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

#should_align_with_base?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 210

def should_align_with_base?
  @base && style == :aligned
end

#should_indent_relative_to_receiver?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 206

def should_indent_relative_to_receiver?
  @base && style == :indented_relative_to_receiver
end

Instance Method Details

#align_with_base_message(rhs) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 219

def align_with_base_message(rhs)
  "Align `#{rhs.source}` with `#{base_source}` on line #{@base.line}."
end

#alignment_base(node, rhs, given_style) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 241

def alignment_base(node, rhs, given_style)
  case given_style
  when :aligned
    semantic_alignment_base(node, rhs) || syntactic_alignment_base(node, rhs)
  when :indented_relative_to_receiver
    receiver_alignment_base(node)
  end
end

#alignment_base_for_chained_receiver?(receiver_chain, base_receiver) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 300

def alignment_base_for_chained_receiver?(receiver_chain, base_receiver)
  base_receiver&.hash_type? ||
    method_on_receiver_last_line?(receiver_chain, base_receiver, :begin)
end

#autocorrect(corrector, node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 83

def autocorrect(corrector, node)
  if @send_node.block_node
    correct_selector_only(corrector, node)
    correct_block(corrector, @send_node.block_node)
  else
    AlignmentCorrector.correct(corrector, processed_source, node, @column_delta)
  end
end

#base_source (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 223

def base_source
  @base.source[/[^\n]*/]
end

#calculate_column_delta_offense(rhs, correct_column) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 181

def calculate_column_delta_offense(rhs, correct_column)
  @column_delta = correct_column - rhs.column
  rhs if @column_delta.nonzero?
end

#check_hash_pair_indentation(node, lhs, rhs) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 154

def check_hash_pair_indentation(node, lhs, rhs)
  @base = find_hash_pair_alignment_base(node) || lhs.source_range

  calculate_column_delta_offense(rhs, @base.column)
end

#check_hash_pair_indented_style(rhs, pair_ancestor) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 145

def check_hash_pair_indented_style(rhs, pair_ancestor)
  pair_key = pair_ancestor.key
  double_indentation = configured_indentation_width * 2
  correct_column = pair_key.source_range.column + double_indentation
  @hash_pair_base_column = pair_key.source_range.column + configured_indentation_width

  calculate_column_delta_offense(rhs, correct_column)
end

#check_regular_indentation(node, lhs, rhs, given_style) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 168

def check_regular_indentation(node, lhs, rhs, given_style)
  @base = alignment_base(node, rhs, given_style)
  correct_column = if @base
                     parent = node.parent
                     parent = parent.parent if parent&.any_block_type?
                     @base.column + extra_indentation(given_style, parent)
                   else
                     indentation(lhs) + correct_indentation(node)
                   end

  calculate_column_delta_offense(rhs, correct_column)
end

#correct_block(corrector, block_node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 98

def correct_block(corrector, block_node)
  AlignmentCorrector.correct(corrector, processed_source, block_node.body, @column_delta)
  end_range = range_by_whole_lines(block_node.loc.end, include_final_newline: false)
  AlignmentCorrector.correct(corrector, processed_source, end_range, @column_delta)
end

#correct_selector_only(corrector, node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 92

def correct_selector_only(corrector, node)
  selector_line = processed_source.buffer.line_range(node.first_line)
  selector_range = range_between(selector_line.begin_pos, selector_line.end_pos)
  AlignmentCorrector.correct(corrector, processed_source, selector_range, @column_delta)
end

#extra_indentation(given_style, parent) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 186

def extra_indentation(given_style, parent)
  return 0 unless given_style == :indented_relative_to_receiver

  if parent&.type?(:splat, :kwsplat)
    configured_indentation_width - parent.loc.operator.length
  else
    configured_indentation_width
  end
end

#find_base_receiver(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 69

def find_base_receiver(node)
  base_receiver = node
  base_receiver = base_receiver.receiver while base_receiver.receiver
  base_receiver
end

#find_continuation_receiver(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 345

def find_continuation_receiver(node)
  receiver = node.receiver
  return unless receiver.call_type? && receiver.loc.dot && receiver.receiver
  return unless receiver.loc.dot.line > receiver.receiver.last_line

  receiver
end

#find_hash_method_base_in_receiver_chain(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 288

def find_hash_method_base_in_receiver_chain(node)
  receiver_chain = unwrap_block_node(node.receiver)
  while receiver_chain&.call_type?
    base_receiver = unwrap_block_node(receiver_chain.receiver)
    if alignment_base_for_chained_receiver?(receiver_chain, base_receiver)
      return receiver_chain.loc.dot.join(receiver_chain.loc.selector)
    end

    receiver_chain = base_receiver
  end
end

#find_hash_pair_alignment_base(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 160

def find_hash_pair_alignment_base(node)
  base_receiver = find_base_receiver(node.receiver)
  return unless base_receiver.hash_type?

  first_call = first_call_has_a_dot(node)
  first_call.loc.dot.join(first_call.loc.selector)
end

#find_multiline_block_chain_node(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 339

def find_multiline_block_chain_node(node)
  return find_continuation_receiver(node) if node.block_node

  handle_descendant_block(node)
end

#find_pair_ancestor(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 75

def find_pair_ancestor(node)
  node.each_ancestor.find(&:pair_type?)
end

#first_call_alignment_node(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 313

def first_call_alignment_node(node)
  node = first_call_has_a_dot(node)
  base_receiver = find_base_receiver(node)

  return node if method_on_receiver_last_line?(node, base_receiver, :array)
  return if node.loc.dot.line != node.first_line
  return if method_on_receiver_last_line?(node, base_receiver, :begin)

  node
end

#first_call_has_a_dot(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 360

def first_call_has_a_dot(node)
  base = find_base_receiver(node)
  node = base.parent
  node = node.parent until node.loc?(:dot)
  node
end

#get_dot_right_above(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 330

def get_dot_right_above(node)
  node.each_ancestor.find do |a|
    dot = a.loc.dot if a.loc?(:dot)
    next unless dot

    dot.line == node.loc.dot.line - 1 && dot.column == node.loc.dot.column
  end
end

#handle_descendant_block(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 353

def handle_descendant_block(node)
  block_node = node.each_descendant(:any_block).first
  return unless block_node&.multiline?

  node.receiver.call_type? ? node.receiver : block_node.parent
end

#hash_pair_aligned?(pair_ancestor, given_style) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 137

def hash_pair_aligned?(pair_ancestor, given_style)
  pair_ancestor && given_style == :aligned
end

#hash_pair_indented?(node, pair_ancestor, given_style) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 141

def hash_pair_indented?(node, pair_ancestor, given_style)
  pair_ancestor && given_style == :indented && find_base_receiver(node).hash_type?
end

#message(node, lhs, rhs) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 196

def message(node, lhs, rhs)
  if should_indent_relative_to_receiver?
    relative_to_receiver_message(rhs)
  elsif should_align_with_base?
    align_with_base_message(rhs)
  else
    no_base_message(lhs, rhs, node)
  end
end

#method_on_receiver_last_line?(node, base_receiver, type) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 324

def method_on_receiver_last_line?(node, base_receiver, type)
  base_receiver &&
    node.loc.dot.line == base_receiver.last_line &&
    base_receiver.type?(type)
end

#no_base_message(lhs, rhs, node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 227

def no_base_message(lhs, rhs, node)
  if @hash_pair_base_column
    used_indentation = rhs.column - @hash_pair_base_column
    expected_indentation = configured_indentation_width
  else
    used_indentation = rhs.column - indentation(lhs)
    expected_indentation = correct_indentation(node)
  end
  what = operation_description(node, rhs)

  "Use #{expected_indentation} (not #{used_indentation}) " \
    "spaces for indenting #{what} spanning multiple lines."
end

#offending_range(node, lhs, rhs, given_style) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 120

def offending_range(node, lhs, rhs, given_style)
  return false unless begins_its_line?(rhs)

  @send_node = node # Store for use in autocorrect
  pair_ancestor = find_pair_ancestor(node)
  if hash_pair_aligned?(pair_ancestor, given_style)
    return check_hash_pair_indentation(node, lhs, rhs)
  end
  if hash_pair_indented?(node, pair_ancestor, given_style)
    return check_hash_pair_indented_style(rhs, pair_ancestor)
  end

  return false if !pair_ancestor && not_for_this_cop?(node)

  check_regular_indentation(node, lhs, rhs, given_style)
end

#operation_rhs(node) {|operation_rhs.first_argument| ... } (private)

Yields:

  • (operation_rhs.first_argument)
[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 367

def operation_rhs(node)
  operation_rhs = node.receiver.each_ancestor(:send).find do |rhs|
    operator_rhs?(rhs, node.receiver)
  end

  return unless operation_rhs

  yield operation_rhs.first_argument
end

#operator_rhs?(node, receiver) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 377

def operator_rhs?(node, receiver)
  node.operator_method? && node.arguments? && within_node?(receiver, node.first_argument)
end

#receiver_alignment_base(node) (private)

a .b .c

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 280

def receiver_alignment_base(node)
  hash_method_base = find_hash_method_base_in_receiver_chain(node)
  return hash_method_base if hash_method_base

  first_call = first_call_has_a_dot(node)
  first_call.receiver.source_range
end

#relative_to_receiver_message(rhs) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 214

def relative_to_receiver_message(rhs)
  "Indent `#{rhs.source}` #{configured_indentation_width} spaces " \
    "more than `#{base_source}` on line #{@base.line}."
end

#relevant_node?(send_node) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 104

def relevant_node?(send_node)
  send_node.loc.dot # Only check method calls with dot operator
end

#right_hand_side(send_node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 108

def right_hand_side(send_node)
  dot = send_node.loc.dot
  selector = send_node.loc.selector
  if (send_node.dot? || send_node.safe_navigation?) && selector && same_line?(dot, selector)
    dot.join(selector)
  elsif selector
    selector
  elsif send_node.implicit_call?
    dot.join(send_node.loc.begin)
  end
end

#semantic_alignment_base(node, rhs) (private)

a.b .c

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 268

def semantic_alignment_base(node, rhs)
  return unless rhs.source.start_with?('.', '&.')

  node = semantic_alignment_node(node)
  return unless node&.loc&.selector && node.loc.dot

  node.loc.dot.join(node.loc.selector)
end

#semantic_alignment_node(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 305

def semantic_alignment_node(node)
  return if argument_in_method_call(node, :with_parentheses)

  get_dot_right_above(node) ||
    find_multiline_block_chain_node(node) ||
    first_call_alignment_node(node)
end

#syntactic_alignment_base(lhs, rhs) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 250

def syntactic_alignment_base(lhs, rhs)
  # a if b
  #      .c
  kw_node_with_special_indentation(lhs) do |base|
    return indented_keyword_expression(base).source_range
  end

  # a = b
  #     .c
  part_of_assignment_rhs(lhs, rhs) { |base| return assignment_rhs(base).source_range }

  # a + b
  #     .c
  operation_rhs(lhs) { |base| return base.source_range }
end

#unwrap_block_node(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 79

def unwrap_block_node(node)
  node&.any_block_type? ? node.send_node : node
end

#validate_config

Raises:

[ GitHub ]

  
# File 'lib/rubocop/cop/layout/multiline_method_call_indentation.rb', line 57

def validate_config
  return unless style == :aligned && cop_config['IndentationWidth']

  raise ValidationError,
        'The `Layout/MultilineMethodCallIndentation` ' \
        'cop only accepts an `IndentationWidth` ' \
        'configuration parameter when ' \
        '`EnforcedStyle` is `indented`.'
end