Skip to content

Commit 61c6a90

Browse files
mpywclaude
andcommitted
feat: add file filtering options for test and generated files
- Add -test flag (default: true) to control analysis of *_test.go files - Auto-exclude generated files (// Code generated ... DO NOT EDIT.) using ast.IsGenerated - Generated files are always excluded (no opt-out) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 68ede3c commit 61c6a90

File tree

2 files changed

+51
-7
lines changed

2 files changed

+51
-7
lines changed

analyzer.go

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ var (
3939
enableSpawner bool
4040
enableSpawnerlabel bool
4141
enableGotask bool
42+
43+
// File filtering flags.
44+
analyzeTests bool
4245
)
4346

4447
func init() {
@@ -56,6 +59,9 @@ func init() {
5659
Analyzer.Flags.BoolVar(&enableSpawner, "spawner", true, "enable spawner checker")
5760
Analyzer.Flags.BoolVar(&enableSpawnerlabel, "spawnerlabel", false, "enable spawnerlabel checker")
5861
Analyzer.Flags.BoolVar(&enableGotask, "gotask", true, "enable gotask checker (requires -goroutine-deriver)")
62+
63+
// File filtering flags
64+
Analyzer.Flags.BoolVar(&analyzeTests, "test", true, "analyze test files (*_test.go)")
5965
}
6066

6167
// Analyzer is the main analyzer for goroutinectx.
@@ -75,11 +81,14 @@ func run(pass *analysis.Pass) (any, error) {
7581
return nil, ErrNoInspector
7682
}
7783

84+
// Build set of files to skip
85+
skipFiles := buildSkipFiles(pass)
86+
7887
// Parse configuration
7988
carriers := carrier.Parse(contextCarriers)
8089

81-
// Build ignore maps for each file
82-
ignoreMaps := buildIgnoreMaps(pass)
90+
// Build ignore maps for each file (excluding skipped files)
91+
ignoreMaps := buildIgnoreMaps(pass, skipFiles)
8392

8493
// Build spawner map from //goroutinectx:spawner directives and -external-spawner flag
8594
spawners := spawnerdir.Build(pass, externalSpawner)
@@ -88,12 +97,12 @@ func run(pass *analysis.Pass) (any, error) {
8897
enabled := buildEnabledCheckers(spawners)
8998

9099
// Run AST-based checks (goroutine, errgroup, waitgroup)
91-
runASTChecks(pass, insp, ignoreMaps, carriers, spawners)
100+
runASTChecks(pass, insp, ignoreMaps, carriers, spawners, skipFiles)
92101

93102
// Run spawnerlabel checker if enabled
94103
if enableSpawnerlabel {
95104
spawnerlabelChecker := spawnerlabel.New(spawners)
96-
spawnerlabelChecker.Check(pass, ignoreMaps)
105+
spawnerlabelChecker.Check(pass, ignoreMaps, skipFiles)
97106
}
98107

99108
// Report unused ignore directives
@@ -102,12 +111,39 @@ func run(pass *analysis.Pass) (any, error) {
102111
return nil, nil
103112
}
104113

114+
// buildSkipFiles creates a set of filenames to skip based on flags.
115+
// Generated files are always skipped.
116+
// Test files are skipped when analyzeTests is false.
117+
func buildSkipFiles(pass *analysis.Pass) map[string]bool {
118+
skipFiles := make(map[string]bool)
119+
120+
for _, file := range pass.Files {
121+
filename := pass.Fset.Position(file.Pos()).Filename
122+
123+
// Always skip generated files
124+
if ast.IsGenerated(file) {
125+
skipFiles[filename] = true
126+
continue
127+
}
128+
129+
// Skip test files if -test=false
130+
if !analyzeTests && strings.HasSuffix(filename, "_test.go") {
131+
skipFiles[filename] = true
132+
}
133+
}
134+
135+
return skipFiles
136+
}
137+
105138
// buildIgnoreMaps creates ignore maps for each file in the pass.
106-
func buildIgnoreMaps(pass *analysis.Pass) map[string]ignore.Map {
139+
func buildIgnoreMaps(pass *analysis.Pass, skipFiles map[string]bool) map[string]ignore.Map {
107140
ignoreMaps := make(map[string]ignore.Map)
108141

109142
for _, file := range pass.Files {
110143
filename := pass.Fset.Position(file.Pos()).Filename
144+
if skipFiles[filename] {
145+
continue
146+
}
111147
ignoreMaps[filename] = ignore.Build(pass.Fset, file)
112148
}
113149

@@ -121,6 +157,7 @@ func runASTChecks(
121157
ignoreMaps map[string]ignore.Map,
122158
carriers []carrier.Carrier,
123159
spawners *spawnerdir.Map,
160+
skipFiles map[string]bool,
124161
) {
125162
// Build context scopes for functions with context parameters
126163
funcScopes := buildFuncScopes(pass, insp, carriers)
@@ -171,12 +208,16 @@ func runASTChecks(
171208
return true
172209
}
173210

211+
filename := pass.Fset.Position(n.Pos()).Filename
212+
if skipFiles[filename] {
213+
return true
214+
}
215+
174216
scope := findEnclosingScope(funcScopes, stack)
175217
if scope == nil {
176218
return true // No context in scope
177219
}
178220

179-
filename := pass.Fset.Position(n.Pos()).Filename
180221
cctx := &context.CheckContext{
181222
Pass: pass,
182223
Scope: scope,

internal/checkers/spawnerlabel/checker.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,12 @@ func New(spawners *spawner.Map) *Checker {
2323
}
2424

2525
// Check runs the spawnerlabel analysis on the given pass.
26-
func (c *Checker) Check(pass *analysis.Pass, ignoreMaps map[string]ignore.Map) {
26+
func (c *Checker) Check(pass *analysis.Pass, ignoreMaps map[string]ignore.Map, skipFiles map[string]bool) {
2727
for _, file := range pass.Files {
2828
filename := pass.Fset.Position(file.Pos()).Filename
29+
if skipFiles[filename] {
30+
continue
31+
}
2932
ignoreMap := ignoreMaps[filename]
3033

3134
for _, decl := range file.Decls {

0 commit comments

Comments
 (0)