@@ -12,18 +12,17 @@ import (
1212 "golang.org/x/tools/go/analysis/passes/inspect"
1313 "golang.org/x/tools/go/ast/inspector"
1414
15- "github.com/mpyw/goroutinectx/internal/checkers"
16- "github.com/mpyw/goroutinectx/internal/checkers/errgroup"
17- "github.com/mpyw/goroutinectx/internal/checkers/goroutine"
18- "github.com/mpyw/goroutinectx/internal/checkers/goroutinederive"
15+ "github.com/mpyw/goroutinectx/internal/checker"
1916 "github.com/mpyw/goroutinectx/internal/checkers/gotask"
2017 "github.com/mpyw/goroutinectx/internal/checkers/spawner"
2118 "github.com/mpyw/goroutinectx/internal/checkers/spawnerlabel"
22- "github.com/mpyw/goroutinectx/internal/checkers/waitgroup"
2319 "github.com/mpyw/goroutinectx/internal/context"
2420 "github.com/mpyw/goroutinectx/internal/directives/carrier"
21+ "github.com/mpyw/goroutinectx/internal/directives/deriver"
2522 "github.com/mpyw/goroutinectx/internal/directives/ignore"
2623 spawnerdir "github.com/mpyw/goroutinectx/internal/directives/spawner"
24+ "github.com/mpyw/goroutinectx/internal/patterns"
25+ "github.com/mpyw/goroutinectx/internal/registry"
2726 internalssa "github.com/mpyw/goroutinectx/internal/ssa"
2827)
2928
@@ -139,7 +138,7 @@ func buildIgnoreMaps(pass *analysis.Pass, skipFiles map[string]bool) map[string]
139138 return ignoreMaps
140139}
141140
142- // runASTChecks runs AST-based checkers on the pass.
141+ // runASTChecks runs checkers on the pass using the unified SSA-based checker .
143142func runASTChecks (
144143 pass * analysis.Pass ,
145144 insp * inspector.Inspector ,
@@ -148,49 +147,73 @@ func runASTChecks(
148147 spawners * spawnerdir.Map ,
149148 skipFiles map [string ]bool ,
150149) {
151- // Build SSA program for future use (currently unused but required for buildssa dependency)
152- _ = internalssa .Build (pass )
150+ // Build SSA program
151+ ssaProg : = internalssa .Build (pass )
153152
154- // Build context scopes for functions with context parameters
155- funcScopes := buildFuncScopes ( pass , insp , carriers )
153+ // Create registry and register APIs
154+ reg := registry . New ( )
156155
157- // Build checkers based on flags
158- var (
159- callCheckers []checkers. CallChecker
160- goStmtCheckers []checkers. GoStmtChecker
161- )
156+ // Register errgroup/waitgroup/conc APIs with ClosureCapturesCtx pattern
157+ checker . RegisterDefaultAPIs ( reg , enableErrgroup , enableWaitgroup )
158+
159+ // Build GoStmt patterns
160+ var goPatterns []patterns. GoStmtPattern
162161
163162 if enableGoroutine {
164- goStmtCheckers = append (goStmtCheckers , goroutine . New () )
163+ goPatterns = append (goPatterns , & patterns. GoStmtCapturesCtx {} )
165164 }
166165
167166 if goroutineDeriver != "" {
168- goStmtCheckers = append (goStmtCheckers , goroutinederive .New (goroutineDeriver ))
167+ matcher := deriver .NewMatcher (goroutineDeriver )
168+ goPatterns = append (goPatterns , & patterns.GoStmtCallsDeriver {Matcher : matcher })
169169 }
170170
171- if enableWaitgroup {
172- callCheckers = append (callCheckers , waitgroup .New ())
171+ // Map pattern names to ignore checker names
172+ checkerNames := map [string ]ignore.CheckerName {
173+ "GoStmtCapturesCtx" : ignore .Goroutine ,
174+ "GoStmtCallsDeriver" : ignore .GoroutineDerive ,
175+ "ClosureCapturesCtx" : ignore .Errgroup , // errgroup/waitgroup use this
173176 }
174177
175- if enableErrgroup {
176- callCheckers = append (callCheckers , errgroup .New ())
177- }
178+ // Create and run unified checker
179+ unifiedChecker := checker .New (
180+ reg ,
181+ goPatterns ,
182+ ssaProg ,
183+ carriers ,
184+ ignoreMaps ,
185+ skipFiles ,
186+ checkerNames ,
187+ )
188+ unifiedChecker .Run (pass , insp )
178189
179- // Add spawner checker if enabled and any functions are marked
180- if enableSpawner && spawners .Len () > 0 {
181- callCheckers = append (callCheckers , spawner .New (spawners ))
182- }
190+ // Run remaining checkers that aren't migrated yet (spawner, gotask)
191+ runLegacyCheckers (pass , insp , ignoreMaps , carriers , spawners , skipFiles )
192+ }
183193
184- // gotask checker requires goroutine-deriver to be set
185- if goroutineDeriver != "" && enableGotask {
186- callCheckers = append (callCheckers , gotask .New (goroutineDeriver ))
194+ // runLegacyCheckers runs checkers that haven't been migrated to the unified checker yet.
195+ func runLegacyCheckers (
196+ pass * analysis.Pass ,
197+ insp * inspector.Inspector ,
198+ ignoreMaps map [string ]ignore.Map ,
199+ carriers []carrier.Carrier ,
200+ spawners * spawnerdir.Map ,
201+ skipFiles map [string ]bool ,
202+ ) {
203+ // Only spawner and gotask need the legacy path
204+ spawnerEnabled := enableSpawner && spawners .Len () > 0
205+ gotaskEnabled := goroutineDeriver != "" && enableGotask
206+ if ! spawnerEnabled && ! gotaskEnabled {
207+ return
187208 }
188209
210+ // Build context scopes for functions with context parameters
211+ funcScopes := buildFuncScopes (pass , insp , carriers )
212+
189213 // Node types we're interested in
190214 nodeFilter := []ast.Node {
191215 (* ast .FuncDecl )(nil ),
192216 (* ast .FuncLit )(nil ),
193- (* ast .GoStmt )(nil ),
194217 (* ast .CallExpr )(nil ),
195218 }
196219
@@ -217,14 +240,14 @@ func runASTChecks(
217240 Carriers : carriers ,
218241 }
219242
220- switch node := n .(type ) {
221- case * ast. GoStmt :
222- for _ , checker := range goStmtCheckers {
223- checker . CheckGoStmt ( cctx , node )
243+ if call , ok := n .(* ast. CallExpr ); ok {
244+ // Spawner checker
245+ if enableSpawner && spawners . Len () > 0 {
246+ spawner . New ( spawners ). CheckCall ( cctx , call )
224247 }
225- case * ast. CallExpr :
226- for _ , checker := range callCheckers {
227- checker . CheckCall (cctx , node )
248+ // Gotask checker
249+ if goroutineDeriver != "" && enableGotask {
250+ gotask . New ( goroutineDeriver ). CheckCall (cctx , call )
228251 }
229252 }
230253
0 commit comments