123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::VariableForce::Variable Private

Relationships & Source Files
Inherits: Object
Defined in: lib/rubocop/cop/variable_force/variable.rb

Overview

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

Constant Summary

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.new(name, declaration_node, scope) ⇒ Variable

[ GitHub ]

  
# 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

Instance Attribute Details

#argument?Boolean (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 105

def argument?
  ARGUMENT_DECLARATION_TYPES.include?(@declaration_node.type)
end

#assignments (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 11

attr_reader :name, :declaration_node, :scope, :assignments, :references, :captured_by_block

#block_argument?Boolean (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 113

def block_argument?
  argument? && @scope.node.block_type?
end

#captured_by_block? (readonly)

Alias for #captured_by_block.

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 13

alias captured_by_block? captured_by_block

#declaration_node (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 11

attr_reader :name, :declaration_node, :scope, :assignments, :references, :captured_by_block

#explicit_block_local_variable?Boolean (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 121

def explicit_block_local_variable?
  @declaration_node.shadowarg_type?
end

#keyword_argument?Boolean (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 117

def keyword_argument?
  %i[kwarg kwoptarg].include?(@declaration_node.type)
end

#method_argument?Boolean (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 109

def method_argument?
  argument? && %i[def defs].include?(@scope.node.type)
end

#name (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 11

attr_reader :name, :declaration_node, :scope, :assignments, :references, :captured_by_block

#referenced?Boolean (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 46

def referenced?
  !@references.empty?
end

#references (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 11

attr_reader :name, :declaration_node, :scope, :assignments, :references, :captured_by_block

#scope (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 11

attr_reader :name, :declaration_node, :scope, :assignments, :references, :captured_by_block

#should_be_unused?Boolean (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 101

def should_be_unused?
  name.to_s.start_with?('_')
end

#used?Boolean (readonly)

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.

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 97

def used?
  @captured_by_block || referenced?
end

Instance Method Details

#assign(node)

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 31

def assign(node)
  assignment = Assignment.new(node, self)

  mark_last_as_reassigned!(assignment)

  @assignments << assignment
end

#capture_with_block!

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 85

def capture_with_block!
  @captured_by_block = true
end

#captured_by_block (readonly) Also known as: #captured_by_block?

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 11

attr_reader :name, :declaration_node, :scope, :assignments, :references, :captured_by_block

#in_modifier_conditional?(assignment) ⇒ Boolean

Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 77

def in_modifier_conditional?(assignment)
  parent = assignment.node.parent
  parent = parent.parent if parent&.begin_type?
  return false if parent.nil?

  parent.type?(:if, :while, :until) && parent.modifier_form?
end

#mark_last_as_reassigned!(assignment)

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 39

def mark_last_as_reassigned!(assignment)
  return if captured_by_block?
  return unless assignment.branch == @assignments.last&.branch

  @assignments.last&.reassigned!
end

#reference!(node)

Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity

[ GitHub ]

  
# File 'lib/rubocop/cop/variable_force/variable.rb', line 51

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