class RuboCop::Cop::VariableForce
This force provides a way to track local variables and scopes of Ruby. Cops interact with this force need to override some of the hook methods.
def before_entering_scope(scope, variable_table) end def after_entering_scope(scope, variable_table) end def before_leaving_scope(scope, variable_table) end def after_leaving_scope(scope, variable_table) end def before_declaring_variable(variable, variable_table) end def after_declaring_variable(variable, variable_table) end
@api private
Constants
- ARGUMENT_DECLARATION_TYPES
- AssignmentReference
- CLASSES_BY_TYPE
- LOGICAL_OPERATOR_ASSIGNMENT_TYPES
- LOOP_TYPES
- MULTIPLE_ASSIGNMENT_TYPE
- NODE_HANDLER_METHOD_NAMES
- OPERATOR_ASSIGNMENT_TYPES
- PATTERN_MATCH_VARIABLE_TYPE
- POST_CONDITION_LOOP_TYPES
- REGEXP_NAMED_CAPTURE_TYPE
- RESCUE_TYPE
- REST_ASSIGNMENT_TYPE
- SCOPE_TYPES
- SEND_TYPE
- TWISTED_SCOPE_TYPES
- VARIABLE_ASSIGNMENT_TYPE
- VARIABLE_ASSIGNMENT_TYPES
- VARIABLE_REFERENCE_TYPE
- VariableReference
- ZERO_ARITY_SUPER_TYPE
Public Instance Methods
investigate(processed_source)
click to toggle source
Starting point.
# File lib/rubocop/cop/variable_force.rb, line 79 def investigate(processed_source) root_node = processed_source.ast return unless root_node variable_table.push_scope(root_node) process_node(root_node) variable_table.pop_scope end
process_node(node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 88 def process_node(node) method_name = node_handler_method_name(node) retval = send(method_name, node) if method_name process_children(node) unless retval == :skip_children end
variable_table()
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 74 def variable_table @variable_table ||= VariableTable.new(self) end
Private Instance Methods
descendant_reference(node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 351 def descendant_reference(node) case node.type when :lvar VariableReference.new(node.children.first) when :lvasgn AssignmentReference.new(node) when *OPERATOR_ASSIGNMENT_TYPES asgn_node = node.children.first VariableReference.new(asgn_node.children.first) if asgn_node.lvasgn_type? end end
each_descendant_reference(loop_node) { |reference| ... }
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 341 def each_descendant_reference(loop_node) # #each_descendant does not consider scope, # but we don't need to care about it here. loop_node.each_descendant do |node| reference = descendant_reference(node) yield reference if reference end end
find_variables_in_loop(loop_node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 326 def find_variables_in_loop(loop_node) referenced_variable_names_in_loop = [] assignment_nodes_in_loop = [] each_descendant_reference(loop_node) do |reference| if reference.assignment? assignment_nodes_in_loop << reference.node else referenced_variable_names_in_loop << reference.name end end [referenced_variable_names_in_loop, assignment_nodes_in_loop] end
inspect_variables_in_scope(scope_node)
click to toggle source
This is called for each scope recursively.
# File lib/rubocop/cop/variable_force.rb, line 97 def inspect_variables_in_scope(scope_node) variable_table.push_scope(scope_node) process_children(scope_node) variable_table.pop_scope end
mark_assignments_as_referenced_in_loop(node)
click to toggle source
Mark all assignments which are referenced in the same loop as referenced by ignoring AST order since they would be referenced in next iteration.
# File lib/rubocop/cop/variable_force.rb, line 307 def mark_assignments_as_referenced_in_loop(node) referenced_variable_names_in_loop, assignment_nodes_in_loop = find_variables_in_loop(node) referenced_variable_names_in_loop.each do |name| variable = variable_table.find_variable(name) # Non related references which are caught in the above scan # would be skipped here. next unless variable variable.assignments.each do |assignment| next if assignment_nodes_in_loop.none? do |assignment_node| assignment_node.equal?(assignment.node) end assignment.reference!(node) end end end
node_handler_method_name(node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 130 def node_handler_method_name(node) NODE_HANDLER_METHOD_NAMES[node.type] end
process_children(origin_node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 103 def process_children(origin_node) origin_node.each_child_node do |child_node| next if scanned_node?(child_node) process_node(child_node) end end
process_loop(node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 243 def process_loop(node) if POST_CONDITION_LOOP_TYPES.include?(node.type) # See the comment at the end of file for this behavior. condition_node, body_node = *node process_node(body_node) process_node(condition_node) else process_children(node) end mark_assignments_as_referenced_in_loop(node) skip_children! end
process_pattern_match_variable(node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 182 def process_pattern_match_variable(node) name = node.children.first variable_table.declare_variable(name, node) unless variable_table.variable_exist?(name) skip_children! end
process_regexp_named_captures(node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 164 def process_regexp_named_captures(node) regexp_node, rhs_node = *node variable_names = regexp_captured_names(regexp_node) variable_names.each do |name| next if variable_table.variable_exist?(name) variable_table.declare_variable(name, node) end process_node(rhs_node) process_node(regexp_node) variable_names.each { |name| variable_table.assign_to_variable(name, node) } skip_children! end
process_rescue(node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 258 def process_rescue(node) resbody_nodes = node.each_child_node(:resbody) contain_retry = resbody_nodes.any? do |resbody_node| resbody_node.each_descendant.any?(&:retry_type?) end # Treat begin..rescue..end with retry as a loop. process_loop(node) if contain_retry end
process_scope(node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 277 def process_scope(node) if TWISTED_SCOPE_TYPES.include?(node.type) # See the comment at the end of file for this behavior. twisted_nodes(node).each do |twisted_node| process_node(twisted_node) scanned_nodes << twisted_node end end inspect_variables_in_scope(node) skip_children! end
process_send(node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 296 def process_send(node) _receiver, method_name, args = *node return unless method_name == :binding return if args && !args.children.empty? variable_table.accessible_variables.each { |variable| variable.reference!(node) } end
process_variable_assignment(node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 146 def process_variable_assignment(node) name = node.children.first variable_table.declare_variable(name, node) unless variable_table.variable_exist?(name) # Need to scan rhs before assignment so that we can mark previous # assignments as referenced if rhs has referencing to the variable # itself like: # # foo = 1 # foo = foo + 1 process_children(node) variable_table.assign_to_variable(name, node) skip_children! end
process_variable_declaration(node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 134 def process_variable_declaration(node) variable_name = node.children.first # restarg and kwrestarg would have no name: # # def initialize(*) # end return unless variable_name variable_table.declare_variable(variable_name, node) end
process_variable_multiple_assignment(node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 231 def process_variable_multiple_assignment(node) lhs_node, rhs_node = *node process_node(rhs_node) process_node(lhs_node) skip_children! end
process_variable_operator_assignment(node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 196 def process_variable_operator_assignment(node) if LOGICAL_OPERATOR_ASSIGNMENT_TYPES.include?(node.type) asgn_node, rhs_node = *node else asgn_node, _operator, rhs_node = *node end return unless asgn_node.lvasgn_type? name = asgn_node.children.first variable_table.declare_variable(name, asgn_node) unless variable_table.variable_exist?(name) # The following statements: # # foo = 1 # foo += foo = 2 # # => 3 # # are equivalent to: # # foo = 1 # foo = foo + (foo = 2) # # => 3 # # So, at operator assignment node, we need to reference the variable # before processing rhs nodes. variable_table.reference_variable(name, node) process_node(rhs_node) variable_table.assign_to_variable(name, asgn_node) skip_children! end
process_variable_referencing(node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 238 def process_variable_referencing(node) name = node.children.first variable_table.reference_variable(name, node) end
process_zero_arity_super(node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 269 def process_zero_arity_super(node) variable_table.accessible_variables.each do |variable| next unless variable.method_argument? variable.reference!(node) end end
regexp_captured_names(node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 190 def regexp_captured_names(node) regexp = node.to_regexp regexp.named_captures.keys end
scanned_node?(node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 363 def scanned_node?(node) scanned_nodes.include?(node) end
scanned_nodes()
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 367 def scanned_nodes @scanned_nodes ||= Set.new.compare_by_identity end
skip_children!()
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 111 def skip_children! :skip_children end
twisted_nodes(node)
click to toggle source
# File lib/rubocop/cop/variable_force.rb, line 290 def twisted_nodes(node) twisted_nodes = [node.children[0]] twisted_nodes << node.children[1] if node.class_type? twisted_nodes.compact end