Class: RuboCop::Cop::Homebrew::UnreferencedLet Private
- Extended by:
- AutoCorrector
- Includes:
- RangeHelp
- Defined in:
- rubocops/unreferenced_let.rb,
sorbet/rbi/dsl/rubo_cop/cop/homebrew/unreferenced_let.rbi
Overview
This class is part of a private API. This class may only be used in the Homebrew/brew repository. Third parties should avoid using this class if possible, as it may be removed or changed without warning.
Flags lazy let declarations whose name is never referenced. A lazy let(:name) { ... }
is only evaluated when name is called, so an unreferenced one is dead code -- its block
never runs -- and is deleted.
Eager let! is intentionally out of scope: it runs its block before every example for its
side effect even when unreferenced, so it cannot simply be deleted. Only plain let is
handled here.
Detection is file-scoped: a let referenced only from another file (through a shared
example or an included test harness) cannot be seen, so the cop stays conservative and
prefers false negatives over false positives:
- a name defined more than once in the file by
let/let!/subject(an override /superchain, including asubjectthat overrides aletof the same name) is never flagged; - a
letdeclared lexically inside ashared_examples/shared_examples_for/shared_contextblock is skipped (its consumers live in other files); - every
letin a file that usesit_behaves_like/it_should_behave_like/include_examples/include_contextis skipped, because an included shared block may reference the binding by a name we cannot follow statically; let(:cop_config)is skipped: it is a rubocop-rspec contract consumed by the:configshared context, not by a reference in the spec file; and- every
letin a file that reflectively dispatches through a name we cannot resolve statically (e.g.send("expected_#{type}")) is skipped, since anyletcould be the target. A name counts as referenced if it is called bare (foo), appears as a symbol (:foo) anywhere but the let's own name argument, or appears as an identifier-shaped token inside any string/heredoc literal -- covering dynamic dispatch,:fooentries in data tables the spec later dispatches on, and bindings named only inside raw SQL/GraphQL text.
Because a bare :foo symbol anywhere counts as a reference, commonly-named lets
(let(:formula), let(:cask), let(:id)) are essentially never flagged. This conservative
bias means the cop realistically only deletes distinctively-named dead lets; it is not a
complete dead-let finder.
Example
# bad (name never referenced -- deleted, the block never runs)
let(:unused) { create(:thing) }
# good
let(:thing) { create(:thing) }
it { expect(thing).to be_present }
Constant Summary collapse
- DEFINITION_METHODS =
This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
[:let, :let!, :subject].freeze
- FRAMEWORK_RESERVED_NAMES =
This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
lets consumed by a test framework rather than by a reference in the spec file. RuboCop's own:configshared context (used by every cop spec) readscop_config,other_cops,cop_optionsandgem_versionsby name from inside the framework, so they are live even though the spec never names them. [:cop_config, :other_cops, :cop_options, :gem_versions].freeze
- DYNAMIC_DISPATCH_METHODS =
This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
Reflective dispatch methods whose target is the first argument. When that argument is not a statically-resolvable name (a
symor plainstr) -- e.g.send("expected_#{type}")-- the called name cannot be known, so the whole file is left untouched. [:send, :public_send, :__send__, :try, :try!, :method, :public_method, :respond_to?].freeze
- IDENTIFIER_IN_STRING =
This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
Identifier-shaped tokens inside a string/heredoc literal. A
letwhose name appears only inside string text -- e.g. a binding or column referenced in raw SQL/GraphQL the spec later executes -- counts as referenced, so it is not deleted. /[A-Za-z_]\w*[!?]?/- MSG =
This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
"Remove unreferenced `let(:%<name>s)` -- its name is never used, so the block never runs."- RESTRICT_ON_SEND =
This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.
[:let].freeze
Instance Method Summary collapse
-
#definition_name(node) ⇒ T.untyped
private
The name symbol of any definition (
let/let!/subject) in any block form -- used to count how many times a name is defined, so override /superchains (including asubjectthat overrides aletof the same name) are never flagged. - #on_send(node) ⇒ void private
Instance Method Details
#definition_name(node) ⇒ T.untyped
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
The name symbol of any definition (let/let!/subject) in any block form -- used to
count how many times a name is defined, so override / super chains (including a
subject that overrides a let of the same name) are never flagged.
79 80 81 |
# File 'rubocops/unreferenced_let.rb', line 79 def_node_matcher :definition_name, <<~PATTERN (any_block (send nil? {#{DEFINITION_METHODS.map { |method| ":#{method}" }.join(" ")}} (sym $_) ...) ...) PATTERN |
#on_send(node) ⇒ void
This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.
This method returns an undefined value.
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'rubocops/unreferenced_let.rb', line 84 def on_send(node) return unless node.receiver.nil? name_argument = node.first_argument return unless name_argument&.sym_type? block = node.block_node return unless block name = name_argument.value return if exempt_from_deletion?(name, block) add_offense(node.loc.selector, message: format(MSG, name:)) do |corrector| corrector.remove(removal_range(block)) end end |