@@ -54,8 +54,10 @@ func (a *parallelAnalyzer) run(pass *analysis.Pass) (interface{}, error) {
54
54
inspector .Preorder (nodeFilter , func (node ast.Node ) {
55
55
funcDecl := node .(* ast.FuncDecl )
56
56
var funcHasParallelMethod ,
57
+ funcCantParallelMethod ,
57
58
rangeStatementOverTestCasesExists ,
58
- rangeStatementHasParallelMethod bool
59
+ rangeStatementHasParallelMethod ,
60
+ rangeStatementCantParallelMethod bool
59
61
var loopVariableUsedInRun * string
60
62
var numberOfTestRun int
61
63
var positionOfTestRunNode []ast.Node
@@ -77,20 +79,29 @@ func (a *parallelAnalyzer) run(pass *analysis.Pass) (interface{}, error) {
77
79
funcHasParallelMethod = methodParallelIsCalledInTestFunction (n , testVar )
78
80
}
79
81
82
+ // Check if the test calls t.Setenv, cannot be used in parallel tests or tests with parallel ancestors
83
+ if ! funcCantParallelMethod {
84
+ funcCantParallelMethod = methodSetenvIsCalledInTestFunction (n , testVar )
85
+ }
86
+
80
87
// Check if the t.Run within the test function is calling t.Parallel
81
88
if methodRunIsCalledInTestFunction (n , testVar ) {
82
89
// n is a call to t.Run; find out the name of the subtest's *testing.T parameter.
83
90
innerTestVar := getRunCallbackParameterName (n )
84
91
85
92
hasParallel := false
93
+ cantParallel := false
86
94
numberOfTestRun ++
87
95
ast .Inspect (v , func (p ast.Node ) bool {
88
96
if ! hasParallel {
89
97
hasParallel = methodParallelIsCalledInTestFunction (p , innerTestVar )
90
98
}
99
+ if ! cantParallel {
100
+ cantParallel = methodSetenvIsCalledInTestFunction (p , innerTestVar )
101
+ }
91
102
return true
92
103
})
93
- if ! hasParallel {
104
+ if ! hasParallel && ! cantParallel {
94
105
positionOfTestRunNode = append (positionOfTestRunNode , n )
95
106
}
96
107
}
@@ -122,6 +133,10 @@ func (a *parallelAnalyzer) run(pass *analysis.Pass) (interface{}, error) {
122
133
rangeStatementHasParallelMethod = methodParallelIsCalledInMethodRun (r .X , innerTestVar )
123
134
}
124
135
136
+ if ! rangeStatementCantParallelMethod {
137
+ rangeStatementCantParallelMethod = methodSetenvIsCalledInMethodRun (r .X , innerTestVar )
138
+ }
139
+
125
140
if loopVariableUsedInRun == nil {
126
141
if run , ok := r .X .(* ast.CallExpr ); ok {
127
142
loopVariableUsedInRun = loopVarReferencedInRun (run , loopVars , pass .TypesInfo )
@@ -134,12 +149,17 @@ func (a *parallelAnalyzer) run(pass *analysis.Pass) (interface{}, error) {
134
149
}
135
150
}
136
151
137
- if ! a .ignoreMissing && ! funcHasParallelMethod {
152
+ // Descendents which call Setenv, also prevent tests from calling Parallel
153
+ if rangeStatementCantParallelMethod {
154
+ funcCantParallelMethod = true
155
+ }
156
+
157
+ if ! a .ignoreMissing && ! funcHasParallelMethod && ! funcCantParallelMethod {
138
158
pass .Reportf (node .Pos (), "Function %s missing the call to method parallel\n " , funcDecl .Name .Name )
139
159
}
140
160
141
161
if rangeStatementOverTestCasesExists && rangeNode != nil {
142
- if ! rangeStatementHasParallelMethod {
162
+ if ! rangeStatementHasParallelMethod && ! rangeStatementCantParallelMethod {
143
163
if ! a .ignoreMissing && ! a .ignoreMissingSubtests {
144
164
pass .Reportf (rangeNode .Pos (), "Range statement for test %s missing the call to method parallel in test Run\n " , funcDecl .Name .Name )
145
165
}
@@ -162,27 +182,31 @@ func (a *parallelAnalyzer) run(pass *analysis.Pass) (interface{}, error) {
162
182
}
163
183
164
184
func methodParallelIsCalledInMethodRun (node ast.Node , testVar string ) bool {
165
- var methodParallelCalled bool
185
+ return targetMethodIsCalledInMethodRun (node , testVar , "Parallel" )
186
+ }
187
+
188
+ func methodSetenvIsCalledInMethodRun (node ast.Node , testVar string ) bool {
189
+ return targetMethodIsCalledInMethodRun (node , testVar , "Setenv" )
190
+ }
191
+
192
+ func targetMethodIsCalledInMethodRun (node ast.Node , testVar , targetMethod string ) bool {
193
+ var called bool
166
194
// nolint: gocritic
167
195
switch callExp := node .(type ) {
168
196
case * ast.CallExpr :
169
197
for _ , arg := range callExp .Args {
170
- if ! methodParallelCalled {
198
+ if ! called {
171
199
ast .Inspect (arg , func (n ast.Node ) bool {
172
- if ! methodParallelCalled {
173
- methodParallelCalled = methodParallelIsCalledInRunMethod (n , testVar )
200
+ if ! called {
201
+ called = exprCallHasMethod (n , testVar , targetMethod )
174
202
return true
175
203
}
176
204
return false
177
205
})
178
206
}
179
207
}
180
208
}
181
- return methodParallelCalled
182
- }
183
-
184
- func methodParallelIsCalledInRunMethod (node ast.Node , testVar string ) bool {
185
- return exprCallHasMethod (node , testVar , "Parallel" )
209
+ return called
186
210
}
187
211
188
212
func methodParallelIsCalledInTestFunction (node ast.Node , testVar string ) bool {
@@ -196,6 +220,11 @@ func methodRunIsCalledInRangeStatement(node ast.Node, testVar string) bool {
196
220
func methodRunIsCalledInTestFunction (node ast.Node , testVar string ) bool {
197
221
return exprCallHasMethod (node , testVar , "Run" )
198
222
}
223
+
224
+ func methodSetenvIsCalledInTestFunction (node ast.Node , testVar string ) bool {
225
+ return exprCallHasMethod (node , testVar , "Setenv" )
226
+ }
227
+
199
228
func exprCallHasMethod (node ast.Node , receiverName , methodName string ) bool {
200
229
// nolint: gocritic
201
230
switch n := node .(type ) {
0 commit comments