Class: Spoom::Deadcode::Remover::NodeFinder

Inherits:
Visitor
  • Object
show all
Extended by:
T::Sig
Defined in:
lib/spoom/deadcode/remover.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Visitor

#visit_alias_global_variable_node, #visit_alias_method_node, #visit_alternation_pattern_node, #visit_and_node, #visit_arguments_node, #visit_array_node, #visit_array_pattern_node, #visit_assoc_node, #visit_assoc_splat_node, #visit_back_reference_read_node, #visit_begin_node, #visit_block_argument_node, #visit_block_local_variable_node, #visit_block_node, #visit_block_parameter_node, #visit_block_parameters_node, #visit_break_node, #visit_call_and_write_node, #visit_call_node, #visit_call_operator_write_node, #visit_call_or_write_node, #visit_call_target_node, #visit_capture_pattern_node, #visit_case_match_node, #visit_case_node, #visit_child_nodes, #visit_class_node, #visit_class_variable_and_write_node, #visit_class_variable_operator_write_node, #visit_class_variable_or_write_node, #visit_class_variable_read_node, #visit_class_variable_target_node, #visit_class_variable_write_node, #visit_constant_and_write_node, #visit_constant_operator_write_node, #visit_constant_or_write_node, #visit_constant_path_and_write_node, #visit_constant_path_node, #visit_constant_path_operator_write_node, #visit_constant_path_or_write_node, #visit_constant_path_target_node, #visit_constant_path_write_node, #visit_constant_read_node, #visit_constant_target_node, #visit_constant_write_node, #visit_def_node, #visit_defined_node, #visit_else_node, #visit_embedded_statements_node, #visit_embedded_variable_node, #visit_ensure_node, #visit_false_node, #visit_find_pattern_node, #visit_flip_flop_node, #visit_float_node, #visit_for_node, #visit_forwarding_arguments_node, #visit_forwarding_parameter_node, #visit_forwarding_super_node, #visit_global_variable_and_write_node, #visit_global_variable_operator_write_node, #visit_global_variable_or_write_node, #visit_global_variable_read_node, #visit_global_variable_target_node, #visit_global_variable_write_node, #visit_hash_node, #visit_hash_pattern_node, #visit_if_node, #visit_imaginary_node, #visit_implicit_node, #visit_implicit_rest_node, #visit_in_node, #visit_index_and_write_node, #visit_index_operator_write_node, #visit_index_or_write_node, #visit_index_target_node, #visit_instance_variable_and_write_node, #visit_instance_variable_operator_write_node, #visit_instance_variable_or_write_node, #visit_instance_variable_read_node, #visit_instance_variable_target_node, #visit_instance_variable_write_node, #visit_integer_node, #visit_interpolated_match_last_line_node, #visit_interpolated_regular_expression_node, #visit_interpolated_string_node, #visit_interpolated_symbol_node, #visit_interpolated_x_string_node, #visit_keyword_hash_node, #visit_keyword_rest_parameter_node, #visit_lambda_node, #visit_local_variable_and_write_node, #visit_local_variable_operator_write_node, #visit_local_variable_or_write_node, #visit_local_variable_read_node, #visit_local_variable_target_node, #visit_local_variable_write_node, #visit_match_last_line_node, #visit_match_predicate_node, #visit_match_required_node, #visit_match_write_node, #visit_missing_node, #visit_module_node, #visit_multi_target_node, #visit_multi_write_node, #visit_next_node, #visit_nil_node, #visit_no_keywords_parameter_node, #visit_numbered_parameters_node, #visit_numbered_reference_read_node, #visit_optional_keyword_parameter_node, #visit_optional_parameter_node, #visit_or_node, #visit_parameters_node, #visit_parentheses_node, #visit_pinned_expression_node, #visit_pinned_variable_node, #visit_post_execution_node, #visit_pre_execution_node, #visit_program_node, #visit_range_node, #visit_rational_node, #visit_redo_node, #visit_regular_expression_node, #visit_required_keyword_parameter_node, #visit_required_parameter_node, #visit_rescue_modifier_node, #visit_rescue_node, #visit_rest_parameter_node, #visit_retry_node, #visit_return_node, #visit_self_node, #visit_singleton_class_node, #visit_source_encoding_node, #visit_source_file_node, #visit_source_line_node, #visit_splat_node, #visit_statements_node, #visit_string_node, #visit_super_node, #visit_symbol_node, #visit_true_node, #visit_undef_node, #visit_unless_node, #visit_until_node, #visit_when_node, #visit_while_node, #visit_x_string_node, #visit_yield_node

Constructor Details

#initialize(location, kind) ⇒ NodeFinder

Returns a new instance of NodeFinder.



621
622
623
624
625
626
627
# File 'lib/spoom/deadcode/remover.rb', line 621

def initialize(location, kind)
  super()
  @location = location
  @kind = kind
  @node = T.let(nil, T.nilable(Prism::Node))
  @nodes_nesting = T.let([], T::Array[Prism::Node])
end

Instance Attribute Details

#nodeObject (readonly)

Returns the value of attribute node.



615
616
617
# File 'lib/spoom/deadcode/remover.rb', line 615

def node
  @node
end

#nodes_nestingObject (readonly)

Returns the value of attribute nodes_nesting.



618
619
620
# File 'lib/spoom/deadcode/remover.rb', line 618

def nodes_nesting
  @nodes_nesting
end

Class Method Details

.find(source, location, kind) ⇒ Object



556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
# File 'lib/spoom/deadcode/remover.rb', line 556

def find(source, location, kind)
  result = Prism.parse(source)

  unless result.success?
    message = result.errors.map do |e|
      "#{e.message} (at #{e.location.start_line}:#{e.location.start_column})."
    end.join(" ")

    raise ParseError, "Error while parsing #{location.file}: #{message}"
  end

  visitor = new(location, kind)
  visitor.visit(result.value)

  node = visitor.node
  unless node
    raise Error, "Can't find node at #{location}"
  end

  if kind && !node_match_kind?(node, kind)
    raise Error, "Can't find node at #{location}, expected #{kind} but got #{node.class}"
  end

   = T.let(
    result.comments.to_h do |comment|
      [comment.location.start_line, comment]
    end,
    T::Hash[Integer, Prism::Comment],
  )

  NodeContext.new(source, , node, visitor.nodes_nesting)
end

.node_match_kind?(node, kind) ⇒ Boolean

Returns:

  • (Boolean)


590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
# File 'lib/spoom/deadcode/remover.rb', line 590

def node_match_kind?(node, kind)
  case kind
  when Definition::Kind::AttrReader, Definition::Kind::AttrWriter
    node.is_a?(Prism::SymbolNode)
  when Definition::Kind::Class
    node.is_a?(Prism::ClassNode)
  when Definition::Kind::Constant
    node.is_a?(Prism::ConstantWriteNode) ||
      node.is_a?(Prism::ConstantAndWriteNode) ||
      node.is_a?(Prism::ConstantOrWriteNode) ||
      node.is_a?(Prism::ConstantOperatorWriteNode) ||
      node.is_a?(Prism::ConstantPathWriteNode) ||
      node.is_a?(Prism::ConstantPathAndWriteNode) ||
      node.is_a?(Prism::ConstantPathOrWriteNode) ||
      node.is_a?(Prism::ConstantPathOperatorWriteNode) ||
      node.is_a?(Prism::ConstantTargetNode)
  when Definition::Kind::Method
    node.is_a?(Prism::DefNode)
  when Definition::Kind::Module
    node.is_a?(Prism::ModuleNode)
  end
end

Instance Method Details

#visit(node) ⇒ Object



630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
# File 'lib/spoom/deadcode/remover.rb', line 630

def visit(node)
  return unless node

  location = Location.from_prism(@location.file, node.location)

  if location == @location
    # We found the node we're looking for at `@location`
    @node = node

    # The node we found matches the kind we're looking for, we can stop here
    return if @kind && self.class.node_match_kind?(node, @kind)

    # There may be a more precise child inside the node that also matches `@location`, let's visit them
    @nodes_nesting << node
    super(node)
    @nodes_nesting.pop if @nodes_nesting.last == @node
  elsif location.include?(@location)
    # The node we're looking for is inside `node`, let's visit it
    @nodes_nesting << node
    super(node)
  end
end