1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
|
# typed: strict
# frozen_string_literal: true
require "rubocop"
module RuboCop
module Cop
module RubyLsp
# Avoid using register without handler method, or handler without register.
#
# @example
# # Register without handler method.
#
# # bad
# class MyListener
# def initialize(dispatcher)
# dispatcher.register(
# self,
# :on_string_node_enter,
# )
# end
# end
#
# # good
# class MyListener
# def initialize(dispatcher)
# dispatcher.register(
# self,
# :on_string_node_enter,
# )
# end
#
# def on_string_node_enter(node)
# end
# end
#
# @example
# # Handler method without register.
#
# # bad
# class MyListener
# def initialize(dispatcher)
# dispatcher.register(
# self,
# )
# end
#
# def on_string_node_enter(node)
# end
# end
#
# # good
# class MyListener
# def initialize(dispatcher)
# dispatcher.register(
# self,
# :on_string_node_enter,
# )
# end
#
# def on_string_node_enter(node)
# end
# end
class UseRegisterWithHandlerMethod < RuboCop::Cop::Base
MSG_MISSING_HANDLER = "Registered to `%{listener}` without a handler defined."
MSG_MISSING_LISTENER = "Created a handler without registering the associated `%{listener}` event."
def_node_search(
:find_all_listeners,
"(send
(_ :dispatcher) :register
(self)
$(sym _)+)",
)
def_node_search(
:find_all_handlers,
"$(def [_ #valid_event_name?] (args (arg _)) ...)",
)
def on_new_investigation
return if processed_source.blank?
listeners = find_all_listeners(processed_source.ast).flat_map { |listener| listener }
handlers = find_all_handlers(processed_source.ast).flat_map { |handler| handler }
add_offense_to_listeners_without_handler(listeners, handlers)
add_offense_handlers_without_listener(listeners, handlers)
end
private
#: (Symbol event_name) -> bool
def valid_event_name?(event_name)
/^on_.*(node_enter|node_leave)$/.match?(event_name)
end
#: (Array[RuboCop::AST::SymbolNode] listeners, Array[RuboCop::AST::DefNode] handlers) -> void
def add_offense_to_listeners_without_handler(listeners, handlers)
return if listeners.none?
listeners
.filter { |node| handlers.map(&:method_name).none?(node.value) }
.each { |node| add_offense(node, message: format(MSG_MISSING_HANDLER, listener: node.value)) }
end
#: (Array[RuboCop::AST::SymbolNode] listeners, Array[RuboCop::AST::DefNode] handlers) -> void
def add_offense_handlers_without_listener(listeners, handlers)
return if handlers.none?
handlers
.filter { |node| listeners.map(&:value).none?(node.method_name) }
.each { |node| add_offense(node, message: format(MSG_MISSING_LISTENER, listener: node.method_name)) }
end
end
end
end
end
|