Summary
The new logfatallibrary analyzer flags log.Fatal / log.Fatalf / log.Fatalln in library (pkg/) packages. But the already-CI-enforced rawloginlib analyzer (.github/workflows/cgo.yml:1208, flag -rawloginlib) already flags every standard-log call in library packages — its rawLogFuncs set explicitly includes Fatal/Fatalf/Fatalln (pkg/linters/rawloginlib/rawloginlib.go:26-30). Both linters use identical gating (astutil.IsPkgSelector(pass, sel, "log"), the same /main+/cmd/ package exemption, the same test-file skip), so logfatallibrary's detection set is a strict subset of rawloginlib's — it reports nothing rawloginlib doesn't already report.
Net effect: logfatallibrary adds no new coverage. If it were added to CI it would double-report every log.Fatal* site (two diagnostics, two distinct //nolint directives required), and today it is dead weight in the registry.
Evidence
| Aspect |
rawloginlib (enforced) |
logfatallibrary (new, not enforced) |
| Matched funcs |
Print/Printf/Println, Fatal/Fatalf/Fatalln, Panic/Panicf/Panicln (rawloginlib.go:26-30) |
Fatal/Fatalf/Fatalln (logfatallibrary.go:20-24) |
| Package identity |
astutil.IsPkgSelector(pass, sel, "log") (rawloginlib.go:58) |
astutil.IsPkgSelector(pass, sel, "log") (logfatallibrary.go:67) |
| Package exemption |
HasSuffix(pkgPath,"/main") || Contains(pkgPath,"/cmd/") (rawloginlib.go:34) |
same (logfatallibrary.go:38) |
| Test skip |
filecheck.IsTestFile (rawloginlib.go:51) |
filecheck.IsTestFile + .test pkg suffix (logfatallibrary.go:57) |
nolint |
per-linter index (rawloginlib.go:42) |
per-linter index (logfatallibrary.go:46) |
| CI-enforced |
yes — -rawloginlib in default + wasm runs (cgo.yml:1208, :1211) |
no |
Because the gating predicates are identical and {Fatal,Fatalf,Fatalln} ⊂ rawLogFuncs, every call flagged by logfatallibrary is also flagged by rawloginlib (which already runs in CI). The two diagnostics even share the same prefix — log.Fatal called in library package <pkg>; ... — differing only in the trailing advice (use pkg/logger instead vs use error returns instead to avoid implicit os.Exit).
Why this is a real problem, not just cosmetic
- Double diagnostics / dual suppression. The
nolint index is per-linter (nolint.BuildLineIndex(pass, "<name>")). A contributor with a justified log.Fatal in library code would need //nolint:rawloginlib,logfatallibrary once both are enforced — a footgun, since suppressing only the visible rawloginlib warning leaves a latent logfatallibrary one.
- Zero added coverage. There are 0 production
log.Fatal* sites in pkg/ today (all grep hits are doc comments, README samples, t.Fatalf from testing.T, or linter testdata), so logfatallibrary currently catches nothing that rawloginlib isn't already positioned to catch.
- Misleading remediation split. For
Fatal, rawloginlib's advice (use pkg/logger instead) is actually weaker than logfatallibrary's (return errors ... avoid implicit os.Exit) — swapping to pkg/logger does not address the os.Exit(1) termination semantics. So the accurate message currently lives in the linter that is NOT enforced.
Recommendation
Partition the two linters so each owns a disjoint, correctly-messaged slice — do not simply add logfatallibrary to CI alongside rawloginlib.
Preferred: remove Fatal/Fatalf/Fatalln from rawloginlib's rawLogFuncs (leaving Print* and Panic*), then wire logfatallibrary into LINTER_FLAGS. Result: rawloginlib owns the "use structured logging" funcs, logfatallibrary owns the Fatal family with the accurate os.Exit remediation, no site is double-reported, and each needs only one //nolint.
Alternative: if rawloginlib is meant to remain the single catch-all, drop logfatallibrary from the registry (cmd/linters/main.go) and fold its more-accurate wording into rawloginlib's message for the Fatal funcs.
Secondary (either path): the designated Fatal-catcher misses *log.Logger instance calls (logger.Fatal(...) from log.New(...)), which have the identical os.Exit(1) harm — currently latent (0 prod sites) but worth a // TODO if logfatallibrary becomes the canonical Fatal linter.
Validation checklist
Effort: small — one-line rawLogFuncs edit + testdata retarget + 2 CI flag additions (partition path), or a registry removal (drop path).
Generated by 🤖 Sergo - Serena Go Expert · 312.4 AIC · ⌖ 14 AIC · ⊞ 5.8K · ◷
Summary
The new
logfatallibraryanalyzer flagslog.Fatal/log.Fatalf/log.Fatallnin library (pkg/) packages. But the already-CI-enforcedrawloginlibanalyzer (.github/workflows/cgo.yml:1208, flag-rawloginlib) already flags every standard-logcall in library packages — itsrawLogFuncsset explicitly includesFatal/Fatalf/Fatalln(pkg/linters/rawloginlib/rawloginlib.go:26-30). Both linters use identical gating (astutil.IsPkgSelector(pass, sel, "log"), the same/main+/cmd/package exemption, the same test-file skip), sologfatallibrary's detection set is a strict subset ofrawloginlib's — it reports nothingrawloginlibdoesn't already report.Net effect:
logfatallibraryadds no new coverage. If it were added to CI it would double-report everylog.Fatal*site (two diagnostics, two distinct//nolintdirectives required), and today it is dead weight in the registry.Evidence
rawloginlib(enforced)logfatallibrary(new, not enforced)Print/Printf/Println,Fatal/Fatalf/Fatalln,Panic/Panicf/Panicln(rawloginlib.go:26-30)Fatal/Fatalf/Fatalln(logfatallibrary.go:20-24)astutil.IsPkgSelector(pass, sel, "log")(rawloginlib.go:58)astutil.IsPkgSelector(pass, sel, "log")(logfatallibrary.go:67)HasSuffix(pkgPath,"/main") || Contains(pkgPath,"/cmd/")(rawloginlib.go:34)logfatallibrary.go:38)filecheck.IsTestFile(rawloginlib.go:51)filecheck.IsTestFile+.testpkg suffix (logfatallibrary.go:57)nolintrawloginlib.go:42)logfatallibrary.go:46)-rawloginlibin default + wasm runs (cgo.yml:1208,:1211)Because the gating predicates are identical and
{Fatal,Fatalf,Fatalln} ⊂ rawLogFuncs, every call flagged bylogfatallibraryis also flagged byrawloginlib(which already runs in CI). The two diagnostics even share the same prefix —log.Fatal called in library package <pkg>; ...— differing only in the trailing advice (use pkg/logger insteadvsuse error returns instead to avoid implicit os.Exit).Why this is a real problem, not just cosmetic
nolintindex is per-linter (nolint.BuildLineIndex(pass, "<name>")). A contributor with a justifiedlog.Fatalin library code would need//nolint:rawloginlib,logfatallibraryonce both are enforced — a footgun, since suppressing only the visiblerawloginlibwarning leaves a latentlogfatallibraryone.log.Fatal*sites inpkg/today (allgrephits are doc comments, README samples,t.Fatalffromtesting.T, or linter testdata), sologfatallibrarycurrently catches nothing thatrawloginlibisn't already positioned to catch.Fatal,rawloginlib's advice (use pkg/logger instead) is actually weaker thanlogfatallibrary's (return errors ... avoid implicit os.Exit) — swapping topkg/loggerdoes not address theos.Exit(1)termination semantics. So the accurate message currently lives in the linter that is NOT enforced.Recommendation
Partition the two linters so each owns a disjoint, correctly-messaged slice — do not simply add
logfatallibraryto CI alongsiderawloginlib.Preferred: remove
Fatal/Fatalf/Fatallnfromrawloginlib'srawLogFuncs(leavingPrint*andPanic*), then wirelogfatallibraryintoLINTER_FLAGS. Result:rawloginlibowns the "use structured logging" funcs,logfatallibraryowns the Fatal family with the accurateos.Exitremediation, no site is double-reported, and each needs only one//nolint.Alternative: if
rawloginlibis meant to remain the single catch-all, droplogfatallibraryfrom the registry (cmd/linters/main.go) and fold its more-accurate wording intorawloginlib's message for the Fatal funcs.Secondary (either path): the designated Fatal-catcher misses
*log.Loggerinstance calls (logger.Fatal(...)fromlog.New(...)), which have the identicalos.Exit(1)harm — currently latent (0 prod sites) but worth a// TODOiflogfatallibrarybecomes the canonical Fatal linter.Validation checklist
Fatal*handled by exactly one enforced linter.rawLogFuncsno longer containsFatal/Fatalf/Fatalln;rawloginlibtestdata updated (e.g.alias_import.gomoved/retargeted);logfatallibraryadded toLINTER_FLAGSin bothcgo.ymlruns.make golint-custom LINTER_FLAGS="-rawloginlib -logfatallibrary -test=false"produces at most one diagnostic perlog.Fatal*site (no duplicates).//nolint:<owner>suppresses a justifiedlog.Fatalwith no residual warning from the other linter.Effort: small — one-line
rawLogFuncsedit + testdata retarget + 2 CI flag additions (partition path), or a registry removal (drop path).