# encoding: utf-8 module RuboCop module Cop class VariableForce # A Variable represents existance of a local variable. # This holds a variable declaration node, # and some states of the variable. class Variable VARIABLE_DECLARATION_TYPES = (VARIABLE_ASSIGNMENT_TYPES + ARGUMENT_DECLARATION_TYPES).freeze attr_reader :name, :declaration_node, :scope, :assignments, :references, :captured_by_block alias_method :captured_by_block?, :captured_by_block def initialize(name, declaration_node, scope) unless VARIABLE_DECLARATION_TYPES.include?(declaration_node.type) fail 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 def assign(node) @assignments << Assignment.new(node, self) end def referenced? !@references.empty? end def reference!(node) reference = Reference.new(node, @scope) @references << reference consumed_branch_ids = Set.new @assignments.reverse_each do |assignment| next if consumed_branch_ids.include?(assignment.branch_id) unless assignment.run_exclusively_with?(reference) assignment.reference! end break unless assignment.inside_of_branch? break if assignment.branch_id == reference.branch_id next if assignment.reference_penetrable? consumed_branch_ids << assignment.branch_id end end def capture_with_block! @captured_by_block = true end # 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 # and it means we cannot track the usage of the variable. # So we consider it's used to suppress false positive offenses. def used? @captured_by_block || referenced? end def should_be_unused? name.to_s.start_with?('_') end def argument? ARGUMENT_DECLARATION_TYPES.include?(@declaration_node.type) end def method_argument? argument? && [:def, :defs].include?(@scope.node.type) end def block_argument? argument? && @scope.node.type == :block end def keyword_argument? [:kwarg, :kwoptarg].include?(@declaration_node.type) end def explicit_block_local_variable? @declaration_node.type == :shadowarg end end end end end