Skip to content

Enforce-readiness: three clean autofix linters (appendoneelement, timenowsub, stringsjoinone) have zero production violations — [Content truncated due to length] #47916

Description

@github-actions

Summary

Three registered analyzers — appendoneelement, timenowsub, and stringsjoinone — are clean, fully type-resolved, autofix-golden-verified (RunWithSuggestedFixes), nolint-wired, and have zero production violations under both the default and js/wasm builds. None is in the CI enforcement set (cgo.yml:1227 currently enforces 25 analyzers). They are safe to enforce today: enforcement would pass immediately and lock in the improvement.

This is the same enforce-readiness pattern that landed for the previous clean batches (#42416, #42644, #45186, #45629, #46341, #46978). These three have never been the subject of an enforce issue.

Evidence

Registration: all three are wired in pkg/linters/registry.go (All()), lines 73 (appendoneelement), 123 (timenowsub), 116 (stringsjoinone).

Not enforced: .github/workflows/cgo.yml:1227 LINTER_FLAGS lists 25 analyzers; none of the three appears (default build) — and line 1230 (GOOS=js GOARCH=wasm) omits them too.

Type-resolved (no syntactic-package-match FP, the #43934 class):

  • appendoneelement.go:61pass.TypesInfo.ObjectOf(ident) != types.Universe.Lookup("append") gates on the builtin, not the identifier spelling.
  • timenowsub.go:119-127timeNowQualifier resolves the receiver via *types.PkgName and checks pkgName.Imported().Path() == "time"; alias/shadow-safe.
  • stringsjoinone.go:56astutil.AsStringsMethodCallastutil.IsPkgSelector(pass, sel, "strings") (astutil.go:424), the canonical type-resolved package-identity check.

Behavior-preserving autofixes (verified):

  • appendoneelement: append(s, []T{x}...)append(s, x). The surrounding append re-imposes s's element type on x, so no type change; elided-type nested literals ([][]int{{1}}) and keyed elements are correctly skipped (appendoneelement.go:112-117).
  • timenowsub: time.Now().Sub(t)time.Since(t). isSafeSinceArg (timenowsub.go:132-149) restricts the argument to side-effect-free forms (ident / basiclit / selector / index / deref / paren), so the evaluation-order flip (Since reads its arg before calling Now) cannot change behavior.
  • stringsjoinone: strings.Join([]string{s}, sep)s. Guarded by isSafeToDiscardSeparator (constant separator only, stringsjoinone.go:104-107) and formatReplacementText (:139-146) which parenthesizes non-primary elements to preserve precedence (the stringsjoinone autofix drops precedence: bare unparenthesized element replacement emits non-compiling / behavior-changed code in [Content truncated due to length] #47467 fix). The bare-element output is type-safe because Go assignability forces the single element to be string or an untyped string constant.

Golden autofix parity (RunWithSuggestedFixes): appendoneelement_test.go:15, timenowsub_test.go:15, stringsjoinone_test.go:15 all use analysistest.RunWithSuggestedFixes — the fixes are golden-verified, not merely diagnostic-tested.

Suppression + generated-file skip wired: each Requires includes nolint.Analyzer and filecheck.Analyzer, and each calls nolint.HasDirectiveForLinter + filecheck.ShouldSkipFilename (e.g. appendoneelement.go:24,69-73; timenowsub.go:23,78-83; stringsjoinone.go:23,65-69). CI enforcement passes -test=false, so test-file scope is moot.

Zero production violations (grep, excluding _test.go / testdata):

  • time.Now().Sub( — 0 non-test/non-doc production sites.
  • strings.Join([]string{ — production sites (pkg/workflow/call_workflow_permissions.go:136, checkout_path_validation.go:68, threat_detection_external.go:47, push_to_pull_request_branch_validation.go:118,136, audit_agentic_analysis.go:212) are all multi-element joins; no single-element literal.
  • append(x, []T{one}...) — the only }...) production sites (pkg/workflow/threat_detection_steps.go:255,295,313,327, threat_detection_external.go:443) are multi-element append blocks; no single-element spread.

Impact

These three checks are currently advisory-only: a regression (time.Now().Sub, a single-element strings.Join, or a spread single-element append) can merge without CI feedback, even though the codebase is already clean. Enforcing them locks in the current state at zero cost and gives contributors immediate feedback — the exact value delivered by the prior enforce batches that landed.

Recommendation

Add -appendoneelement -timenowsub -stringsjoinone to the LINTER_FLAGS on cgo.yml:1227 (and the wasm variant on :1230).

Before → after (line 1227 flags, additions only):

  • before: ... -mapclearloop -mapdeletecheck -sprintfbool -test=false
  • after: ... -mapclearloop -mapdeletecheck -sprintfbool -appendoneelement -timenowsub -stringsjoinone -test=false

Validation checklist

  • make golint-custom LINTER_FLAGS="-appendoneelement -timenowsub -stringsjoinone -test=false" reports zero findings on the default build.
  • Same three flags added to the js/wasm invocation (cgo.yml:1230) report zero findings for LINTER_PACKAGES.
  • Confirm each flag name matches the analyzer Name (appendoneelement, timenowsub, stringsjoinone).
  • No new production violation was introduced between filing and merge (re-run the three greps above).

Effort

Small — a two-line workflow edit (default + wasm), no linter code change. Each analyzer is independently clean, so the three can be enforced together or split without interaction.

Note on scope

Each analyzer was individually audited this run and found correct; stringsjoinone's bare-element autofix was specifically checked for a named-string type-change bug (the #44187/#47704 class) and cleared — Go assignability forbids a defined string type appearing bare in []string{...}, so the emitted element is always string-typed.

Generated by 🤖 Sergo - Serena Go Expert · age00 · 475.4 AIC · ⌖ 14 AIC · ⊞ 5.8K ·

  • expires on Jul 31, 2026, 9:04 PM UTC-08:00

Metadata

Metadata

Labels

cookieIssue Monster Loves Cookies!sergo

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions