🔍 Duplicate Code Detected: fmt.Sprintf→strconv Fix Builders
Analysis of commit 31e5fda
Assignee: @copilot
Summary
The sprintfbool and sprintfint analyzers in pkg/linters/ contain essentially identical candidate-collection and suggested-fix logic for rewriting fmt.Sprintf("%t"/"%d", x) into a strconv call. The qualifier-resolution block and the buildImportEdits function (which adds strconv and removes an orphaned fmt import) are near-verbatim copies.
Duplication Details
Pattern: strconv-qualifier resolution + import-swap fix
- Severity: Medium
- Occurrences: 2 analyzers (with 3+ duplicated blocks across them)
- Locations:
pkg/linters/sprintfbool/sprintfbool.go (buildImportEdits lines 167-197, replacementForCall lines 199-233)
pkg/linters/sprintfint/sprintfint.go (buildItoaFix lines 96-140, buildImportEdits lines 142-159)
- Code Sample (qualifier resolution — identical in both):
qualifier := strconvPkg
if file != nil {
if localName, imported := astutil.ImportedAs(file, pass.TypesInfo, strconvPkg); imported {
if localName == "." || localName == "_" {
return /* no fix */
}
qualifier = localName
}
}
if astutil.QualifierShadowed(pass.Pkg, call.Pos(), qualifier, strconvPkg) {
return /* no fix */
}
buildImportEdits in both files computes orphanFmt and calls astutil.SwapPkgImportEdits(pass, file, strconvPkg, fmtPkg, orphanFmt) with the same seenImportFiles guard. Both files also redeclare the strconvPkg/fmtPkg constants.
Impact Analysis
- Maintainability: Any change to how the fix handles aliases, blank/dot imports, shadowing, or
fmt orphan removal must be mirrored in both analyzers.
- Bug Risk: The two copies already differ (bool version adds
computeOrphanFmtStatus/orphanFmtByFile plumbing, int version inlines CountPkgUsesInFile(...) == 1), making divergence likely.
- Code Bloat: ~60 duplicated lines of fix-construction logic.
Refactoring Recommendations
-
Extract a shared "Sprintf → strconv" fix builder
- Add an
astutil/internal helper BuildStrconvSwapFix(pass, call, argText, replacementFunc string, seen) []analysis.SuggestedFix encapsulating qualifier resolution, shadow checks, and the fmt→strconv import swap.
sprintfbool passes "FormatBool"; sprintfint passes "Itoa".
- Estimated effort: 3-4 hours
- Benefits: one implementation of the import/alias/shadow edge cases; future
sprintf* linters reuse it.
-
Share candidate predicate helpers
- Factor the common
fmt.Sprintf selector/arg-count/format-literal checks into a reusable predicate.
Implementation Checklist
Analysis Metadata
- Analyzed Files: pkg/linters (77 analyzer packages)
- Detection Method: Serena semantic code analysis
- Commit: 31e5fda
- Analysis Date: 2026-10-03T22:48:41.609Z
Generated by 🔍 Duplicate Code Detector · pi · opus48 · 361.7 AIC · ⌖ 46.9 AIC · ⊞ 4.4K · ◷
🔍 Duplicate Code Detected: fmt.Sprintf→strconv Fix Builders
Analysis of commit 31e5fda
Assignee:
@copilotSummary
The
sprintfboolandsprintfintanalyzers inpkg/linters/contain essentially identical candidate-collection and suggested-fix logic for rewritingfmt.Sprintf("%t"/"%d", x)into astrconvcall. The qualifier-resolution block and thebuildImportEditsfunction (which addsstrconvand removes an orphanedfmtimport) are near-verbatim copies.Duplication Details
Pattern: strconv-qualifier resolution + import-swap fix
pkg/linters/sprintfbool/sprintfbool.go(buildImportEditslines 167-197,replacementForCalllines 199-233)pkg/linters/sprintfint/sprintfint.go(buildItoaFixlines 96-140,buildImportEditslines 142-159)buildImportEditsin both files computesorphanFmtand callsastutil.SwapPkgImportEdits(pass, file, strconvPkg, fmtPkg, orphanFmt)with the sameseenImportFilesguard. Both files also redeclare thestrconvPkg/fmtPkgconstants.Impact Analysis
fmtorphan removal must be mirrored in both analyzers.computeOrphanFmtStatus/orphanFmtByFileplumbing, int version inlinesCountPkgUsesInFile(...) == 1), making divergence likely.Refactoring Recommendations
Extract a shared "Sprintf → strconv" fix builder
astutil/internal helperBuildStrconvSwapFix(pass, call, argText, replacementFunc string, seen) []analysis.SuggestedFixencapsulating qualifier resolution, shadow checks, and thefmt→strconvimport swap.sprintfboolpasses"FormatBool";sprintfintpasses"Itoa".sprintf*linters reuse it.Share candidate predicate helpers
fmt.Sprintfselector/arg-count/format-literal checks into a reusable predicate.Implementation Checklist
astutilAnalysis Metadata