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

Inherits:
Object
  • Object
show all
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 collapse

VARIABLE_DECLARATION_TYPES =
(VARIABLE_ASSIGNMENT_TYPES + ARGUMENT_DECLARATION_TYPES).freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, declaration_node, scope) ⇒ Variable


16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/rubocop/cop/variable_force/variable.rb', line 16

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

#assignmentsObject (readonly)

Returns the value of attribute assignments


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

def assignments
  @assignments
end

#captured_by_blockObject (readonly) Also known as: captured_by_block?

Returns the value of attribute captured_by_block


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

def captured_by_block
  @captured_by_block
end

#declaration_nodeObject (readonly)

Returns the value of attribute declaration_node


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

def declaration_node
  @declaration_node
end

#nameObject (readonly)

Returns the value of attribute name


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

def name
  @name
end

#referencesObject (readonly)

Returns the value of attribute references


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

def references
  @references
end

#scopeObject (readonly)

Returns the value of attribute scope


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

def scope
  @scope
end

Instance Method Details

#argument?Boolean


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

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

#assign(node) ⇒ Object


32
33
34
# File 'lib/rubocop/cop/variable_force/variable.rb', line 32

def assign(node)
  @assignments << Assignment.new(node, self)
end

#block_argument?Boolean


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

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

#capture_with_block!Object


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

def capture_with_block!
  @captured_by_block = true
end

#explicit_block_local_variable?Boolean


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

def explicit_block_local_variable?
  @declaration_node.shadowarg_type?
end

#in_modifier_if?(assignment) ⇒ Boolean

rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity


69
70
71
72
73
# File 'lib/rubocop/cop/variable_force/variable.rb', line 69

def in_modifier_if?(assignment)
  parent = assignment.node.parent
  parent = parent.parent if parent&.begin_type?
  parent&.if_type? && parent&.modifier_form?
end

#keyword_argument?Boolean


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

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

#method_argument?Boolean


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

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

#reference!(node) ⇒ Object

rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity


41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/rubocop/cop/variable_force/variable.rb', line 41

def reference!(node)
  reference = Reference.new(node, @scope)
  @references << reference
  consumed_branches = Set.new

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

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

    # 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_if?(assignment)

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

    unless assignment.branch.may_run_incompletely?
      consumed_branches << assignment.branch
    end
  end
end

#referenced?Boolean


36
37
38
# File 'lib/rubocop/cop/variable_force/variable.rb', line 36

def referenced?
  !@references.empty?
end

#should_be_unused?Boolean


91
92
93
# File 'lib/rubocop/cop/variable_force/variable.rb', line 91

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

#used?Boolean

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.


87
88
89
# File 'lib/rubocop/cop/variable_force/variable.rb', line 87

def used?
  @captured_by_block || referenced?
end