Создать полицейский рубокоп, который срабатывает при вызове метода без блока

Я пытаюсь разработать новый коп (на основе эти рекомендации) и ломаю голову, пытаясь получить правильный шаблон узла.

Я хочу, чтобы полицейский зарегистрировал правонарушение, когда X.some_method называется без предоставления блока.
то есть X.some_method является нарушением, а X.some_method { blah } нет.

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

Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Пошаговое руководство по созданию собственного Slackbot: От установки до развертывания
Шаг 1: Создание приложения Slack Чтобы создать Slackbot, вам необходимо создать приложение Slack. Войдите в свою учетную запись Slack и перейдите на...
2
0
239
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

По-видимому, в проанализированном 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

Другие вопросы по теме