lib/spoom/deadcode/indexer.rb in spoom-1.3.2 vs lib/spoom/deadcode/indexer.rb in spoom-1.3.3

- old
+ new

@@ -10,460 +10,39 @@ attr_reader :path sig { returns(Index) } attr_reader :index - sig { params(path: String, source: String, index: Index, plugins: T::Array[Plugins::Base]).void } - def initialize(path, source, index, plugins: []) + sig { params(path: String, index: Index, plugins: T::Array[Plugins::Base]).void } + def initialize(path, index, plugins: []) super() @path = path - @file_name = T.let(File.basename(path), String) - @source = source @index = index @plugins = plugins - @previous_node = T.let(nil, T.nilable(Prism::Node)) - @names_nesting = T.let([], T::Array[String]) - @nodes_nesting = T.let([], T::Array[Prism::Node]) - @in_const_field = T.let(false, T::Boolean) - @in_opassign = T.let(false, T::Boolean) - @in_symbol_literal = T.let(false, T::Boolean) end # Visit - sig { override.params(node: T.nilable(Prism::Node)).void } - def visit(node) - return unless node - - @nodes_nesting << node - super - @nodes_nesting.pop - @previous_node = node - end - - sig { override.params(node: Prism::AliasMethodNode).void } - def visit_alias_method_node(node) - reference_method(node.old_name.slice, node) - end - - sig { override.params(node: Prism::AndNode).void } - def visit_and_node(node) - reference_method(node.operator_loc.slice, node) - super - end - - sig { override.params(node: Prism::BlockArgumentNode).void } - def visit_block_argument_node(node) - expression = node.expression - case expression - when Prism::SymbolNode - reference_method(expression.unescaped, expression) - else - visit(expression) - end - end - - sig { override.params(node: Prism::CallAndWriteNode).void } - def visit_call_and_write_node(node) - visit(node.receiver) - reference_method(node.read_name.to_s, node) - reference_method(node.write_name.to_s, node) - visit(node.value) - end - - sig { override.params(node: Prism::CallOperatorWriteNode).void } - def visit_call_operator_write_node(node) - visit(node.receiver) - reference_method(node.read_name.to_s, node) - reference_method(node.write_name.to_s, node) - visit(node.value) - end - - sig { override.params(node: Prism::CallOrWriteNode).void } - def visit_call_or_write_node(node) - visit(node.receiver) - reference_method(node.read_name.to_s, node) - reference_method(node.write_name.to_s, node) - visit(node.value) - end - sig { override.params(node: Prism::CallNode).void } def visit_call_node(node) - visit_send( - Send.new( - node: node, - name: node.name.to_s, - recv: node.receiver, - args: node.arguments&.arguments || [], - block: node.block, - ), - ) - end + visit(node.receiver) - sig { override.params(node: Prism::ClassNode).void } - def visit_class_node(node) - constant_path = node.constant_path.slice - - if constant_path.start_with?("::") - full_name = constant_path.delete_prefix("::") - - # We found a top level definition such as `class ::A; end`, we need to reset the name nesting - old_nesting = @names_nesting.dup - @names_nesting.clear - @names_nesting << full_name - - define_class(T.must(constant_path.split("::").last), full_name, node) - - # We do not call `super` here because we don't want to visit the `constant` again - visit(node.superclass) if node.superclass - visit(node.body) - - # Restore the name nesting once we finished visited the class - @names_nesting.clear - @names_nesting = old_nesting - else - @names_nesting << constant_path - define_class(T.must(constant_path.split("::").last), @names_nesting.join("::"), node) - - # We do not call `super` here because we don't want to visit the `constant` again - visit(node.superclass) if node.superclass - visit(node.body) - - @names_nesting.pop - end - end - - sig { override.params(node: Prism::ConstantAndWriteNode).void } - def visit_constant_and_write_node(node) - reference_constant(node.name.to_s, node) - visit(node.value) - end - - sig { override.params(node: Prism::ConstantOperatorWriteNode).void } - def visit_constant_operator_write_node(node) - reference_constant(node.name.to_s, node) - visit(node.value) - end - - sig { override.params(node: Prism::ConstantOrWriteNode).void } - def visit_constant_or_write_node(node) - reference_constant(node.name.to_s, node) - visit(node.value) - end - - sig { override.params(node: Prism::ConstantPathWriteNode).void } - def visit_constant_path_write_node(node) - parent = node.target.parent - name = node.target.child.slice - - if parent - visit(parent) - - parent_name = parent.slice - full_name = [*@names_nesting, parent_name, name].compact.join("::") - define_constant(name, full_name, node) - else - define_constant(name, name, node) - end - - visit(node.value) - end - - sig { override.params(node: Prism::ConstantReadNode).void } - def visit_constant_read_node(node) - reference_constant(node.name.to_s, node) - end - - sig { override.params(node: Prism::ConstantWriteNode).void } - def visit_constant_write_node(node) - name = node.name.to_s - full_name = [*@names_nesting, name].join("::") - define_constant(name, full_name, node) - visit(node.value) - end - - sig { override.params(node: Prism::DefNode).void } - def visit_def_node(node) - name = node.name.to_s - define_method(name, [*@names_nesting, name].join("::"), node) - - super - end - - sig { override.params(node: Prism::LocalVariableAndWriteNode).void } - def visit_local_variable_and_write_node(node) - name = node.name.to_s - reference_method(name, node) - reference_method("#{name}=", node) - visit(node.value) - end - - sig { override.params(node: Prism::LocalVariableOperatorWriteNode).void } - def visit_local_variable_operator_write_node(node) - name = node.name.to_s - reference_method(name, node) - reference_method("#{name}=", node) - visit(node.value) - end - - sig { override.params(node: Prism::LocalVariableOrWriteNode).void } - def visit_local_variable_or_write_node(node) - name = node.name.to_s - reference_method(name, node) - reference_method("#{name}=", node) - visit(node.value) - end - - sig { override.params(node: Prism::LocalVariableWriteNode).void } - def visit_local_variable_write_node(node) - visit(node.value) - reference_method("#{node.name}=", node) - end - - sig { override.params(node: Prism::ModuleNode).void } - def visit_module_node(node) - constant_path = node.constant_path.slice - - if constant_path.start_with?("::") - full_name = constant_path.delete_prefix("::") - - # We found a top level definition such as `class ::A; end`, we need to reset the name nesting - old_nesting = @names_nesting.dup - @names_nesting.clear - @names_nesting << full_name - - define_module(T.must(constant_path.split("::").last), full_name, node) - - visit(node.body) - - # Restore the name nesting once we finished visited the class - @names_nesting.clear - @names_nesting = old_nesting - else - @names_nesting << constant_path - define_module(T.must(constant_path.split("::").last), @names_nesting.join("::"), node) - - # We do not call `super` here because we don't want to visit the `constant` again - visit(node.body) - - @names_nesting.pop - end - end - - sig { override.params(node: Prism::MultiWriteNode).void } - def visit_multi_write_node(node) - node.lefts.each do |const| - case const - when Prism::ConstantTargetNode, Prism::ConstantPathTargetNode - name = const.slice - define_constant(T.must(name.split("::").last), [*@names_nesting, name].join("::"), const) - when Prism::LocalVariableTargetNode - reference_method("#{const.name}=", node) - end - end - visit(node.value) - end - - sig { override.params(node: Prism::OrNode).void } - def visit_or_node(node) - reference_method(node.operator_loc.slice, node) - super - end - - sig { params(send: Send).void } - def visit_send(send) - visit(send.recv) - - case send.name - when "attr_reader" - send.args.each do |arg| - next unless arg.is_a?(Prism::SymbolNode) - - name = arg.unescaped - define_attr_reader(name, [*@names_nesting, name].join("::"), arg) - end - when "attr_writer" - send.args.each do |arg| - next unless arg.is_a?(Prism::SymbolNode) - - name = arg.unescaped - define_attr_writer("#{name}=", "#{[*@names_nesting, name].join("::")}=", arg) - end - when "attr_accessor" - send.args.each do |arg| - next unless arg.is_a?(Prism::SymbolNode) - - name = arg.unescaped - full_name = [*@names_nesting, name].join("::") - define_attr_reader(name, full_name, arg) - define_attr_writer("#{name}=", "#{full_name}=", arg) - end - else - @plugins.each do |plugin| - plugin.internal_on_send(self, send) - end - - reference_method(send.name, send.node) - - case send.name - when "<", ">", "<=", ">=" - # For comparison operators, we also reference the `<=>` method - reference_method("<=>", send.node) - end - - visit_all(send.args) - visit(send.block) - end - end - - # Definition indexing - - sig { params(name: String, full_name: String, node: Prism::Node).void } - def define_attr_reader(name, full_name, node) - definition = Definition.new( - kind: Definition::Kind::AttrReader, - name: name, - full_name: full_name, - location: node_location(node), + send = Send.new( + node: node, + name: node.name.to_s, + recv: node.receiver, + args: node.arguments&.arguments || [], + block: node.block, + location: Location.from_prism(@path, node.location), ) - @index.define(definition) - @plugins.each { |plugin| plugin.internal_on_define_accessor(self, definition) } - end - sig { params(name: String, full_name: String, node: Prism::Node).void } - def define_attr_writer(name, full_name, node) - definition = Definition.new( - kind: Definition::Kind::AttrWriter, - name: name, - full_name: full_name, - location: node_location(node), - ) - @index.define(definition) - @plugins.each { |plugin| plugin.internal_on_define_accessor(self, definition) } - end - - sig { params(name: String, full_name: String, node: Prism::Node).void } - def define_class(name, full_name, node) - definition = Definition.new( - kind: Definition::Kind::Class, - name: name, - full_name: full_name, - location: node_location(node), - ) - @index.define(definition) - @plugins.each { |plugin| plugin.internal_on_define_class(self, definition) } - end - - sig { params(name: String, full_name: String, node: Prism::Node).void } - def define_constant(name, full_name, node) - definition = Definition.new( - kind: Definition::Kind::Constant, - name: name, - full_name: full_name, - location: node_location(node), - ) - @index.define(definition) - @plugins.each { |plugin| plugin.internal_on_define_constant(self, definition) } - end - - sig { params(name: String, full_name: String, node: Prism::Node).void } - def define_method(name, full_name, node) - definition = Definition.new( - kind: Definition::Kind::Method, - name: name, - full_name: full_name, - location: node_location(node), - ) - @index.define(definition) - @plugins.each { |plugin| plugin.internal_on_define_method(self, definition) } - end - - sig { params(name: String, full_name: String, node: Prism::Node).void } - def define_module(name, full_name, node) - definition = Definition.new( - kind: Definition::Kind::Module, - name: name, - full_name: full_name, - location: node_location(node), - ) - @index.define(definition) - @plugins.each { |plugin| plugin.internal_on_define_module(self, definition) } - end - - # Reference indexing - - sig { params(name: String, node: Prism::Node).void } - def reference_constant(name, node) - @index.reference(Reference.new(name: name, kind: Reference::Kind::Constant, location: node_location(node))) - end - - sig { params(name: String, node: Prism::Node).void } - def reference_method(name, node) - @index.reference(Reference.new(name: name, kind: Reference::Kind::Method, location: node_location(node))) - end - - # Context - - sig { returns(Prism::Node) } - def current_node - T.must(@nodes_nesting.last) - end - - sig { type_parameters(:N).params(type: T::Class[T.type_parameter(:N)]).returns(T.nilable(T.type_parameter(:N))) } - def nesting_node(type) - @nodes_nesting.reverse_each do |node| - return T.unsafe(node) if node.is_a?(type) + @plugins.each do |plugin| + plugin.on_send(send) end - nil - end - - sig { returns(T.nilable(Prism::ClassNode)) } - def nesting_class - nesting_node(Prism::ClassNode) - end - - sig { returns(T.nilable(Prism::BlockNode)) } - def nesting_block - nesting_node(Prism::BlockNode) - end - - sig { returns(T.nilable(Prism::CallNode)) } - def nesting_call - nesting_node(Prism::CallNode) - end - - sig { returns(T.nilable(String)) } - def nesting_class_name - nesting_class = self.nesting_class - return unless nesting_class - - nesting_class.name.to_s - end - - sig { returns(T.nilable(String)) } - def nesting_class_superclass_name - nesting_class_superclass = nesting_class&.superclass - return unless nesting_class_superclass - - nesting_class_superclass.slice.delete_prefix("::") - end - - sig { returns(T.nilable(String)) } - def last_sig - previous_call = @previous_node - return unless previous_call.is_a?(Prism::CallNode) - return unless previous_call.name == :sig - - previous_call.slice - end - - # Node utils - - sig { params(node: Prism::Node).returns(Location) } - def node_location(node) - Location.from_prism(@path, node.location) + visit(node.arguments) + visit(send.block) end end end end