diff --git a/cmd/linters/main.go b/cmd/linters/main.go index 9c1f95b8647..fd3486fe000 100644 --- a/cmd/linters/main.go +++ b/cmd/linters/main.go @@ -18,6 +18,7 @@ import ( "github.com/github/gh-aw/pkg/linters/contextcancelnotdeferred" "github.com/github/gh-aw/pkg/linters/ctxbackground" + "github.com/github/gh-aw/pkg/linters/errorfwrapv" "github.com/github/gh-aw/pkg/linters/errormessage" "github.com/github/gh-aw/pkg/linters/errstringmatch" "github.com/github/gh-aw/pkg/linters/excessivefuncparams" @@ -53,6 +54,7 @@ func main() { errormessage.Analyzer, fprintlnsprintf.Analyzer, errstringmatch.Analyzer, + errorfwrapv.Analyzer, execcommandwithoutcontext.Analyzer, excessivefuncparams.Analyzer, fileclosenotdeferred.Analyzer, diff --git a/docs/adr/39263-custom-linter-for-errorf-percent-v-error-wrapping.md b/docs/adr/39263-custom-linter-for-errorf-percent-v-error-wrapping.md new file mode 100644 index 00000000000..4e06c982d8d --- /dev/null +++ b/docs/adr/39263-custom-linter-for-errorf-percent-v-error-wrapping.md @@ -0,0 +1,42 @@ +# ADR-39263: Ship a Custom `errorfwrapv` Linter for `fmt.Errorf` Error Wrapping with `%v` + +**Date**: 2026-06-14 +**Status**: Draft + +## Context + +`fmt.Errorf("...: %v", err)` formats an `error` argument with `%v`, which renders the error's text but discards the wrapped-error reference. Only the `%w` verb records the cause in the chain that `errors.Is` and `errors.As` traverse, so a `%v` wrap silently breaks sentinel- and type-based error inspection by downstream callers — a correctness bug that is easy to overlook in review. No commonly enabled linter flags this distinction: `go vet`'s `printf` pass validates verb/argument type compatibility but treats `%v` on an error as valid. A scan of this repository (run #38) found a real occurrence in `pkg/workflow/compiler_orchestrator_frontmatter.go:50` (suppressed with a `nolint`), so the project needs an automated guard that fits its existing in-repo linter suite (`pkg/linters/*` registered in `cmd/linters/main.go`). + +## Decision + +We will add a bespoke `go/analysis` analyzer, `errorfwrapv`, to the project's custom linter suite rather than relying on an external linter. The analyzer inspects `fmt.Errorf` calls whose format argument is a string literal, parses the format string to map each positional verb to its argument index, and reports when a `%v` verb corresponds to an argument whose type implements the `error` interface (verified via `types.Implements` against the universe `error` type, not a syntactic match). It reports at most once per call, honors `//nolint:errorfwrapv` suppression for intentional non-wrapping, skips test files, and is registered in the multichecker in `cmd/linters/main.go`. + +## Alternatives Considered + +### Alternative 1: Rely on `go vet` / `golangci-lint` defaults +The project already runs standard linters, so extending their configuration would avoid new code. Rejected because no default rule distinguishes `%v` from `%w` for error arguments — `go vet`'s `printf` pass considers `%v` on an `error` correct — so the chain-breaking pattern goes uncaught. + +### Alternative 2: Document the convention and rely on code review +A contributor guideline plus manual review requires no tooling. Rejected because the `%v` vs `%w` distinction is subtle and easily missed — an instance already reached production with a `nolint` — and manual review provides no repeatable, enforceable guard. + +### Alternative 3: Syntactic (string-match) detection of `%v` next to an `err` identifier +A simpler analyzer could flag any `%v` whose argument is named `err`. Rejected because it would both miss errors stored under other names and false-positive on non-error values; type-based `error`-interface checking is more precise at modest extra complexity. + +## Consequences + +### Positive +- Catches a real, otherwise-undetected correctness pattern (lost error chains) automatically in CI. +- Follows the established in-repo linter convention, so it composes with the existing `cmd/linters` multichecker and shared `internal` helpers (`astutil`, `filecheck`, `nolint`). +- Precise: `types.Implements` identity checking and per-argument verb mapping target genuine error wraps while allowing `%v` on non-error arguments. + +### Negative +- Adds a custom analyzer the team must maintain, including a hand-rolled format-string verb parser that must track Go's `fmt` flag/width/precision/explicit-index (`%[n]`) syntax as it evolves. +- Limited to string-literal format arguments: `fmt.Errorf` calls with a non-literal (variable or concatenated) format string are not analyzed, so some misses remain uncaught and may create a false sense of full coverage. + +### Neutral +- Suppression is available via `//nolint:errorfwrapv` for intentional non-wrapping cases. +- Test files are excluded from analysis, matching the suite's existing conventions. + +--- + +*This is a DRAFT ADR generated by the [Design Decision Gate](https://github.com/github/gh-aw/actions/runs/27507782045) workflow. The PR author must review, complete, and finalize this document before the PR can merge.* diff --git a/pkg/linters/README.md b/pkg/linters/README.md index c8074cb0efc..abebdb12349 100644 --- a/pkg/linters/README.md +++ b/pkg/linters/README.md @@ -8,6 +8,7 @@ This package currently provides custom Go analyzers in the following subpackages - `contextcancelnotdeferred` — reports context cancel functions that are called directly instead of deferred. - `ctxbackground` — reports `context.Background()` calls inside functions that already receive a `context.Context` parameter. +- `errorfwrapv` — reports `fmt.Errorf` calls that format error arguments with `%v` instead of `%w`. - `excessivefuncparams` — reports function declarations that exceed a configurable parameter-count threshold. - `errormessage` — reports non-actionable error-message patterns in changed files. - `errstringmatch` — reports `strings.Contains(err.Error(), "...")` patterns and recommends `errors.Is` / `errors.As`. @@ -16,6 +17,7 @@ This package currently provides custom Go analyzers in the following subpackages - `fmterrorfnoverbs` — reports `fmt.Errorf` calls whose format string contains no verbs, recommending `errors.New` instead. - `fprintlnsprintf` — reports `fmt.Fprintln(..., fmt.Sprintf(...))` patterns and recommends direct formatting calls. - `hardcodedfilepath` — reports hard-coded file path string literals that match known path constants or should be extracted into named constants; also annotates paths that appear in log/print calls. +- `httpnoctx` — reports HTTP client and package-level HTTP calls that do not accept a `context.Context`. - `jsonmarshalignoredeerror` — reports `json.Marshal` and `json.Unmarshal` calls where the error return is discarded with `_`. - `largefunc` — reports function bodies that exceed a configurable line-count threshold. - `lenstringzero` — reports `len(s) == 0` / `len(s) != 0` comparisons on string values that should use `s == ""` / `s != ""`. @@ -30,6 +32,7 @@ This package currently provides custom Go analyzers in the following subpackages - `ssljson` — validates `ssl.json` skill artifacts found in `.github/skills/` against the SSL spec (enum membership, graph integrity, transition targets, entry pointer validity). - `strconvparseignorederror` — reports `strconv` parsing calls (`Atoi`, `ParseInt`, etc.) where the error return is discarded with `_`. - `timeafterleak` — reports `time.After` calls used as the channel-receive expression in a `select` case inside a `for` or `range` loop that leak a timer channel on each iteration when another case fires first. +- `timesleepnocontext` — reports `time.Sleep` calls inside functions that already receive a `context.Context`, where a context-aware `select` should be used instead. - `tolowerequalfold` — reports case-insensitive string comparisons using `strings.ToLower`/`ToUpper` that should use `strings.EqualFold`. - `uncheckedtypeassertion` — reports single-value type assertions where unchecked panics are possible. - `internal` — shared helper packages for analyzers (file checks and `nolint` handling). @@ -42,6 +45,7 @@ This package currently provides custom Go analyzers in the following subpackages |------------|-------------| | `contextcancelnotdeferred` | Custom `go/analysis` analyzer that flags context cancel functions called directly instead of deferred | | `ctxbackground` | Custom `go/analysis` analyzer that flags `context.Background()` calls inside functions that already receive a context parameter | +| `errorfwrapv` | Custom `go/analysis` analyzer that flags `fmt.Errorf` calls that format error arguments with `%v` instead of `%w` | | `excessivefuncparams` | Custom `go/analysis` analyzer that flags function declarations with too many positional parameters | | `errormessage` | Custom `go/analysis` analyzer that flags non-actionable error message patterns in changed files | | `errstringmatch` | Custom `go/analysis` analyzer that flags brittle `strings.Contains(err.Error(), "...")` checks | @@ -50,6 +54,7 @@ This package currently provides custom Go analyzers in the following subpackages | `fmterrorfnoverbs` | Custom `go/analysis` analyzer that flags `fmt.Errorf` calls with no format verbs, recommending `errors.New` | | `fprintlnsprintf` | Custom `go/analysis` analyzer that flags `fmt.Fprintln(..., fmt.Sprintf(...))` patterns | | `hardcodedfilepath` | Custom `go/analysis` analyzer that flags hard-coded file path string literals that match known path constants or should be extracted as named constants; annotates paths in log/print calls | +| `httpnoctx` | Custom `go/analysis` analyzer that flags HTTP calls that do not accept a `context.Context` | | `jsonmarshalignoredeerror` | Custom `go/analysis` analyzer that flags `json.Marshal`/`json.Unmarshal` calls where the error return is discarded with `_` | | `largefunc` | Custom `go/analysis` analyzer that flags large functions with actionable diagnostics | | `lenstringzero` | Custom `go/analysis` analyzer that flags `len(s) == 0` / `len(s) != 0` on string values that should use `s == ""` / `s != ""` | @@ -64,6 +69,7 @@ This package currently provides custom Go analyzers in the following subpackages | `ssljson` | Custom `go/analysis` analyzer that validates SSL JSON skill artifacts in `.github/skills/` | | `strconvparseignorederror` | Custom `go/analysis` analyzer that flags `strconv` parsing calls where the error return is discarded with `_` | | `timeafterleak` | Custom `go/analysis` analyzer that flags `time.After` in `select` cases inside loops that leak a timer channel on each iteration when another case fires first | +| `timesleepnocontext` | Custom `go/analysis` analyzer that flags `time.Sleep` calls in context-aware functions | | `tolowerequalfold` | Custom `go/analysis` analyzer that flags case-insensitive comparisons via `strings.ToLower`/`ToUpper` that should use `strings.EqualFold` | | `uncheckedtypeassertion` | Custom `go/analysis` analyzer that flags unchecked single-value type assertions | | `internal` | Shared helper subpackages used by analyzers (`internal/filecheck`, `internal/nolint`) | diff --git a/pkg/linters/doc.go b/pkg/linters/doc.go index cb67fd23611..0bab5358155 100644 --- a/pkg/linters/doc.go +++ b/pkg/linters/doc.go @@ -1,9 +1,10 @@ // Package linters is a namespace for gh-aw's custom Go analysis linters. // -// The actual analyzers are implemented in subpackages. All 25 active analyzers: +// The actual analyzers are implemented in subpackages. All 29 active analyzers: // // - contextcancelnotdeferred — flags context cancel functions called directly instead of deferred // - ctxbackground — flags context.Background() inside functions that already receive a context +// - errorfwrapv — flags fmt.Errorf calls that format error arguments with %v instead of %w // - errormessage — flags non-actionable error message patterns in changed files // - errstringmatch — flags brittle strings.Contains(err.Error(), "...") checks // - excessivefuncparams — flags function declarations with too many positional parameters @@ -11,6 +12,7 @@ // - fileclosenotdeferred — flags file Close() calls that are not deferred // - fmterrorfnoverbs — flags fmt.Errorf calls with no format verbs, recommending errors.New // - fprintlnsprintf — flags fmt.Fprintln(..., fmt.Sprintf(...)) patterns +// - httpnoctx — flags HTTP calls that do not accept a context.Context // - jsonmarshalignoredeerror — flags json.Marshal/Unmarshal calls where the error is discarded with _ // - largefunc — flags function bodies that exceed a configurable line-count threshold // - lenstringzero — flags len(s) == 0 / len(s) != 0 on string values that should use s == "" / s != "" @@ -25,6 +27,7 @@ // - ssljson — validates ssl.json skill artifacts in .github/skills/ against the SSL spec // - strconvparseignorederror — flags strconv parsing calls where the error is discarded with _ // - timeafterleak — flags time.After in select cases inside loops that leak timer channels +// - timesleepnocontext — flags time.Sleep calls in context-aware functions that should propagate cancellation // - tolowerequalfold — flags case-insensitive comparisons via ToLower/ToUpper that should use EqualFold // - uncheckedtypeassertion — flags unchecked single-value type assertions // diff --git a/pkg/linters/errorfwrapv/errorfwrapv.go b/pkg/linters/errorfwrapv/errorfwrapv.go new file mode 100644 index 00000000000..f1d2ac1799f --- /dev/null +++ b/pkg/linters/errorfwrapv/errorfwrapv.go @@ -0,0 +1,207 @@ +// Package errorfwrapv implements a Go analysis linter that flags calls to +// fmt.Errorf that format error arguments with %v instead of %w, which breaks +// error-chain inspection via errors.Is and errors.As. +package errorfwrapv + +import ( + "errors" + "go/ast" + "go/token" + "go/types" + "strconv" + + "golang.org/x/tools/go/analysis" + "golang.org/x/tools/go/analysis/passes/inspect" + + "github.com/github/gh-aw/pkg/linters/internal/astutil" + "github.com/github/gh-aw/pkg/linters/internal/filecheck" + "github.com/github/gh-aw/pkg/linters/internal/nolint" +) + +var errorIface = universeErrorInterface() + +// universeErrorInterface returns the built-in error interface type, or nil if +// it cannot be resolved from types.Universe. +func universeErrorInterface() *types.Interface { + errorObj := types.Universe.Lookup("error") + if errorObj == nil { + return nil + } + + iface, ok := errorObj.Type().Underlying().(*types.Interface) + if !ok { + return nil + } + + return iface +} + +type formatVerb struct { + argIdx int + verb rune +} + +// Analyzer is the errorfwrapv analysis pass. +var Analyzer = &analysis.Analyzer{ + Name: "errorfwrapv", + Doc: "reports fmt.Errorf calls that format error arguments with %v instead of %w", + URL: "https://github.com/github/gh-aw/tree/main/pkg/linters/errorfwrapv", + Requires: []*analysis.Analyzer{inspect.Analyzer}, + Run: run, +} + +func run(pass *analysis.Pass) (any, error) { + if errorIface == nil { + return nil, errors.New("failed to resolve built-in error interface from types.Universe") + } + + insp, err := astutil.Inspector(pass) + if err != nil { + return nil, err + } + noLintLinesByFile := nolint.BuildLineIndex(pass, "errorfwrapv") + + nodeFilter := []ast.Node{ + (*ast.CallExpr)(nil), + } + + insp.Preorder(nodeFilter, func(n ast.Node) { + call, ok := n.(*ast.CallExpr) + if !ok { + return + } + + position := pass.Fset.PositionFor(call.Pos(), false) + if filecheck.IsTestFile(position.Filename) { + return + } + + if !astutil.IsFmtErrorf(pass, call) { + return + } + + if len(call.Args) == 0 { + return + } + + lit, ok := call.Args[0].(*ast.BasicLit) + if !ok || lit.Kind != token.STRING { + return + } + + verbs := parseFormatVerbs(lit.Value) + for _, fv := range verbs { + if fv.verb != 'v' { + continue + } + callArgIdx := fv.argIdx + 1 + if callArgIdx >= len(call.Args) { + continue + } + tv, ok := pass.TypesInfo.Types[call.Args[callArgIdx]] + if !ok || tv.Type == nil { + continue + } + if !types.Implements(tv.Type, errorIface) { + continue + } + if nolint.HasDirective(position, noLintLinesByFile) { + return + } + pass.ReportRangef(call, "fmt.Errorf formats an error argument with %%v; use %%w to preserve the error chain") + return + } + }) + + return nil, nil +} + +func parseFormatVerbs(s string) []formatVerb { + var verbs []formatVerb + if len(s) >= 2 { + s = s[1 : len(s)-1] + } + + nextArgIdx := 0 + for i := 0; i < len(s); i++ { + if s[i] != '%' { + continue + } + i++ + if i >= len(s) { + break + } + if s[i] == '%' { + continue + } + + valueArgIdx := 0 + hasExplicitValueArg := false + if idx, nextPos, ok := parseFormatArgIndex(s, i); ok { + valueArgIdx = idx + nextArgIdx = idx + 1 + hasExplicitValueArg = true + i = nextPos + } + for i < len(s) { + switch s[i] { + case '-', '+', '#', '0', ' ': + i++ + default: + goto width + } + } + + width: + i = consumeFormatWidthOrPrecision(s, i, &nextArgIdx) + if i < len(s) && s[i] == '.' { + i++ + i = consumeFormatWidthOrPrecision(s, i, &nextArgIdx) + } + if i >= len(s) { + break + } + if !hasExplicitValueArg { + valueArgIdx = nextArgIdx + nextArgIdx++ + } + verbs = append(verbs, formatVerb{argIdx: valueArgIdx, verb: rune(s[i])}) + } + + return verbs +} + +func consumeFormatWidthOrPrecision(s string, i int, nextArgIdx *int) int { + if idx, nextPos, ok := parseFormatArgIndex(s, i); ok && nextPos < len(s) && s[nextPos] == '*' { + *nextArgIdx = idx + 1 + return nextPos + 1 + } + if i < len(s) && s[i] == '*' { + *nextArgIdx = *nextArgIdx + 1 + return i + 1 + } + for i < len(s) && s[i] >= '0' && s[i] <= '9' { + i++ + } + return i +} + +func parseFormatArgIndex(s string, i int) (int, int, bool) { + if i >= len(s) || s[i] != '[' { + return 0, i, false + } + + j := i + 1 + for j < len(s) && s[j] >= '0' && s[j] <= '9' { + j++ + } + if j == i+1 || j >= len(s) || s[j] != ']' { + return 0, i, false + } + + n, err := strconv.Atoi(s[i+1 : j]) + if err != nil || n <= 0 { + return 0, i, false + } + return n - 1, j + 1, true +} diff --git a/pkg/linters/errorfwrapv/errorfwrapv_test.go b/pkg/linters/errorfwrapv/errorfwrapv_test.go new file mode 100644 index 00000000000..3dafbb564f3 --- /dev/null +++ b/pkg/linters/errorfwrapv/errorfwrapv_test.go @@ -0,0 +1,16 @@ +//go:build !integration + +package errorfwrapv_test + +import ( + "testing" + + "golang.org/x/tools/go/analysis/analysistest" + + "github.com/github/gh-aw/pkg/linters/errorfwrapv" +) + +func TestErrorfWrapV(t *testing.T) { + testdata := analysistest.TestData() + analysistest.Run(t, testdata, errorfwrapv.Analyzer, "errorfwrapv") +} diff --git a/pkg/linters/errorfwrapv/testdata/src/errorfwrapv/errorfwrapv.go b/pkg/linters/errorfwrapv/testdata/src/errorfwrapv/errorfwrapv.go new file mode 100644 index 00000000000..d6c4d0e6d5c --- /dev/null +++ b/pkg/linters/errorfwrapv/testdata/src/errorfwrapv/errorfwrapv.go @@ -0,0 +1,59 @@ +package errorfwrapv + +import ( + "errors" + "fmt" +) + +var ErrBase = errors.New("base error") + +type myError struct { + msg string +} + +func (e *myError) Error() string { return e.msg } + +// BadVWrap uses %v to format an error — should be %w. +func BadVWrap(err error) error { + return fmt.Errorf("operation failed: %v", err) // want `fmt\.Errorf formats an error argument with %v` +} + +// BadVWrapExtra has %v for an error arg alongside a non-error arg. +func BadVWrapExtra(err error, count int) error { + return fmt.Errorf("error %v occurred %d times", err, count) // want `fmt\.Errorf formats an error argument with %v` +} + +// BadExplicitIndexV uses an explicit positional index for the error argument. +func BadExplicitIndexV(name string, err error) error { + return fmt.Errorf("%[2]v while handling %[1]q", name, err) // want `fmt\.Errorf formats an error argument with %v` +} + +// BadDynamicWidthWrap uses a dynamic width before the error argument. +func BadDynamicWidthWrap(err error) error { + return fmt.Errorf("%*v", 10, err) // want `fmt\.Errorf formats an error argument with %v` +} + +// BadConcretePointerWrap passes a concrete pointer type that implements error. +func BadConcretePointerWrap(err *myError) error { + return fmt.Errorf("wrapped: %v", err) // want `fmt\.Errorf formats an error argument with %v` +} + +// GoodWWrap uses %w to properly wrap the error. +func GoodWWrap(err error) error { + return fmt.Errorf("operation failed: %w", err) +} + +// GoodNonErrorVerb uses %v for a non-error argument only. +func GoodNonErrorVerb(name string) error { + return fmt.Errorf("operation %v failed", name) +} + +// GoodMixedVerbs uses %w for the error and %v for a non-error. +func GoodMixedVerbs(name string, err error) error { + return fmt.Errorf("operation %v failed: %w", name, err) +} + +// SuppressedByNolint is intentionally suppressed. +func SuppressedByNolint(err error) error { + return fmt.Errorf("operation failed: %v", err) //nolint:errorfwrapv +} diff --git a/pkg/linters/spec_test.go b/pkg/linters/spec_test.go index c061f1ccfe2..f53401eb72d 100644 --- a/pkg/linters/spec_test.go +++ b/pkg/linters/spec_test.go @@ -13,6 +13,7 @@ import ( "github.com/github/gh-aw/pkg/linters" "github.com/github/gh-aw/pkg/linters/contextcancelnotdeferred" "github.com/github/gh-aw/pkg/linters/ctxbackground" + "github.com/github/gh-aw/pkg/linters/errorfwrapv" "github.com/github/gh-aw/pkg/linters/errormessage" "github.com/github/gh-aw/pkg/linters/errstringmatch" "github.com/github/gh-aw/pkg/linters/excessivefuncparams" @@ -21,6 +22,7 @@ import ( "github.com/github/gh-aw/pkg/linters/fmterrorfnoverbs" "github.com/github/gh-aw/pkg/linters/fprintlnsprintf" "github.com/github/gh-aw/pkg/linters/hardcodedfilepath" + "github.com/github/gh-aw/pkg/linters/httpnoctx" "github.com/github/gh-aw/pkg/linters/jsonmarshalignoredeerror" "github.com/github/gh-aw/pkg/linters/largefunc" "github.com/github/gh-aw/pkg/linters/lenstringzero" @@ -34,6 +36,8 @@ import ( "github.com/github/gh-aw/pkg/linters/sortslice" "github.com/github/gh-aw/pkg/linters/ssljson" "github.com/github/gh-aw/pkg/linters/strconvparseignorederror" + "github.com/github/gh-aw/pkg/linters/timeafterleak" + "github.com/github/gh-aw/pkg/linters/timesleepnocontext" "github.com/github/gh-aw/pkg/linters/tolowerequalfold" "github.com/github/gh-aw/pkg/linters/uncheckedtypeassertion" ) @@ -51,22 +55,23 @@ type docAnalyzer struct { } // documentedAnalyzers returns the analyzer subpackages documented in the README -// "Public API > Subpackages" table. The README documents 25 analyzer +// "Public API > Subpackages" table. The README documents 29 analyzer // subpackages (the non-analyzer `internal` helper subpackage is excluded because // it exposes no Analyzer). // // Spec (README "Public API > Subpackages"): // -// contextcancelnotdeferred, ctxbackground, excessivefuncparams, errormessage, +// contextcancelnotdeferred, ctxbackground, errorfwrapv, excessivefuncparams, errormessage, // errstringmatch, execcommandwithoutcontext, fileclosenotdeferred, fmterrorfnoverbs, fprintlnsprintf, -// hardcodedfilepath, jsonmarshalignoredeerror, largefunc, lenstringzero, manualmutexunlock, -// osexitinlibrary, ossetenvlibrary, panic-in-library-code, rawloginlib, -// regexpcompileinfunction, seenmapbool, sortslice, ssljson, -// strconvparseignorederror, tolowerequalfold, uncheckedtypeassertion +// hardcodedfilepath, httpnoctx, jsonmarshalignoredeerror, largefunc, lenstringzero, +// manualmutexunlock, osexitinlibrary, ossetenvlibrary, panic-in-library-code, rawloginlib, +// regexpcompileinfunction, seenmapbool, sortslice, ssljson, strconvparseignorederror, +// timeafterleak, timesleepnocontext, tolowerequalfold, uncheckedtypeassertion func documentedAnalyzers() []docAnalyzer { return []docAnalyzer{ {"contextcancelnotdeferred", contextcancelnotdeferred.Analyzer}, {"ctxbackground", ctxbackground.Analyzer}, + {"errorfwrapv", errorfwrapv.Analyzer}, {"excessivefuncparams", excessivefuncparams.Analyzer}, {"errormessage", errormessage.Analyzer}, {"errstringmatch", errstringmatch.Analyzer}, @@ -75,6 +80,7 @@ func documentedAnalyzers() []docAnalyzer { {"fmterrorfnoverbs", fmterrorfnoverbs.Analyzer}, {"fprintlnsprintf", fprintlnsprintf.Analyzer}, {"hardcodedfilepath", hardcodedfilepath.Analyzer}, + {"httpnoctx", httpnoctx.Analyzer}, {"jsonmarshalignoredeerror", jsonmarshalignoredeerror.Analyzer}, {"largefunc", largefunc.Analyzer}, {"lenstringzero", lenstringzero.Analyzer}, @@ -88,6 +94,8 @@ func documentedAnalyzers() []docAnalyzer { {"sortslice", sortslice.Analyzer}, {"ssljson", ssljson.Analyzer}, {"strconvparseignorederror", strconvparseignorederror.Analyzer}, + {"timeafterleak", timeafterleak.Analyzer}, + {"timesleepnocontext", timesleepnocontext.Analyzer}, {"tolowerequalfold", tolowerequalfold.Analyzer}, {"uncheckedtypeassertion", uncheckedtypeassertion.Analyzer}, } diff --git a/pkg/workflow/compiler_orchestrator_frontmatter.go b/pkg/workflow/compiler_orchestrator_frontmatter.go index 32cbad34c6e..6b7063ef18b 100644 --- a/pkg/workflow/compiler_orchestrator_frontmatter.go +++ b/pkg/workflow/compiler_orchestrator_frontmatter.go @@ -47,7 +47,7 @@ func (c *Compiler) parseFrontmatterSection(markdownPath string) (*frontmatterPar if err != nil { orchestratorFrontmatterLog.Printf("Failed to read file: %s, error: %v", cleanPath, err) // Intentionally not wrapping to avoid exposing internal path details - return nil, fmt.Errorf("failed to read file: %v", err) //nolint:errorlint // intentionally not wrapping to avoid exposing os.PathError + return nil, fmt.Errorf("failed to read file: %v", err) //nolint:errorfwrapv,errorlint // intentionally not wrapping to avoid exposing os.PathError } contentString := string(content)