Class: RuboCop::Cop::Browserctl::TypedError

Inherits:
RuboCop::Cop::Base
  • Object
show all
Defined in:
lib/browserctl/rubocop/cops/typed_error.rb

Overview

Enforces that any explicit ‘code:` keyword passed to a `raise` of a `Browserctl::*` error refers to a constant from `Browserctl::Error::Codes` rather than a free-form string literal.

Subclasses with their own ‘default_code` are trusted (the cop does not try to statically resolve `default_code` across files); the contract is enforced by the unit test suite. This cop’s job is to catch the specific failure mode of inlining a stale code string at the raise site and bypassing the canonical enum.

The pattern is intentionally narrow. The full default_code-vs-Codes reconciliation lives in ‘lib/browserctl/errors.rb` and is covered by `spec/unit/errors_spec.rb`.

The cop was tightened in v0.14 WS-1 PR 5 to remove the previous “canonical SCREAMING_SNAKE string literals are also fine” escape hatch — every ‘code:` must now be a constant reference so renames in `Codes` propagate through the codebase via the constant, not via a stale whitelist baked into this cop.

Examples:

# bad — any string literal, even one that happens to match a
# canonical code today, drifts when the enum is renamed
raise Browserctl::Error, "state expired", code: "STATE_EXPIRED"

# good — Codes constant reference
raise Browserctl::Error, "state expired",
      code: Browserctl::Error::Codes::STATE_EXPIRED

# good — typed subclass relies on its own default_code
raise Browserctl::SelectorNotFound, "no such selector"

Constant Summary collapse

MSG =
"Browserctl raise: `code:` must reference Browserctl::Error::Codes::* — " \
"got string literal %<value>p. See lib/browserctl/error/codes.rb."

Instance Method Summary collapse

Instance Method Details

#on_send(node) ⇒ Object



63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/browserctl/rubocop/cops/typed_error.rb', line 63

def on_send(node)
  return unless node.method?(:raise)

  [
    browserctl_raise_with_code(node),
    browserctl_raise_new_with_code(node)
  ].compact.each do |code_value|
    next unless code_value.str_type?

    add_offense(code_value, message: format(MSG, value: code_value.value))
  end
end