Class: RuboCop::Cop::VariableForce Private
| Relationships & Source Files | |
| Namespace Children | |
|
Modules:
| |
|
Classes:
| |
| Super Chains via Extension / Inclusion / Inheritance | |
|
Class Chain:
self,
Force
|
|
|
Instance Chain:
self,
Force
|
|
| Inherits: |
RuboCop::Cop::Force
|
| Defined in: | lib/rubocop/cop/variable_force.rb, lib/rubocop/cop/variable_force/assignment.rb, lib/rubocop/cop/variable_force/branch.rb, lib/rubocop/cop/variable_force/branchable.rb, lib/rubocop/cop/variable_force/reference.rb, lib/rubocop/cop/variable_force/scope.rb, lib/rubocop/cop/variable_force/variable.rb, lib/rubocop/cop/variable_force/variable_table.rb |
Overview
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
Constant Summary
-
ARGUMENT_DECLARATION_TYPES =
# File 'lib/rubocop/cop/variable_force.rb', line 44[ :arg, :optarg, :restarg, :kwarg, :kwoptarg, :kwrestarg, :blockarg, # This doesn't mean block argument, it's block-pass (&block). :shadowarg # This means block local variable (obj.each { |arg; this| }). ].freeze
-
BRANCH_NODES =
# File 'lib/rubocop/cop/variable_force.rb', line 83%i[if case case_match rescue].freeze
-
LOCAL_VARIABLE_NAME =
# File 'lib/rubocop/cop/variable_force.rb', line 37
Ruby assigns a named capture to a local variable only when its name is a valid local variable name, so
(?<Foo>…)and(?<if>…)declare no variable./\A(?![[:upper:]\p{Lt}])[a-z_\P{ASCII}][a-zA-Z0-9_\P{ASCII}]*\z/.freeze
-
LOGICAL_OPERATOR_ASSIGNMENT_TYPES =
# File 'lib/rubocop/cop/variable_force.rb', line 51%i[or_asgn and_asgn].freeze
-
LOOP_TYPES =
# File 'lib/rubocop/cop/variable_force.rb', line 60(POST_CONDITION_LOOP_TYPES + %i[while until for]).freeze
-
MULTIPLE_ASSIGNMENT_TYPE =
# File 'lib/rubocop/cop/variable_force.rb', line 54:masgn
-
NODE_HANDLER_METHOD_NAMES =
private
# File 'lib/rubocop/cop/variable_force.rb', line 126[ [VARIABLE_ASSIGNMENT_TYPE, :process_variable_assignment], [REGEXP_NAMED_CAPTURE_TYPE, :process_regexp_named_captures], [PATTERN_MATCH_VARIABLE_TYPE, :process_pattern_match_variable], [MULTIPLE_ASSIGNMENT_TYPE, :process_variable_multiple_assignment], [VARIABLE_REFERENCE_TYPE, :process_variable_referencing], [RESCUE_TYPE, :process_rescue], [ZERO_ARITY_SUPER_TYPE, :process_zero_arity_super], [SEND_TYPE, :process_send], *ARGUMENT_DECLARATION_TYPES.product([:process_variable_declaration]), *OPERATOR_ASSIGNMENT_TYPES.product([:process_variable_operator_assignment]), *LOOP_TYPES.product([:process_loop]), *SCOPE_TYPES.product([:process_scope]) ].to_h.freeze
-
OPERATOR_ASSIGNMENT_TYPES =
# File 'lib/rubocop/cop/variable_force.rb', line 52(LOGICAL_OPERATOR_ASSIGNMENT_TYPES + [:op_asgn]).freeze
-
PATTERN_MATCH_VARIABLE_TYPE =
# File 'lib/rubocop/cop/variable_force.rb', line 30:match_var
-
POST_CONDITION_LOOP_TYPES =
# File 'lib/rubocop/cop/variable_force.rb', line 59%i[while_post until_post].freeze
-
REGEXP_NAMED_CAPTURE_TYPE =
# File 'lib/rubocop/cop/variable_force.rb', line 29:match_with_lvasgn
-
RESCUE_TYPE =
# File 'lib/rubocop/cop/variable_force.rb', line 62:rescue
-
RESERVED_WORDS =
# File 'lib/rubocop/cop/variable_force.rb', line 38%w[ __ENCODING__ __FILE__ __LINE__ BEGIN END alias and begin break case class def do else elsif end ensure false for if in module next nil not or redo rescue retry return self super then true undef unless until when while yield ].to_set.freeze
-
REST_ASSIGNMENT_TYPE =
# File 'lib/rubocop/cop/variable_force.rb', line 55:splat
-
SCOPE_TYPES =
# File 'lib/rubocop/cop/variable_force.rb', line 67(TWISTED_SCOPE_TYPES + [:def]).freeze
-
SEND_TYPE =
# File 'lib/rubocop/cop/variable_force.rb', line 69:send
-
TWISTED_SCOPE_TYPES =
# File 'lib/rubocop/cop/variable_force.rb', line 66%i[block numblock itblock class sclass defs module].freeze
-
VARIABLE_ASSIGNMENT_TYPE =
# File 'lib/rubocop/cop/variable_force.rb', line 28:lvasgn
-
VARIABLE_ASSIGNMENT_TYPES =
# File 'lib/rubocop/cop/variable_force.rb', line 31[ VARIABLE_ASSIGNMENT_TYPE, REGEXP_NAMED_CAPTURE_TYPE, PATTERN_MATCH_VARIABLE_TYPE ].freeze
-
VARIABLE_REFERENCE_TYPE =
# File 'lib/rubocop/cop/variable_force.rb', line 57:lvar
-
ZERO_ARITY_SUPER_TYPE =
# File 'lib/rubocop/cop/variable_force.rb', line 64:zsuper
Class Method Summary
Force - Inherited
Instance Attribute Summary
Instance Method Summary
-
#investigate(processed_source)
Internal use only
Starting point.
- #process_node(node) Internal use only
- #variable_table Internal use only
- #descendant_reference(node) private Internal use only
- #each_descendant_reference(loop_node) private Internal use only
- #find_variables_in_loop(loop_node) private Internal use only
-
#inspect_variables_in_scope(scope_node)
private
Internal use only
This is called for each scope recursively.
- #local_variable_name?(name) ⇒ Boolean private Internal use only
-
#mark_assignments_as_referenced_in_loop(node)
private
Internal use only
Mark last assignments which are referenced in the same loop as referenced by ignoring AST order since they would be referenced in next iteration.
- #node_handler_method_name(node) private Internal use only
- #process_children(origin_node) private Internal use only
- #process_loop(node) private Internal use only
- #process_pattern_match_variable(node) private Internal use only
- #process_regexp_named_captures(node) private Internal use only
- #process_rescue(node) private Internal use only
- #process_scope(node) private Internal use only
- #process_send(node) private Internal use only
- #process_variable_assignment(node) private Internal use only
- #process_variable_declaration(node) private Internal use only
- #process_variable_multiple_assignment(node) private Internal use only
- #process_variable_operator_assignment(node) private Internal use only
- #process_variable_referencing(node) private Internal use only
- #process_zero_arity_super(node) private Internal use only
- #reference_assignments(loop_assignments, loop_node) private Internal use only
- #regexp_captured_names(node) private Internal use only
- #scanned_node?(node) ⇒ Boolean private Internal use only
- #scanned_nodes private Internal use only
- #skip_children! private Internal use only
- #twisted_nodes(node) private Internal use only
Force - Inherited
Constructor Details
This class inherits a constructor from RuboCop::Cop::Force
Instance Method Details
#descendant_reference(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 365
def descendant_reference(node) case node.type when :lvar VariableReference.new(node.children.first) when :lvasgn AssignmentReference.new(node) when *OPERATOR_ASSIGNMENT_TYPES VariableReference.new(node.lhs.name) if node.lhs.lvasgn_type? end end
#each_descendant_reference(loop_node) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 355
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) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 340
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) (private)
This is called for each scope recursively.
# File 'lib/rubocop/cop/variable_force.rb', line 108
def inspect_variables_in_scope(scope_node) variable_table.push_scope(scope_node) process_children(scope_node) variable_table.pop_scope end
#investigate(processed_source)
Starting point.
# File 'lib/rubocop/cop/variable_force.rb', line 90
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
#local_variable_name?(name) ⇒ Boolean (private)
# File 'lib/rubocop/cop/variable_force.rb', line 207
def local_variable_name?(name) LOCAL_VARIABLE_NAME.match?(name) && !RESERVED_WORDS.include?(name) end
#mark_assignments_as_referenced_in_loop(node) (private)
Mark last 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 322
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 loop_assignments = variable.assignments.select do |assignment| assignment_nodes_in_loop.include?(assignment.node) end next unless loop_assignments.any? reference_assignments(loop_assignments, node) end end
#node_handler_method_name(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 141
def node_handler_method_name(node) NODE_HANDLER_METHOD_NAMES[node.type] end
#process_children(origin_node) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 114
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) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 253
def process_loop(node) if node.post_condition_loop? # See the comment at the end of file for this behavior. condition_node, body_node = *node process_node(body_node) process_node(condition_node) elsif node.for_type? # In `for item in items` the rightmost expression is evaluated first. process_node(node.collection) process_node(node.variable) process_node(node.body) if node.body else process_children(node) end mark_assignments_as_referenced_in_loop(node) skip_children! end
#process_node(node)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 99
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
#process_pattern_match_variable(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 193
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) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 175
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) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 273
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) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 292
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) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 311
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) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 157
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) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 145
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) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 241
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) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 211
def process_variable_operator_assignment(node) asgn_node = node.lhs return unless asgn_node.lvasgn_type? name = asgn_node.name 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(node.rhs) variable_table.assign_to_variable(name, asgn_node) skip_children! end
#process_variable_referencing(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 248
def process_variable_referencing(node) name = node.children.first variable_table.reference_variable(name, node) end
#process_zero_arity_super(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 284
def process_zero_arity_super(node) variable_table.accessible_variables.each do |variable| next unless variable.method_argument? variable.reference!(node) end end
#reference_assignments(loop_assignments, loop_node) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 376
def reference_assignments(loop_assignments, loop_node) # If inside a branching statement, mark all as referenced. # Otherwise, mark only the last assignment as referenced. # Note that `rescue` must be considered as branching because of # the `retry` keyword. loop_assignments.each do |assignment| assignment.reference!(loop_node) if assignment.node.each_ancestor(*BRANCH_NODES).any? end loop_assignments.last&.reference!(loop_node) end
#regexp_captured_names(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 201
def regexp_captured_names(node) regexp = node.to_regexp regexp.names.select { |name| local_variable_name?(name) } end
#scanned_node?(node) ⇒ Boolean (private)
# File 'lib/rubocop/cop/variable_force.rb', line 387
def scanned_node?(node) scanned_nodes.include?(node) end
#scanned_nodes (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 391
def scanned_nodes @scanned_nodes ||= Set.new.compare_by_identity end
#skip_children! (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 122
def skip_children! :skip_children end
#twisted_nodes(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 305
def twisted_nodes(node) twisted_nodes = [node.children[0]] twisted_nodes << node.children[1] if node.class_type? twisted_nodes.compact end
#variable_table
[ GitHub ]# File 'lib/rubocop/cop/variable_force.rb', line 85
def variable_table @variable_table ||= VariableTable.new(self) end