RuboCop AST

此 gem 引入了 RuboCop 的两个核心类

  • RuboCop::AST::Node - 这是对 parser gem 的 Node 类的扩展,它添加了一个更简单、更强大的面向对象 API,使处理节点变得更容易。

  • RuboCop::AST::NodePattern - 一种类似正则表达式的遍历和匹配抽象语法树中节点的方法。请参阅 "节点模式" 以了解 NodePattern 的功能。

此 gem 可以独立于主 RuboCop gem 使用。它是在版本 0.84 中从 RuboCop 中提取出来的,它唯一的依赖是 parser gem,rubocop-ast 对其进行了扩展。

原理

虽然使用 parser 的 AST 表示相当容易(尤其是与 Ruby 内置 ripper 库的 AST 相比),但我们仍然认为有些地方可以改进。

  • 处理 AST 节点的规范方法是像数组一样解构节点,这会导致代码难以阅读。

  • 查找复杂的 AST 节点模式需要大量样板代码。

  • 没有简单的方法来区分某些类型的 AST 节点 - 例如前缀与后缀条件语句。

  • 没有简单的方法来获取某个节点的父节点。

rubocop-ast 应运而生,旨在解决这些问题。这个库作为 RuboCop 的一部分发展了多年,最终被剥离出来,希望它能对其他基于 parser 的项目有所帮助。

RuboCop::AST::NodeparserNode 类提供了一个包装器(换句话说,RuboCop::AST::Node < Parser::AST::Node)。除了提供许多方法来简化处理之外,包装器类还提供了一种检查节点 **父节点** 的方法,而 parser 节点不支持此功能。

以下是一些使用 parserrubocop-ast 的示例。

parser

rubocop-ast

# type = :if
is_if = node.loc.keyword == 'if'
if_branch = node.children[1]
else_branch = node.children[2]
has_elsif_branch = node.children[2].type == :if && node.children[2].keyword == 'elsif'
# type = :if
is_if = node.if?
if_branch = node.if_branch
else_branch = node.else_branch
has_elsif_branch = node.elsif_conditional?
# type = :hash
pairs = node.children
pairs.each do |pair_node|
  key = pair_node.children[0]
  value = pair_node.children[1]
  do_something(key, value)
end
# type = :hash
node.each_pair do |pair_node|
  do_something(pair_node.key, pair_node.value)
end

示例用法

class MyRule < Parser::AST::Processor
  include RuboCop::AST::Traversal

  def on_sym(node)
    puts "I found a symbol! #{node.value}"
  end
end

source = RuboCop::AST::ProcessedSource.new(code, 2.7)
rule = MyRule.new
source.ast.each_node { |n| rule.process(n) }

在 RuboCop AST 中,您可以指定 Prism 作为解析器引擎后端。

如果通过 Bundler 运行,请先将 gem 'prism' 添加到您的 Gemfile 中。

gem 'prism'

通过指定 parser_engine: :parser_prism,可以使用 Prism 进行解析。

# Using the Parser gem with `parser_engine: parser_whitequark` is the default.
ProcessedSource.new(@options[:stdin], ruby_version, file, parser_engine: :parser_prism)

这是一个实验性功能。如果您在 Prism 和 Parser gem 之间遇到任何不兼容性,请查看以下 URL:https://github.com/ruby/prism/issues?q=is%3Aissue+is%3Aopen+label%3Arubocop