· via dev.to (home feed)
Go 1.26 //go:fix inline lets go fix rewrite every caller of a deprecated function
Go 1.26's rebuilt go fix includes a source-level inliner: a //go:fix inline directive on a deprecated wrapper automatically rewrites every caller, even in downstream modules.

A rebuilt go fix lands in Go 1.26
According to a dev.to article by Gabriel Anhaia, Go 1.26 — which the article dates to February 2026 — removed the old go fix fixers entirely and rebuilt the command on the same analysis framework that powers go vet. The practical consequence is that any analyzer capable of flagging a problem in go vet can now also apply the repair in go fix.
Most of the bundled analyzers are modernizers: one replaces interface{} with any, another collapses if/else chains into min or max calls. The article points readers to two Go blog posts by Alan Donovan — dated 17 February and 10 March 2026 in the piece — as the primary documentation. One analyzer stands apart: inline, which carries out whatever migration a library author has encoded in a //go:fix inline comment on their own API.
The canonical case is ioutil.ReadFile, a one-line wrapper around os.ReadFile since Go 1.16 whose Deprecated note historically only made linters grumble while call sites stayed put. With the directive on the wrapper, anyone who runs go fix has those calls rewritten to os.ReadFile directly, import included.
The wrapper has to be thin
The article's worked example is a money package whose old FormatCents function is marked Deprecated and given a //go:fix inline line. The crucial detail is the body: it must do nothing but call the replacement, Format with DefaultLocale. Guidance from the inline analyzer, cited in the article, recommends writing deprecated functions as thin wrappers over their successors.
The inliner works by pasting the wrapper's body into each call site, substituting arguments for parameters and qualifying names for the caller's package. If the deprecated function contains its own logic, the tool will still inline it — but every caller receives a copy of that logic rather than a call to the new API, which defeats the purpose of the migration. Refactor the wrapper first, then add the directive. Anhaia is candid that he did not execute the sample module himself; the diffs he shows follow the inliner's documented rules rather than a verified run.
Preview, then apply
The recommended workflow, per the article: start from a clean git tree so the resulting change contains only go fix's edits. Preview with go fix -diff ./...; to run only the inliner and skip the modernizers, use go fix -inline ./..., again with -diff to preview. Once the diff looks right, drop -diff and the tool rewrites the files in place, so build, test, review and commit.
The mechanism crosses module boundaries. According to the analyzer documentation cited in the article, calls made from other packages are flagged too — so a downstream team that upgrades a dependency and runs go fix ./... migrates in its own repository, with no pull request needed from the library's maintainers. The directive also works on constants that alias other constants and on type aliases, covering rename-style and relocation-style migrations.
What the inliner will not touch
The tool's design rule is that it must never change what a program does; when it cannot prove a rewrite is safe, it either falls back to something clumsier or does nothing. The article highlights three cases:
Unexported references in the wrapper's body. If the body mentions a lowercase identifier, callers in other packages cannot legally name it, and the inliner rejects the rewrite. Everything the body touches must be exported, or the migration only works inside the author's own package.
Arguments with side effects. Where substitution could reorder effects or repeat them once per loop iteration, the inliner binds the argument to a fresh variable first and uses that variable in the inlined body — correct, but sometimes odd-looking. A flag, -inline.allow_binding_decl=false, skips such call sites instead.
Bodies containing defer. Inlining a deferred call into a caller would delay it until the caller returns. The gopls inliner handles this by wrapping the body in an immediately invoked closure, but go fix declines those rewrites, so a wrapper using defer will not migrate anyone.
Why it matters
A Deprecated comment has always been advice, and advice loses to a backlog. //go:fix inline turns that advice into an executable migration: one comment on a wrapper, and every consumer who runs go fix is moved to the new API without hand-editing. For maintainers of large Go codebases, that shifts the cost of an API change from many downstream afternoons to a one-time refactor on the library side. The restrictions — exported-only references, no defer — bound what it can do, but the common cases of renaming, replacing and relocating APIs are now largely automatable.
- #golang
- #developer-tools
- #static-analysis
- #api-migration
- #deprecation