Sdlc/Dry/HandRolledFetch: restrict match to side-effect-free literals and receivers #1346
Labels
No labels
P0
P1
P2
P3
bug
category/code-change
category/cross-repo
category/eval-run
category/investigation
category/no-test-target
category/owner-ruling
category/prose-artifact
category/recurring-chore
category/structure
category/umbrella
crc-refactor
create
delete
enhancement
filed-by/agent
filed-by/user
frozen
lint-rule
needs-info
needs-triage
next
plugin/cc-architect
plugin/os
plugin/os-adr
plugin/os-aidd-lint
plugin/os-backlog
plugin/os-context
plugin/os-doc-hygiene
plugin/os-sdlc
plugin/os-vault
project/cc-os
ready-for-agent
ready-for-human
recurring
review
rule-proposal
update
waiting
wayfinder:grilling
wayfinder:map
wayfinder:map
wayfinder:research
wayfinder:task
wayfinder:task
wontfix
worklist/deviations
worklist/lint-rule
worklist/new-implement-build
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
jared/cc-os#1346
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
Rewritten 2026-10-27 from docs/lint-lessons/analyses/sdlc-dry-hand-rolled-fetch.md.
Sdlc/Dry/HandRolledFetchrewritesR.key?(K) ? R[K] : DtoR.fetch(K, D)when D isliteral?or a constant.fetchruns D before the lookup, on every call.literal?is true for xstr, dstr, and arrays or hashes that hold calls. Soh.key?(k) ? h[k] : `git rev-parse HEAD`becomesh.fetch(k, `git rev-parse HEAD`), and the command runs on every call. When R is a method call, the rewrite also changes how many times R is called, from 2 to 1.recursive_basic_literal?is true for an xstr, so that check alone does not fix the case. Also, the cop does not reporth.key?(k) ? h[k] : compute, which is the same hand-rolled fetch.Approach
Report every hand-rolled fetch ternary, whatever the default. Autocorrect only when D is a constant default and R is a simple receiver. Otherwise, do not autocorrect, and the message prescribes
R.fetch(K) { D }. Fix this through node shape only: noExcludelist and no disable comment.Required work:
recursive_basic_literal?, or a const node — check: presentrecursive_basic_literal?inplugins/os-sdlc/lib/os_sdlc/cops/dry/hand_rolled_fetch.rbxstrinplugins/os-sdlc/lib/os_sdlc/cops/dry/hand_rolled_fetch.rbdefault_literal?early return, so that a send default is also reported.plugins/os-sdlc/tests/cops/hand_rolled_fetch_test.rbfor each case below.Acceptance criteria
h.key?(k) ? h[k] : `git rev-parse HEAD`, when the cop runs with autocorrect, then it reports one offense and the source does not change."#{x}",[compute],{ a: compute },[`ls`], orcomputeas the default, when the cop runs with autocorrect, then it reports one offense and the source does not change.config.key?(k) ? config[k] : "x", when the cop runs with autocorrect, then it reports one offense and the source does not change.h.key?(k) ? h[k] : "default",DEFAULT,nil, or[1, :a], when the cop runs with autocorrect, then the result ish.fetch(k, <default>).h.key?(k) ? other[k] : "x", when the cop runs, then it reports no offense.Excludelist or disable comment carries this change.Owner sketch
The change lives in
RuboCop::Cop::Sdlc::Dry::HandRolledFetch, in two private predicates (constant_default?(node)andsimple_receiver?(node)) and in the guard around the corrector.Origin
VERDICT: CREATE
Skeptic DROP (depth-1, 0 hits today): proposed new cop Sdlc/Structural/EffectInLookupFallback — a shell command inside a lookup fallback (key? ternary else, fetch default/block,
x[k] || cmd). Revisit when a first real site appears. Draft: docs/lint-lessons/analyses/sdlc-dry-hand-rolled-fetch.md question (b).Body rewritten 2026-10-27 from the false-positive process; analysis and lint lesson merged to main (docs/lint-lessons/analyses/, plugins/os-sdlc/lint-lessons/).
2026-10-27: work started in fast-batch-cop-matchers (AFK). Ticket confirmed valid on main
a02b9458.Follow-up cop for a shell command in a lookup fallback (analysis section 7-8) not filed. Skeptic DROP: 0 code sites in plugins/ and ~/dev/ruby-gems; speculative; depth-1 rule. Note here if it ever shows up.
Resolution
Done: HandRolledFetch reports every key?-ternary; autocorrects to R.fetch(K, D) only for a constant default (const or recursive_basic_literal? with no xstr) on an lvar/ivar/cvar/gvar/const/self receiver; message names the argument form and the block form. Commits
7d30b7f3,3faf91da(fast-batch-cop-matchers, mergedf5310f2b, main89ae8b3b).Evidence: AC1-AC5 fixtures in plugins/os-sdlc/tests/cops/hand_rolled_fetch_test.rb; suite 2825 runs 0 failures; sweep over os-sdlc 0 offenses (no new hits); no Exclude or disable.
Follow-ups: Second cop (shell command in lookup fallback) dropped by skeptic: 0 sites. Lesson unchanged (already matched).