Documentation
¶
Overview ¶
Package identifier implements mutation operators whose eligibility depends on static type information, answering questions purely syntactic operators (control, expression, literal, operator, statement) cannot: "is this identifier interchangeable with that one?"
Why the scope is restricted so hard ¶
The strconv ParseUint bug this operator is modelled on (see PROPOSAL.md's "Known gap" section) was a wrong-identifier substitution: a bitSize-scoped local swapped for a package-level named constant. The natural generalisation — "any type-compatible identifier in scope" — is deliberately not what this operator does. Walking every *ast.Ident use against every same-type identifier Go's scoping rules make visible at that point (locals, params, package vars, package consts, dot-imports) would make Applies true for a large fraction of identifier references in an ordinary file and offer one mutation per same-type sibling for each — on a function with a dozen int-typed locals, that one function alone could dwarf what the other twelve operators produce combined.
v1's restriction is package-level const-for-const only, with an exact type match (types.Identical, not just a shared underlying kind — a looser match would mostly inflate the NotViable count with mutants Go's type system was always going to reject) and a same-declaration-group requirement: two constants are only offered as swaps for each other when they are declared in the same parenthesized `const ( ... )` block, or — for a constant declared on its own — the same file. This targets the common "picked the wrong sibling constant" bug shape (adjacent error codes, size limits, enum-like values) while keeping the candidate set per identifier small and bounded by how many constants a block or file realistically holds, rather than by total package size.
This does not reproduce the strconv bug exactly — that swap was local-to- package-const, not const-to-const — and is a known, deliberate v1 limitation.
v2: local-variable-to-package-constant swap (identifier/localconstswap) ¶
[localConstSwap], also in this file, is the v2 extension that closes the strconv gap directly: it allows a *local* variable's use — not just another package-level constant's use — to be swapped for a same-type package-level constant. It is a separate registered operator (a separate node shape needs a separate Applies/Mutate pair, following this codebase's existing precedent of splitting related- but-distinct mutation shapes into distinct operators — see how control/if and control/else are two operators despite both editing parts of the same *ast.IfStmt), not a change to [constSwap] above.
Why this operator opts into TypedMutator ¶
Every other operator in this codebase implements only mutator.Mutator and is purely syntactic: Applies/Mutate see an *ast.Node and nothing else. This operator additionally needs to know what an identifier resolves to and what it is typed as, which only go/types can answer. It gets that by implementing mutator.TypedMutator: the registry-held instance constructed by init is inert (its Applies always reports false, since it has no type information to consult), and the engine calls WithScope once per package, after type-checking, to obtain a package-bound instance that does the real work. WithScope always returns a new value rather than mutating the receiver, so the registry-held instance stays stateless and shareable exactly as mutator.Mutator's contract requires, and the bound instance's state (the type info, the package, and the const groupings derived from them) is set once, before the value is ever used by more than one file's walk, and never written to afterwards — safe for the concurrently-mutated files of one package to share.
Index ¶
Constants ¶
const ConstSwapName = "identifier/constswap"
ConstSwapName is the registry name of the package-level const-swap operator.
const LocalConstSwapName = "identifier/localconstswap"
LocalConstSwapName is the registry name of the v2, local-variable-to- package-constant swap operator.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
This section is empty.