From 24447af2280e8fb2230347b61fd6e160d4aad995 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Jun 2026 06:33:06 +0000 Subject: [PATCH 1/3] Initial plan From afa258651d9c8d4e0a8a196efefd129831e70e92 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Jun 2026 06:41:16 +0000 Subject: [PATCH 2/3] fix(seenmapbool): stop first-pass recursion at FuncLit to prevent duplicate diagnostics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first `ast.Inspect` pass in `inspectBody` now returns `false` when it encounters a nested `*ast.FuncLit`, so maps declared inside closures are collected only by the closure's own Preorder visit — not re-collected by the enclosing function's pass. The second (write-disqualification) pass still recurses into closures so that a non-true write made inside a closure correctly prevents reporting the outer function's map as a pure set. Test cases added: - BadSetBoolInClosure: single // want in a 1-level closure - BadSetBoolInNestedClosure: single // want in a 2-level closure - GoodOuterMapWrittenFalseInClosure: outer map disqualified by closure write Closes #40733 Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/linters/seenmapbool/seenmapbool.go | 5 +++ .../testdata/src/seenmapbool/seenmapbool.go | 41 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/pkg/linters/seenmapbool/seenmapbool.go b/pkg/linters/seenmapbool/seenmapbool.go index 00baae409bf..e5d91ab9ac5 100644 --- a/pkg/linters/seenmapbool/seenmapbool.go +++ b/pkg/linters/seenmapbool/seenmapbool.go @@ -67,7 +67,12 @@ func inspectBody(pass *analysis.Pass, body *ast.BlockStmt, noLintLinesByFile map candidates := make(map[types.Object]ast.Node) // object -> declaration node for reporting // First pass: collect declarations of map[string]bool locals. + // Stop at nested FuncLit boundaries so each closure is handled by its own + // Preorder visit — preventing duplicate diagnostics. ast.Inspect(body, func(n ast.Node) bool { + if _, ok := n.(*ast.FuncLit); ok { + return false // do not descend into nested closures + } switch stmt := n.(type) { case *ast.AssignStmt: // seen := make(map[string]bool) or seen := map[string]bool{} diff --git a/pkg/linters/seenmapbool/testdata/src/seenmapbool/seenmapbool.go b/pkg/linters/seenmapbool/testdata/src/seenmapbool/seenmapbool.go index b8a74f700ac..344c630dbe2 100644 --- a/pkg/linters/seenmapbool/testdata/src/seenmapbool/seenmapbool.go +++ b/pkg/linters/seenmapbool/testdata/src/seenmapbool/seenmapbool.go @@ -39,3 +39,44 @@ func GoodBoolMapWithFalse() { flags["disabled"] = false _ = flags } + +func BadSetBoolInClosure() []string { + // Set-map declared inside a closure must be reported exactly once. + unique := func(in []string) []string { + seen := make(map[string]bool) // want `map\[string\]bool "seen" used as a set` + var out []string + for _, x := range in { + if !seen[x] { + seen[x] = true + out = append(out, x) + } + } + return out + } + return unique([]string{"a", "b", "a"}) +} + +func BadSetBoolInNestedClosure() { + // Two levels of nesting: each set-map is reported exactly once. + outer := func() { + inner := func() { + seen := make(map[string]bool) // want `map\[string\]bool "seen" used as a set` + seen["x"] = true + _ = seen + } + inner() + } + outer() +} + +func GoodOuterMapWrittenFalseInClosure() { + // Map declared in outer function but written false inside a closure — not a + // pure set; must NOT be reported. + flags := make(map[string]bool) + modify := func() { + flags["disabled"] = false + } + flags["enabled"] = true + modify() + _ = flags +} From ba9e2122fc49cfdfa2d4209f7e11b4b92f58872a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 22 Jun 2026 06:42:02 +0000 Subject: [PATCH 3/3] fix(seenmapbool): add nil guard before FuncLit check in first pass Address code review: check n == nil before the *ast.FuncLit type assertion for clearer control flow, since ast.Inspect calls the callback with nil after visiting each node's children. Co-authored-by: pelikhan <4175913+pelikhan@users.noreply.github.com> --- pkg/linters/seenmapbool/seenmapbool.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/linters/seenmapbool/seenmapbool.go b/pkg/linters/seenmapbool/seenmapbool.go index e5d91ab9ac5..2da5836e009 100644 --- a/pkg/linters/seenmapbool/seenmapbool.go +++ b/pkg/linters/seenmapbool/seenmapbool.go @@ -70,6 +70,9 @@ func inspectBody(pass *analysis.Pass, body *ast.BlockStmt, noLintLinesByFile map // Stop at nested FuncLit boundaries so each closure is handled by its own // Preorder visit — preventing duplicate diagnostics. ast.Inspect(body, func(n ast.Node) bool { + if n == nil { + return false + } if _, ok := n.(*ast.FuncLit); ok { return false // do not descend into nested closures }