lib/spoom/deadcode/plugins/base.rb in spoom-1.2.3 vs lib/spoom/deadcode/plugins/base.rb in spoom-1.2.4

- old
+ new

@@ -15,10 +15,64 @@ class << self extend T::Sig # Plugins DSL + # Mark classes matching `names` as ignored. + # + # Names can be either strings or regexps: + # + # ~~~rb + # class MyPlugin < Spoom::Deadcode::Plugins::Base + # ignore_class_names( + # "Foo", + # "Bar", + # /Baz.*/, + # ) + # end + # ~~~ + sig { params(names: T.any(String, Regexp)).void } + def ignore_classes_named(*names) + save_names_and_patterns(names, :@ignored_class_names, :@ignored_class_patterns) + end + + # Mark classes directly subclassing a class matching `names` as ignored. + # + # Names can be either strings or regexps: + # + # ~~~rb + # class MyPlugin < Spoom::Deadcode::Plugins::Base + # ignore_classes_inheriting_from( + # "Foo", + # "Bar", + # /Baz.*/, + # ) + # end + # ~~~ + sig { params(names: T.any(String, Regexp)).void } + def ignore_classes_inheriting_from(*names) + save_names_and_patterns(names, :@ignored_subclasses_of_names, :@ignored_subclasses_of_patterns) + end + + # Mark constants matching `names` as ignored. + # + # Names can be either strings or regexps: + # + # ~~~rb + # class MyPlugin < Spoom::Deadcode::Plugins::Base + # ignore_class_names( + # "FOO", + # "BAR", + # /BAZ.*/, + # ) + # end + # ~~~ + sig { params(names: T.any(String, Regexp)).void } + def ignore_constants_named(*names) + save_names_and_patterns(names, :@ignored_constant_names, :@ignored_constant_patterns) + end + # Mark methods matching `names` as ignored. # # Names can be either strings or regexps: # # ~~~rb @@ -29,25 +83,43 @@ # /baz.*/, # ) # end # ~~~ sig { params(names: T.any(String, Regexp)).void } - def ignore_method_names(*names) + def ignore_methods_named(*names) save_names_and_patterns(names, :@ignored_method_names, :@ignored_method_patterns) end + # Mark modules matching `names` as ignored. + # + # Names can be either strings or regexps: + # + # ~~~rb + # class MyPlugin < Spoom::Deadcode::Plugins::Base + # ignore_class_names( + # "Foo", + # "Bar", + # /Baz.*/, + # ) + # end + # ~~~ + sig { params(names: T.any(String, Regexp)).void } + def ignore_modules_named(*names) + save_names_and_patterns(names, :@ignored_module_names, :@ignored_module_patterns) + end + private sig { params(names: T::Array[T.any(String, Regexp)], names_variable: Symbol, patterns_variable: Symbol).void } def save_names_and_patterns(names, names_variable, patterns_variable) ignored_names = instance_variable_set(names_variable, Set.new) ignored_patterns = instance_variable_set(patterns_variable, []) names.each do |name| case name when String - ignored_names << name + ignored_names << name.delete_prefix("::") when Regexp ignored_patterns << name end end end @@ -71,10 +143,16 @@ sig { params(indexer: Indexer, definition: Definition).void } def on_define_accessor(indexer, definition) # no-op end + # Do not override this method, use `on_define_accessor` instead. + sig { params(indexer: Indexer, definition: Definition).void } + def internal_on_define_accessor(indexer, definition) + on_define_accessor(indexer, definition) + end + # Called when a class is defined. # # Will be called when the indexer processes a `class` node. # Note that when this method is called, the definition for the node has already been added to the index. # It is still possible to ignore it from the plugin: @@ -89,10 +167,22 @@ sig { params(indexer: Indexer, definition: Definition).void } def on_define_class(indexer, definition) # no-op end + # Do not override this method, use `on_define_class` instead. + sig { params(indexer: Indexer, definition: Definition).void } + def internal_on_define_class(indexer, definition) + if ignored_class_name?(definition.name) + definition.ignored! + elsif ignored_subclass?(indexer.nesting_class_superclass_name) + definition.ignored! + end + + on_define_class(indexer, definition) + end + # Called when a constant is defined. # # Will be called when the indexer processes a `CONST =` node. # Note that when this method is called, the definition for the node has already been added to the index. # It is still possible to ignore it from the plugin: @@ -107,10 +197,18 @@ sig { params(indexer: Indexer, definition: Definition).void } def on_define_constant(indexer, definition) # no-op end + # Do not override this method, use `on_define_constant` instead. + sig { params(indexer: Indexer, definition: Definition).void } + def internal_on_define_constant(indexer, definition) + definition.ignored! if ignored_constant_name?(definition.name) + + on_define_constant(indexer, definition) + end + # Called when a method is defined. # # Will be called when the indexer processes a `def` or `defs` node. # Note that when this method is called, the definition for the node has already been added to the index. # It is still possible to ignore it from the plugin: @@ -124,11 +222,19 @@ # end # end # ~~~ sig { params(indexer: Indexer, definition: Definition).void } def on_define_method(indexer, definition) + # no-op + end + + # Do not override this method, use `on_define_method` instead. + sig { params(indexer: Indexer, definition: Definition).void } + def internal_on_define_method(indexer, definition) definition.ignored! if ignored_method_name?(definition.name) + + on_define_method(indexer, definition) end # Called when a module is defined. # # Will be called when the indexer processes a `module` node. @@ -145,10 +251,18 @@ sig { params(indexer: Indexer, definition: Definition).void } def on_define_module(indexer, definition) # no-op end + # Do not override this method, use `on_define_module` instead. + sig { params(indexer: Indexer, definition: Definition).void } + def internal_on_define_module(indexer, definition) + definition.ignored! if ignored_module_name?(definition.name) + + on_define_module(indexer, definition) + end + # Called when a send is being processed # # ~~~rb # class MyPlugin < Spoom::Deadcode::Plugins::Base # def on_send(indexer, send) @@ -163,38 +277,77 @@ sig { params(indexer: Indexer, send: Send).void } def on_send(indexer, send) # no-op end + # Do not override this method, use `on_send` instead. + sig { params(indexer: Indexer, send: Send).void } + def internal_on_send(indexer, send) + on_send(indexer, send) + end + private + # DSL support + + sig { params(name: T.nilable(String)).returns(T::Boolean) } + def ignored_class_name?(name) + return false unless name + + ignored_name?(name, :@ignored_class_names, :@ignored_class_patterns) + end + + sig { params(superclass_name: T.nilable(String)).returns(T::Boolean) } + def ignored_subclass?(superclass_name) + return false unless superclass_name + + ignored_name?(superclass_name, :@ignored_subclasses_of_names, :@ignored_subclasses_of_patterns) + end + sig { params(name: String).returns(T::Boolean) } + def ignored_constant_name?(name) + ignored_name?(name, :@ignored_constant_names, :@ignored_constant_patterns) + end + + sig { params(name: String).returns(T::Boolean) } def ignored_method_name?(name) ignored_name?(name, :@ignored_method_names, :@ignored_method_patterns) end - sig { params(const: Symbol).returns(T::Set[String]) } - def names(const) - self.class.instance_variable_get(const) || Set.new + sig { params(name: String).returns(T::Boolean) } + def ignored_module_name?(name) + ignored_name?(name, :@ignored_module_names, :@ignored_module_patterns) end sig { params(name: String, names_variable: Symbol, patterns_variable: Symbol).returns(T::Boolean) } def ignored_name?(name, names_variable, patterns_variable) names(names_variable).include?(name) || patterns(patterns_variable).any? { |pattern| pattern.match?(name) } end + sig { params(const: Symbol).returns(T::Set[String]) } + def names(const) + self.class.instance_variable_get(const) || Set.new + end + sig { params(const: Symbol).returns(T::Array[Regexp]) } def patterns(const) self.class.instance_variable_get(const) || [] end - sig { params(indexer: Indexer, send: Send).void } - def reference_send_first_symbol_as_method(indexer, send) - first_arg = send.args.first - return unless first_arg.is_a?(SyntaxTree::SymbolLiteral) + # Plugin utils - name = indexer.node_string(first_arg.value) - indexer.reference_method(name, send.node) + sig { params(name: String).returns(String) } + def camelize(name) + name = T.must(name.split("::").last) + name = T.must(name.split("/").last) + name = name.gsub(/[^a-zA-Z0-9_]/, "") + name = name.sub(/^[a-z\d]*/, &:capitalize) + name = name.gsub(%r{(?:_|(/))([a-z\d]*)}) do + s1 = Regexp.last_match(1) + s2 = Regexp.last_match(2) + "#{s1}#{s2&.capitalize}" + end + name end end end end end