123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::Lint::DuplicateBranch

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

Overview

Checks that there are no repeated bodies within if/unless, case-when, case-in and rescue constructs.

With IgnoreLiteralBranches: true, branches are not registered as offenses if they return a basic literal value (string, symbol, integer, float, rational, complex, true, false, or nil), or return an array, hash, regexp or range that only contains one of the above basic literal values.

With IgnoreConstantBranches: true, branches are not registered as offenses if they return a constant value.

With IgnoreDuplicateElseBranch: true, in conditionals with multiple branches, duplicate 'else' branches are not registered as offenses.

Examples:

# bad
if foo
  do_foo
  do_something_else
elsif bar
  do_foo
  do_something_else
end

# good
if foo || bar
  do_foo
  do_something_else
end

# bad
case x
when foo
  do_foo
when bar
  do_foo
else
  do_something_else
end

# good
case x
when foo, bar
  do_foo
else
  do_something_else
end

# bad
begin
  do_something
rescue FooError
  handle_error
rescue BarError
  handle_error
end

# good
begin
  do_something
rescue FooError, BarError
  handle_error
end

IgnoreLiteralBranches: true

# good
case size
when "small" then 100
when "medium" then 250
when "large" then 1000
else 250
end

IgnoreConstantBranches: true

# good
case size
when "small" then SMALL_SIZE
when "medium" then MEDIUM_SIZE
when "large" then LARGE_SIZE
else MEDIUM_SIZE
end

IgnoreDuplicateElseBranch: true

# good
if foo
  do_foo
elsif bar
  do_bar
else
  do_foo
end

Constant Summary

::RuboCop::Cop::Base - Inherited

EMPTY_OFFENSES, RESTRICT_ON_SEND

Class Attribute Summary

::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::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_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

#ignore_constant_branches?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/duplicate_branch.rb', line 155

def ignore_constant_branches?
  cop_config.fetch('IgnoreConstantBranches', false)
end

#ignore_duplicate_else_branches?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/duplicate_branch.rb', line 159

def ignore_duplicate_else_branches?
  cop_config.fetch('IgnoreDuplicateElseBranch', false)
end

#ignore_literal_branches?Boolean (readonly, private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/duplicate_branch.rb', line 151

def ignore_literal_branches?
  cop_config.fetch('IgnoreLiteralBranches', false)
end

Instance Method Details

#branches(node) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/duplicate_branch.rb', line 136

def branches(node)
  node.branches.compact
end

#consider_branch?(branches, branch) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/duplicate_branch.rb', line 140

def consider_branch?(branches, branch)
  return false if ignore_literal_branches? && literal_branch?(branch)
  return false if ignore_constant_branches? && const_branch?(branch)

  if ignore_duplicate_else_branches? && duplicate_else_branch?(branches, branch)
    return false
  end

  true
end

#const_branch?(branch) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/duplicate_branch.rb', line 174

def const_branch?(branch)
  branch.const_type?
end

#duplicate_else_branch?(branches, branch) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/duplicate_branch.rb', line 178

def duplicate_else_branch?(branches, branch)
  return false unless (parent = branch.parent)

  branches.size > 2 &&
    branch.equal?(branches.last) &&
    parent.respond_to?(:else?) && parent.else?
end

#literal_branch?(branch) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/duplicate_branch.rb', line 163

def literal_branch?(branch) # rubocop:disable Metrics/CyclomaticComplexity
  return false if !branch.literal? || branch.xstr_type?
  return true if branch.basic_literal?

  branch.each_descendant.all? do |node|
    node.basic_literal? ||
      node.pair_type? || # hash keys and values are contained within a `pair` node
      (node.const_type? && ignore_constant_branches?)
  end
end

#offense_range(duplicate_branch) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/duplicate_branch.rb', line 122

def offense_range(duplicate_branch)
  parent = duplicate_branch.parent

  if parent.respond_to?(:else_branch) && parent.else_branch.equal?(duplicate_branch)
    if parent.if_type? && parent.ternary?
      duplicate_branch.source_range
    else
      parent.loc.else
    end
  else
    parent.source_range
  end
end

#on_branching_statement(node) Also known as: #on_case, #on_case_match, #on_rescue

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/duplicate_branch.rb', line 102

def on_branching_statement(node)
  branches = branches(node)
  branches.each_with_object(Set.new) do |branch, previous|
    next unless consider_branch?(branches, branch)

    add_offense(offense_range(branch)) unless previous.add?(branch)
  end
end

#on_case(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/duplicate_branch.rb', line 110

alias on_case on_branching_statement

#on_case_match(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/duplicate_branch.rb', line 111

alias on_case_match on_branching_statement

#on_if(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/duplicate_branch.rb', line 114

def on_if(node)
  # Ignore 'elsif' nodes, because we don't want to check them separately whether
  # the 'else' branch is duplicated. We want to check only on the outermost conditional.
  on_branching_statement(node) unless node.elsif?
end

#on_rescue(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/lint/duplicate_branch.rb', line 112

alias on_rescue on_branching_statement