From 1d0f68ced0bb7c60881242c7ad155b81eb20465f Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Jul 2026 18:10:52 +0000 Subject: [PATCH 1/2] Initial plan From 06081755e9b2b759313ef6cf8211f6d1fc3671a7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 29 Jul 2026 18:19:39 +0000 Subject: [PATCH 2/2] Fix panicinlibrarycode sync.OnceValue/OnceFunc exemptions Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- .../panic-in-library-code.go | 47 +++++++++++++++++-- .../panicinlibrarycode/panicinlibrarycode.go | 9 ++++ 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/pkg/linters/panic-in-library-code/panic-in-library-code.go b/pkg/linters/panic-in-library-code/panic-in-library-code.go index b83091baf8f..257ac92090c 100644 --- a/pkg/linters/panic-in-library-code/panic-in-library-code.go +++ b/pkg/linters/panic-in-library-code/panic-in-library-code.go @@ -86,13 +86,13 @@ func run(pass *analysis.Pass) (any, error) { } func shouldSkipPanic(pass *analysis.Pass, call *ast.CallExpr, cur inspector.Cursor) bool { - return isInSyncOnceDoFuncLit(pass, cur) || + return isInSyncOnceFuncLit(pass, cur) || panicMessageStartsWithBUG(pass, call) || isInInitFunction(cur) || hasDocumentedPanicContract(cur) } -func isInSyncOnceDoFuncLit(pass *analysis.Pass, cur inspector.Cursor) bool { +func isInSyncOnceFuncLit(pass *analysis.Pass, cur inspector.Cursor) bool { for encl := range cur.Enclosing((*ast.FuncLit)(nil)) { funcLit, ok := encl.Node().(*ast.FuncLit) if !ok { @@ -103,17 +103,54 @@ func isInSyncOnceDoFuncLit(pass *analysis.Pass, cur inspector.Cursor) bool { if !ok || !containsExpr(call.Args, funcLit) { continue } - sel, ok := call.Fun.(*ast.SelectorExpr) - if !ok || sel.Sel.Name != "Do" { + sel, ok := selectorExprFromCallFun(call.Fun) + if !ok { continue } - if isSyncOnceType(pass.TypesInfo.TypeOf(sel.X)) { + if isSyncOnceDoCall(pass, sel) || isSyncOnceConstructorCall(pass, sel) { return true } } return false } +func selectorExprFromCallFun(fun ast.Expr) (*ast.SelectorExpr, bool) { + switch f := fun.(type) { + case *ast.SelectorExpr: + return f, true + case *ast.IndexExpr: + sel, ok := f.X.(*ast.SelectorExpr) + return sel, ok + case *ast.IndexListExpr: + sel, ok := f.X.(*ast.SelectorExpr) + return sel, ok + default: + return nil, false + } +} + +func isSyncPackageFunc(pass *analysis.Pass, sel *ast.SelectorExpr, names ...string) bool { + if !slices.Contains(names, sel.Sel.Name) { + return false + } + obj := pass.TypesInfo.Uses[sel.Sel] + if obj == nil || obj.Pkg() == nil { + return false + } + return obj.Pkg().Path() == "sync" && slices.Contains(names, obj.Name()) +} + +func isSyncOnceDoCall(pass *analysis.Pass, sel *ast.SelectorExpr) bool { + if sel.Sel.Name != "Do" { + return false + } + return isSyncOnceType(pass.TypesInfo.TypeOf(sel.X)) +} + +func isSyncOnceConstructorCall(pass *analysis.Pass, sel *ast.SelectorExpr) bool { + return isSyncPackageFunc(pass, sel, "OnceValue", "OnceFunc") +} + func containsExpr(args []ast.Expr, target ast.Expr) bool { return slices.Contains(args, target) } diff --git a/pkg/linters/panic-in-library-code/testdata/src/panicinlibrarycode/panicinlibrarycode.go b/pkg/linters/panic-in-library-code/testdata/src/panicinlibrarycode/panicinlibrarycode.go index 1f1aecc7e57..aaf96870e91 100644 --- a/pkg/linters/panic-in-library-code/testdata/src/panicinlibrarycode/panicinlibrarycode.go +++ b/pkg/linters/panic-in-library-code/testdata/src/panicinlibrarycode/panicinlibrarycode.go @@ -59,6 +59,15 @@ func allowedSyncOncePanic() { }) } +var onceValue = sync.OnceValue(func() int { + panic("lazy init failure in sync.OnceValue") // should not be flagged + return 0 +}) + +var onceFunc = sync.OnceFunc(func() { + panic("lazy init failure in sync.OnceFunc") // should not be flagged +}) + // ok: panic whose message starts with "BUG:" — invariant violation. func allowedBUGPanic() { panic(fmt.Sprintf("BUG: unreachable: %v", errors.New("boom"))) // should not be flagged