lib/mutant/matcher/method/metaclass.rb
# frozen_string_literal: true module Mutant class Matcher class Method # Matcher for metaclass methods # i.e. ones defined using class << self or class << CONSTANT. It might?? # work for methods defined like class << obj, but I don't think the # plumbing will be in place in the subject for that to work class Metaclass < self # New singleton method matcher # # @return [Matcher::Method::Singleton] def self.new(scope:, target_method:) super(scope:, target_method:, evaluator: Evaluator) end # Metaclass method evaluator class Evaluator < Evaluator # Terminology note: the "receiver" is the `self` in `class << self` CONST_NAME_INDEX = 1 MATCH_NODE_TYPE = :def NAME_INDEX = 0 SCLASS_RECEIVER_INDEX = 0 SUBJECT_CLASS = Subject::Method::Metaclass RECEIVER_WARNING = 'Can only match :def inside :sclass on ' \ ':self or :const, got :sclass on %p ' \ 'unable to match' private def match?(node) name?(node) && metaclass_receiver?(node) end def metaclass_receiver?(node) candidate = metaclass_containing(node) candidate && metaclass_target?(candidate) end def metaclass_containing(node) AST::FindMetaclassContaining.call(ast:, target: node) end def name?(node) node.children.fetch(NAME_INDEX).equal?(method_name) end def metaclass_target?(node) receiver = node.children.fetch(SCLASS_RECEIVER_INDEX) case receiver.type when :self true when :const sclass_const_name?(receiver) else env.warn(RECEIVER_WARNING % receiver.type) nil end end def sclass_const_name?(node) name = node.children.fetch(CONST_NAME_INDEX) name.to_s.eql?(scope.unqualified_name) end end # Evaluator private_constant(*constants(false)) end # Metaclass end # Method end # Matcher end # Mutant