class RuboCop::Cop::VariableForce::Variable

A Variable represents existence of a local variable. This holds a variable declaration node and some states of the variable.

Constants

VARIABLE_DECLARATION_TYPES

Attributes

assignments[R]
captured_by_block[R]
captured_by_block?[R]
declaration_node[R]
name[R]
references[R]
scope[R]

Public Class Methods

new(name, declaration_node, scope) click to toggle source
# File lib/rubocop/cop/variable_force/variable.rb, line 15
def initialize(name, declaration_node, scope)
  unless VARIABLE_DECLARATION_TYPES.include?(declaration_node.type)
    raise ArgumentError,
          "Node type must be any of #{VARIABLE_DECLARATION_TYPES}, " \
          "passed #{declaration_node.type}"
  end

  @name = name.to_sym
  @declaration_node = declaration_node
  @scope = scope

  @assignments = []
  @references = []
  @captured_by_block = false
end

Public Instance Methods

argument?() click to toggle source
# File lib/rubocop/cop/variable_force/variable.rb, line 98
def argument?
  ARGUMENT_DECLARATION_TYPES.include?(@declaration_node.type)
end
assign(node) click to toggle source
# File lib/rubocop/cop/variable_force/variable.rb, line 31
def assign(node)
  assignment = Assignment.new(node, self)

  @assignments.last&.reassigned! unless captured_by_block?

  @assignments << assignment
end
block_argument?() click to toggle source
# File lib/rubocop/cop/variable_force/variable.rb, line 106
def block_argument?
  argument? && @scope.node.block_type?
end
capture_with_block!() click to toggle source
# File lib/rubocop/cop/variable_force/variable.rb, line 78
def capture_with_block!
  @captured_by_block = true
end
explicit_block_local_variable?() click to toggle source
# File lib/rubocop/cop/variable_force/variable.rb, line 114
def explicit_block_local_variable?
  @declaration_node.shadowarg_type?
end
in_modifier_conditional?(assignment) click to toggle source

rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity

# File lib/rubocop/cop/variable_force/variable.rb, line 70
def in_modifier_conditional?(assignment)
  parent = assignment.node.parent
  parent = parent.parent if parent&.begin_type?
  return false if parent.nil?

  (parent.if_type? || parent.while_type? || parent.until_type?) && parent.modifier_form?
end
keyword_argument?() click to toggle source
# File lib/rubocop/cop/variable_force/variable.rb, line 110
def keyword_argument?
  %i[kwarg kwoptarg].include?(@declaration_node.type)
end
method_argument?() click to toggle source
# File lib/rubocop/cop/variable_force/variable.rb, line 102
def method_argument?
  argument? && %i[def defs].include?(@scope.node.type)
end
reference!(node) click to toggle source

rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity

# File lib/rubocop/cop/variable_force/variable.rb, line 44
def reference!(node)
  reference = Reference.new(node, @scope)
  @references << reference
  consumed_branches = nil

  @assignments.reverse_each do |assignment|
    next if consumed_branches&.include?(assignment.branch)

    assignment.reference!(node) unless assignment.run_exclusively_with?(reference)

    # Modifier if/unless conditions are special. Assignments made in
    # them do not put the assigned variable in scope to the left of the
    # if/unless keyword. A preceding assignment is needed to put the
    # variable in scope. For this reason we skip to the next assignment
    # here.
    next if in_modifier_conditional?(assignment)

    break if !assignment.branch || assignment.branch == reference.branch

    unless assignment.branch.may_run_incompletely?
      (consumed_branches ||= Set.new) << assignment.branch
    end
  end
end
referenced?() click to toggle source
# File lib/rubocop/cop/variable_force/variable.rb, line 39
def referenced?
  !@references.empty?
end
should_be_unused?() click to toggle source
# File lib/rubocop/cop/variable_force/variable.rb, line 94
def should_be_unused?
  name.to_s.start_with?('_')
end
used?() click to toggle source

This is a convenient way to check whether the variable is used in its entire variable lifetime. For more precise usage check, refer Assignment#used?.

Once the variable is captured by a block, we have no idea when, where, and how many times the block would be invoked. This means we cannot track the usage of the variable. So we consider it’s used to suppress false positive offenses.

# File lib/rubocop/cop/variable_force/variable.rb, line 90
def used?
  @captured_by_block || referenced?
end