Я пытаюсь разработать новый коп (на основе эти рекомендации) и ломаю голову, пытаясь получить правильный шаблон узла.
Я хочу, чтобы полицейский зарегистрировал правонарушение, когда X.some_method называется без предоставления блока.
то есть X.some_method является нарушением, а X.some_method { blah } нет.
Я получил правильный образец для идентификации X.some_method, который является '(send (const nil? :X) :some_method ...'.
Но не знаете, как создать шаблон для «не дан блок»?

По-видимому, в проанализированном AST, когда узлу дается блок, этот узел затем представляется как Первый ребенок этого блока. то есть
[92] pry(RuboCop)> node # `X.some_method(something) { do_something }`
=> s(:block,
s(:send,
s(:const, nil, :X), :some_method,
s(:send, nil, :something)),
s(:args),
s(:send, nil, :do_something))
И мы можем проверить это, используя экземпляры Rubocop::AST.
Вот полная реализация (включая опцию для нескольких имен методов):
MSG = 'Avoid using `X.%<method>s` without providing a block.'
def_node_matcher :x_method, '(send (const nil? :X) ${:some_method :another_method} ...)'
def on_send(node)
x_method(node) do |method_name|
return if !method_name || first_child_of_block?(node)
add_offense(node, location: :selector, message: format(MSG, method: method_name))
end
end
private
# checks if the given node's parent is a block, and the given node is its first child,
# which would mean that the block is supplied to the given node (i.e `node { block }`)
def first_child_of_block?(node)
return false unless (parent = node.parent)
return false unless parent.type == :block
parent.children.first == node
end