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
-
VARIABLE_DECLARATION_TYPES =
# File 'lib/rubocop/cop/variable_force/variable.rb', line 9(VARIABLE_ASSIGNMENT_TYPES + ARGUMENT_DECLARATION_TYPES).freeze
Class Method Summary
- .new(name, declaration_node, scope) ⇒ Variable constructor Internal use only
Instance Attribute Summary
- #argument? ⇒ Boolean readonly Internal use only
- #assignments readonly Internal use only
- #block_argument? ⇒ Boolean readonly Internal use only
-
#captured_by_block?
readonly
Alias for #captured_by_block.
- #declaration_node readonly Internal use only
- #explicit_block_local_variable? ⇒ Boolean readonly Internal use only
- #keyword_argument? ⇒ Boolean readonly Internal use only
- #method_argument? ⇒ Boolean readonly Internal use only
- #name readonly Internal use only
- #referenced? ⇒ Boolean readonly Internal use only
- #references readonly Internal use only
- #scope readonly Internal use only
- #should_be_unused? ⇒ Boolean readonly Internal use only
-
#used? ⇒ Boolean
readonly
Internal use only
This is a convenient way to check whether the variable is used in its entire variable lifetime.
Instance Method Summary
- #assign(node) Internal use only
- #capture_with_block! Internal use only
- #captured_by_block (also: #captured_by_block?) readonly Internal use only
- #covers?(container, node) ⇒ Boolean Internal use only
-
#exception_may_reach?(branch, reference_node) ⇒ Boolean
Internal use only
A branch nested in the main body of
begincan be cut short by an exception too. - #in_modifier_conditional?(assignment, reference_node) ⇒ Boolean Internal use only
- #mark_last_as_reassigned!(assignment) Internal use only
- #modifier_conditional_of(node) Internal use only
-
#reference!(node)
Internal use only
rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity.
Constructor Details
.new(name, declaration_node, scope) ⇒ Variable
# 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)
# File 'lib/rubocop/cop/variable_force/variable.rb', line 129
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)
# File 'lib/rubocop/cop/variable_force/variable.rb', line 137
def block_argument? argument? && @scope.node.block_type? end
#captured_by_block? (readonly)
Alias for #captured_by_block.
# 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)
# File 'lib/rubocop/cop/variable_force/variable.rb', line 145
def explicit_block_local_variable? @declaration_node.shadowarg_type? end
#keyword_argument? ⇒ Boolean (readonly)
# File 'lib/rubocop/cop/variable_force/variable.rb', line 141
def keyword_argument? %i[kwarg kwoptarg].include?(@declaration_node.type) end
#method_argument? ⇒ Boolean (readonly)
# File 'lib/rubocop/cop/variable_force/variable.rb', line 133
def method_argument? argument? && @scope.node.any_def_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)
# 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)
# File 'lib/rubocop/cop/variable_force/variable.rb', line 125
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.
# File 'lib/rubocop/cop/variable_force/variable.rb', line 121
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 109
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
#covers?(container, node) ⇒ Boolean
# File 'lib/rubocop/cop/variable_force/variable.rb', line 105
def covers?(container, node) container.equal?(node) || container.source_range.contains?(node.source_range) end
#exception_may_reach?(branch, reference_node) ⇒ Boolean
A branch nested in the main body of begin can be cut short by an exception
too. An earlier assignment in it is visible to a reference in the rescue or
ensure clause, or after a rescue that handles the exception.
# File 'lib/rubocop/cop/variable_force/variable.rb', line 90
def exception_may_reach?(branch, reference_node) branch.each_ancestor.any? do |ancestor| next false unless ancestor.may_run_incompletely? next false if covers?(ancestor.child_node, reference_node) ancestor.control_node.rescue_type? || covers?(ancestor.control_node, reference_node) end end
#in_modifier_conditional?(assignment, reference_node) ⇒ Boolean
# File 'lib/rubocop/cop/variable_force/variable.rb', line 77
def in_modifier_conditional?(assignment, reference_node) conditional = modifier_conditional_of(assignment.node) return false unless conditional # The out-of-scope problem only affects a reference in the modifier body (to the # left of the keyword); a reference after the modifier is put in scope by the # condition's assignment, so an earlier assignment there is genuinely useless. covers?(conditional, reference_node) && !covers?(conditional.condition, reference_node) 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
#modifier_conditional_of(node)
[ GitHub ]# File 'lib/rubocop/cop/variable_force/variable.rb', line 99
def modifier_conditional_of(node) node.each_ancestor(:if, :while, :until).find do |conditional| conditional.modifier_form? && covers?(conditional.condition, node) end end
#reference!(node)
rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
# 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, node) break if !assignment.branch || assignment.branch == reference.branch unless assignment.branch.may_run_incompletely? || exception_may_reach?(assignment.branch, node) (consumed_branches ||= Set.new) << assignment.branch end end end