v1 升级说明

Cop 升级指南

您的自定义 cop 应该继续在 v1 中工作。

但是,建议您按照以下步骤调整它们以使用 v1 API

1) 您的类应该继承自 RuboCop::Cop::Base 而不是 RuboCop::Cop::Cop

2) 找到您对 add_offense 的调用,并确保您将 AST::Node::Parser::Source::Comment::Parser::Source::Range 作为第一个参数传递,并且没有 location: 命名参数。

示例

# Before
class MySillyCop < Cop
  def on_send(node)
    if node.method_name == :-
      add_offense(node, location: :selector, message: "Be positive")
    end
  end
end

# After
class MySillyCop < Base
  def on_send(node)
    if node.method_name == :-
      add_offense(node.loc.selector, message: "Be positive")
    end
  end
end

如果您的类支持自动更正

您的类必须 extend AutoCorrector

corrector 现在从 add_offense 中产生。将您方法 autocorrect 的代码移动到该块中,并且不要将您的更正包装在 lambda 中。Corrector 更强大,现在可以 merge

示例

# Before
class MySillyCorrectingCop < Cop
  def on_send(node)
    if node.method_name == :-
      add_offense(node, location: :selector, message: 'Be positive')
    end
  end

  def autocorrect(node)
    lambda do |corrector|
      corrector.replace(node.loc.selector, '+')
    end
  end
end

# After
class MySillyCorrectingCop < Base
  extend AutoCorrector

  def on_send(node)
    if node.method_name == :-
      add_offense(node.loc.selector, message: 'Be positive') do |corrector|
        corrector.replace(node.loc.selector, '+')
      end
    end
  end
end

实例变量

不要使用 RuboCop 的内部实例变量。如果您使用的是 @processed_source,请使用 processed_source。如果您需要访问实例变量,请打开一个包含您的用例的 issue。

默认情况下,对于给定的 processed_source,Cop 实例只会调用一次,因此在调查开始时实例变量将未初始化。使用 @cache ||= …​ 就可以了。如果你想初始化一些实例变量,回调 on_new_investigation 是最好的地方。

class MyCachingCop < Base
  def on_send(node)
    if my_cached_data[node]
      @counts(node.method_name) += 1
      #...
    end
  end

  # One way:
  def my_cached_data
    @data ||= processed_source.comments.map { # ... }
  end

  # Another way:
  def on_new_investigation
    @counts = Hash.new(0)
    super  # Be nice and call super for callback
  end
end

其他 API 更改

如果你的 Cop 使用 investigateinvestigate_post_walkjoin_force? 或内部类,例如 CorrectorCommissionerTeam,这些都已更改。请参阅 详细的 API 更改

升级规范

强烈建议你在规范中使用 expect_offense / expect_correction / expect_no_offense,例如:

require 'rubocop/rspec/support'

RSpec.describe RuboCop::Cop::Custom::MySillyCorrectingCop, :config do
  # No need for `let(:cop)`
  it 'is positive' do
    expect_offense(<<~RUBY)
      42 + 2 - 2
             ^ Be positive
    RUBY

    expect_correction(<<~RUBY)
      42 + 2 + 2
    RUBY
  end

  it 'does not register an offense for calls to `despair`' do
    expect_no_offenses(<<~RUBY)
      "don't".despair
    RUBY
  end
end

在极少数情况下,如果你直接使用类 RuboCop::Cop::Corrector,它已经改变了一些,但你可以使用 RuboCop::Cop::Legacy::Corrector 来简化你的过渡,它旨在与旧版本兼容。你需要 require 'rubocop/cop/legacy/corrector'

详细的 API 更改

本节列出了 API 的所有更改(无论大小)。它适用于 RuboCop 的维护者;大多数 Cop 作者不会受到这些更改的影响,因此不是目标受众。

基类

旧版本:Cop 继承自 Cop::Cop

当前:Cop 继承自 Cop::Base。拥有不同的基类使实现更加干净,并可以轻松地指示正在使用哪个 API。Cop::Cop 继承自 Cop::Base 并细化了一些方法以实现向后兼容性。

add_offense API

参数

旧版本:接口允许使用 node,并带有可选的 location(符号或范围)或带有强制范围的范围作为位置。一些 Cop 滥用了 node 参数并传递了非常不同的内容。

当前:传递一个范围(或作为 node.loc.expression 的快捷方式的节点),没有 location:。不允许滥用。

对更改进行重复数据删除

两者都对 range 进行重复数据删除,并且根本不会处理重复的违规行为。

遗留: 如果违规发生在同一个node上但范围不同:被视为多个违规,但只调用一次自动修正。

当前: 不适用,也不需要自动修正的 API。

yield

两者在相同条件下都会 yield(除非该行禁用了 cop),但是

遗留: 在违规被添加到 #offenses 后 yield

当前: 在违规被添加到 #offenses 之前 yield。

即使是遗留模式也会 yield 一个修正器,但如果开发人员使用它,会引发错误,要求她继承自 Cop::Base

自动修正

#autocorrect

遗留: 调用 autocorrect,除非它被禁用或自动修正被关闭。

当前: yield 一个修正器,除非它被禁用。如果自动修正被关闭等,修正器将被忽略。不支持 autocorrect 方法,但如果该方法仍然定义,会发出警告。

空修正

遗留: autocorrect 可能会在无法实际进行修正的情况下返回 nil / false

当前: 没有特殊的 API。没有进行修正的情况会自动检测到。

修正时机

遗留: lambda 仅在流程的后期被调用,并且仅在特定条件下被调用(如果自动修正设置已打开等)。

当前: 修正立即构建(假设该行没有禁用 cop),并在流程的后期应用。

异常处理

两者:Commissioner 会在分析过程中(除非 option[:raise_error])捕获所有 StandardError,并在其错误列表中存储相应的 ErrorWithAnalyzedFileLocation。这在调用 cop 的 on_send & al. 或调用 investigate / investigate_post_walk 回调时完成。

遗留: 自动修正 cop 对错误的处理方式不同,具体取决于错误发生的时间。一些错误被静默忽略。其他错误被捕获如上。其他错误导致崩溃。Team 中的一些代码会捕获错误并将它们添加到错误列表中,但我认为代码不起作用。

当前: Team 不再有任何特殊的错误处理,因为潜在的异常发生在 Commissioner 运行时。

其他错误处理

遗留: 覆盖错误被静默忽略。调用 insert_before 时,范围超出源代码范围会被静默修复。

当前: 不会忽略此类错误。虽然允许一个 Cop 的修正覆盖另一个 Cop 的修正,但任何给定的 Cop 不应该发出相互覆盖的修正,或者具有无效范围的修正,否则这些修正将被列在处理错误中。

#corrections

旧版本: 修正保存在 #corrections 中,作为 lambda 数组。编写了一个代理来保持与 cop.corrections << ...cop.corrections.concat ... 等的兼容性。

当前: 修正保存在 current_corrector 中,这是一个继承自 Source::TreeRewriterCorrector

#support_autocorrect?

旧版本: 是一个实例方法。

当前: 现在是一个类方法。

联合力量

旧版本: join_force?(force_class) 会对每个 force 类进行调用。

当前: self.joining_forces 现在用于返回要加入的 force(或 force 数组)。

Cop 持久性

Cop 现在可以在文件之间持久化。默认情况下,每个源都会创建新的 Cop 实例。请参阅 support_multiple_source? 文档。

内部类

Corrector

旧版本: initialize 接受第二个参数(一个 lambda 数组)。如果需要,可以通过 Legacy::Corrector 获取。

当前: 继承自 parserTreeRewriterinitialize 没有第二个参数;因为可以合并修正器,所以不需要。

Commissioner & Team

为了更好地分离关注点,进行了重构,使其可重用,并提供更好的结果报告和错误处理。

其他 API 更改

  • 澄清了 Commissioner 的内部 API。它调用 begin_investigation 并接收 complete_investigation 中的结果。

  • 新方法 add_global_offense 用于不附加到特定位置的违规行为;目前仅用于语法错误。

  • #offenses:不再可访问。

  • 回调 investigate(processed_source)investigate_post_walk(processed_source) 被重命名为 on_new_investigationon_investigation_end,并且不接受参数;所有 on_ 回调都应该依赖于 processed_source

  • #find_location 已弃用。

  • Correction 已弃用。

  • 一些注册表访问方法已从 Cop 移动到 Registry,既是为了正确性(例如,MyCop.qualified_cop_name 不起作用,也没有意义),也是为了让 Cop::Cop 不再包含任何必要的代码。保持向后兼容性。

    • Cop.registry => Registry.global

    • Cop.all => Registry.all

    • Cop.qualified_cop_name => Registry.qualified_cop_name

  • 用于跟踪配置选项的排除限制的 ConfigurableMax 混合已弃用。请改用 exclude_limit ParameterName