Skip to content

Commit 2f44856

Browse files
Add DoNotUseThreadSleepAnalyzer to warn against Thread.Sleep in async methods (#304)
1 parent 27f90f9 commit 2f44856

6 files changed

Lines changed: 444 additions & 25 deletions

File tree

Lines changed: 342 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,342 @@
1+
using NUnit.Framework;
2+
using System.Threading.Tasks;
3+
using Verify = ErrorProne.NET.TestHelpers.CSharpCodeFixVerifier<
4+
ErrorProne.NET.AsyncAnalyzers.DoNotUseThreadSleepAnalyzer,
5+
Microsoft.CodeAnalysis.Testing.EmptyCodeFixProvider>;
6+
7+
using T = System.Threading.Thread;
8+
9+
namespace ErrorProne.NET.CoreAnalyzers.Tests.AsyncAnalyzers
10+
{
11+
[TestFixture]
12+
public class DoNotUseThreadSleepAnalyzerTests
13+
{
14+
[Test]
15+
public async Task WarnOnThreadSleepInAsyncMethod()
16+
{
17+
var test = @"
18+
using System;
19+
using System.Threading;
20+
using System.Threading.Tasks;
21+
using T = System.Threading.Thread;
22+
23+
class Test {
24+
async Task Foo()
25+
{
26+
[|Thread.Sleep(1000)|];
27+
await Task.Delay(1);
28+
[|T.Sleep(500)|];
29+
}
30+
}
31+
";
32+
await Verify.VerifyAsync(test);
33+
}
34+
35+
[Test]
36+
public async Task WarnOnThreadSleepInAsyncMethod_With_TimeSpan()
37+
{
38+
var test = @"
39+
using System;
40+
using System.Threading;
41+
using System.Threading.Tasks;
42+
43+
class Test {
44+
async Task Foo()
45+
{
46+
[|Thread.Sleep(TimeSpan.FromSeconds(1))|];
47+
await Task.Delay(1);
48+
}
49+
}
50+
";
51+
await Verify.VerifyAsync(test);
52+
}
53+
54+
[Test]
55+
public async Task WarnOnThreadSleepInAsyncVoidMethod()
56+
{
57+
var test = @"
58+
using System;
59+
using System.Threading;
60+
using System.Threading.Tasks;
61+
62+
class Test {
63+
async void Bar()
64+
{
65+
[|Thread.Sleep(500)|];
66+
await Task.Delay(1);
67+
}
68+
}
69+
";
70+
await Verify.VerifyAsync(test);
71+
}
72+
73+
[Test]
74+
public async Task WarnOnThreadSleepWithTimeSpanInAsyncMethod()
75+
{
76+
var test = @"
77+
using System;
78+
using System.Threading;
79+
using System.Threading.Tasks;
80+
81+
class Test {
82+
async Task<int> GetValue()
83+
{
84+
[|Thread.Sleep(TimeSpan.FromSeconds(1))|];
85+
await Task.Delay(1);
86+
return 42;
87+
}
88+
}
89+
";
90+
await Verify.VerifyAsync(test);
91+
}
92+
93+
[Test]
94+
public async Task NoWarningOnThreadSleepInSyncMethod()
95+
{
96+
var test = @"
97+
using System;
98+
using System.Threading;
99+
100+
class Test {
101+
void SyncMethod()
102+
{
103+
Thread.Sleep(1000);
104+
}
105+
106+
int GetValue()
107+
{
108+
Thread.Sleep(500);
109+
return 42;
110+
}
111+
}
112+
";
113+
await Verify.VerifyAsync(test);
114+
}
115+
116+
[Test]
117+
public async Task NoWarningOnTaskDelayInAsyncMethod()
118+
{
119+
var test = @"
120+
using System;
121+
using System.Threading.Tasks;
122+
123+
class Test {
124+
async Task Foo()
125+
{
126+
await Task.Delay(1000);
127+
}
128+
129+
async Task<string> GetData()
130+
{
131+
await Task.Delay(TimeSpan.FromSeconds(1));
132+
return ""data"";
133+
}
134+
}
135+
";
136+
await Verify.VerifyAsync(test);
137+
}
138+
139+
[Test]
140+
public async Task NoWarningOnOtherMethodsWithSleepName()
141+
{
142+
var test = @"
143+
using System;
144+
using System.Threading.Tasks;
145+
146+
class CustomClass
147+
{
148+
public static void Sleep(int ms) { }
149+
}
150+
151+
class Test {
152+
async Task Foo()
153+
{
154+
CustomClass.Sleep(1000);
155+
await Task.Delay(1);
156+
}
157+
}
158+
";
159+
await Verify.VerifyAsync(test);
160+
}
161+
162+
[Test]
163+
public async Task WarnOnThreadSleepInAsyncLambda()
164+
{
165+
var test = @"
166+
using System;
167+
using System.Threading;
168+
using System.Threading.Tasks;
169+
170+
class Test {
171+
void TestAsyncLambda()
172+
{
173+
Func<Task> asyncLambda = async () =>
174+
{
175+
[|Thread.Sleep(1000)|];
176+
await Task.Delay(1);
177+
};
178+
}
179+
}
180+
";
181+
await Verify.VerifyAsync(test);
182+
}
183+
184+
[Test]
185+
public async Task WarnOnThreadSleepInAsyncLambdaWithParameter()
186+
{
187+
var test = @"
188+
using System;
189+
using System.Threading;
190+
using System.Threading.Tasks;
191+
192+
class Test {
193+
void TestAsyncLambdaWithParam()
194+
{
195+
Func<int, Task> asyncLambda = async (x) =>
196+
{
197+
[|Thread.Sleep(x * 100)|];
198+
await Task.Delay(x);
199+
};
200+
}
201+
}
202+
";
203+
await Verify.VerifyAsync(test);
204+
}
205+
206+
[Test]
207+
public async Task WarnOnThreadSleepInAsyncLocalFunction()
208+
{
209+
var test = @"
210+
using System;
211+
using System.Threading;
212+
using System.Threading.Tasks;
213+
214+
class Test {
215+
void TestAsyncLocalFunction()
216+
{
217+
async Task LocalAsyncMethod()
218+
{
219+
[|Thread.Sleep(500)|];
220+
await Task.Delay(1);
221+
}
222+
223+
LocalAsyncMethod();
224+
}
225+
}
226+
";
227+
await Verify.VerifyAsync(test);
228+
}
229+
230+
[Test]
231+
public async Task WarnOnThreadSleepInAsyncLocalFunctionWithReturnValue()
232+
{
233+
var test = @"
234+
using System;
235+
using System.Threading;
236+
using System.Threading.Tasks;
237+
238+
class Test {
239+
void TestAsyncLocalFunctionWithReturn()
240+
{
241+
async Task<int> LocalAsyncMethod(int input)
242+
{
243+
[|Thread.Sleep(input)|];
244+
await Task.Delay(1);
245+
return input * 2;
246+
}
247+
248+
var result = LocalAsyncMethod(100);
249+
}
250+
}
251+
";
252+
await Verify.VerifyAsync(test);
253+
}
254+
255+
[Test]
256+
public async Task NoWarningOnThreadSleepInSyncLambda()
257+
{
258+
var test = @"
259+
using System;
260+
using System.Threading;
261+
using System.Threading.Tasks;
262+
263+
class Test {
264+
void TestSyncLambda()
265+
{
266+
Action syncLambda = () =>
267+
{
268+
Thread.Sleep(1000);
269+
};
270+
271+
Func<int, int> syncLambdaWithReturn = (x) =>
272+
{
273+
Thread.Sleep(x);
274+
return x * 2;
275+
};
276+
}
277+
}
278+
";
279+
await Verify.VerifyAsync(test);
280+
}
281+
282+
[Test]
283+
public async Task NoWarningOnThreadSleepInSyncLocalFunction()
284+
{
285+
var test = @"
286+
using System;
287+
using System.Threading;
288+
289+
class Test {
290+
void TestSyncLocalFunction()
291+
{
292+
void LocalSyncMethod()
293+
{
294+
Thread.Sleep(500);
295+
}
296+
297+
int LocalSyncMethodWithReturn(int input)
298+
{
299+
Thread.Sleep(input);
300+
return input * 2;
301+
}
302+
303+
LocalSyncMethod();
304+
var result = LocalSyncMethodWithReturn(100);
305+
}
306+
}
307+
";
308+
await Verify.VerifyAsync(test);
309+
}
310+
311+
[Test]
312+
public async Task WarnOnThreadSleepInNestedAsyncLambda()
313+
{
314+
var test = @"
315+
using System;
316+
using System.Threading;
317+
using System.Threading.Tasks;
318+
319+
class Test {
320+
async Task TestNestedAsyncLambda()
321+
{
322+
Func<Task> outerLambda = async () =>
323+
{
324+
Func<Task> innerLambda = async () =>
325+
{
326+
[|Thread.Sleep(200)|];
327+
await Task.Delay(1);
328+
};
329+
330+
await innerLambda();
331+
[|Thread.Sleep(100)|];
332+
await Task.Delay(1);
333+
};
334+
335+
await outerLambda();
336+
}
337+
}
338+
";
339+
await Verify.VerifyAsync(test);
340+
}
341+
}
342+
}

src/ErrorProne.NET.CoreAnalyzers/AnalyzerReleases.Unshipped.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ EPC29 | CodeSmell | Warning | ExcludeFromCodeCoverageMessageAnalyzer: Warn when
2424
EPC30 | CodeSmell | Warning | RecursiveCallAnalyzer: Warns when a method calls itself recursively (conditionally or unconditionally).
2525
EPC31 | CodeSmell | Warning | DoNotReturnNullForTaskLikeAnalyzer
2626
EPC32 | CodeSmell | Warning | TaskCompletionSourceRunContinuationsAnalyzer
27+
EPC33 | CodeSmell | Warning | DoNotUseThreadSleepAnalyzer
2728
ERP021 | CodeSmell | Warning | ThrowExAnalyzer
2829
ERP022 | CodeSmell | Warning | SwallowAllExceptionsAnalyzer
2930
ERP031 | Concurrency | Warning | ConcurrentCollectionAnalyzer

0 commit comments

Comments
 (0)