Skip to content

Commit 8e3822d

Browse files
BoukeJohanLarsson
authored andcommitted
Support using declaration in IDISP013
1 parent a445e62 commit 8e3822d

3 files changed

Lines changed: 86 additions & 5 deletions

File tree

IDisposableAnalyzers.Test/IDISP013AwaitInUsingTests/Diagnostics.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,32 @@ private static async ValueTask<int> InnerAsync()
6060
RoslynAssert.Diagnostics(Analyzer, ExpectedDiagnostic, code);
6161
}
6262

63+
[Test]
64+
public static void UsingDeclaration()
65+
{
66+
var code = @"
67+
namespace N
68+
{
69+
using System.Threading.Tasks;
70+
71+
public static class C
72+
{
73+
public static ValueTask<int> MAsync()
74+
{
75+
using var file = System.IO.File.OpenRead(string.Empty);
76+
return ↓InnerAsync();
77+
}
78+
79+
private static async ValueTask<int> InnerAsync()
80+
{
81+
await Task.Delay(10).ConfigureAwait(false);
82+
return 1;
83+
}
84+
}
85+
}";
86+
RoslynAssert.Diagnostics(Analyzer, ExpectedDiagnostic, code);
87+
}
88+
6389
[Test]
6490
public static void LocalTask()
6591
{

IDisposableAnalyzers.Test/IDISP013AwaitInUsingTests/Valid.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,28 @@ public async Task<string> M()
3232
RoslynAssert.Valid(Analyzer, code);
3333
}
3434

35+
[Test]
36+
public static void AwaitWebClientDownloadStringTaskAsyncInUsingDeclaration()
37+
{
38+
var code = @"
39+
#pragma warning disable SYSLIB0014
40+
namespace N
41+
{
42+
using System.Net;
43+
using System.Threading.Tasks;
44+
45+
public class C
46+
{
47+
public Task<string> M()
48+
{
49+
var client = new WebClient();
50+
return client.DownloadStringTaskAsync(string.Empty);
51+
}
52+
}
53+
}";
54+
RoslynAssert.Valid(Analyzer, code);
55+
}
56+
3557
[Test]
3658
public static void UsingAwaited()
3759
{

IDisposableAnalyzers/Analyzers/ReturnValueAnalyzer.cs

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
namespace IDisposableAnalyzers
22
{
3+
using System.Collections.Generic;
34
using System.Collections.Immutable;
5+
using System.Linq;
46
using System.Threading;
57

68
using Gu.Roslyn.AnalyzerExtensions;
7-
9+
using Gu.Roslyn.CodeFixExtensions;
810
using Microsoft.CodeAnalysis;
911
using Microsoft.CodeAnalysis.CSharp;
1012
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -123,17 +125,48 @@ private static void HandleReturnValue(SyntaxNodeAnalysisContext context, Express
123125
}
124126

125127
if (ReturnType(context)?.IsAwaitable() == true &&
126-
returnValue.TryFirstAncestor<UsingStatementSyntax>(out var usingStatement) &&
127-
usingStatement.Statement.Contains(returnValue) &&
128+
IsInUsingScope(returnValue) &&
128129
!returnValue.TryFirstAncestorOrSelf<AwaitExpressionSyntax>(out _) &&
129-
context.SemanticModel.TryGetType(returnValue, context.CancellationToken, out var returnValueType) &&
130-
returnValueType.IsAwaitable() &&
130+
context.SemanticModel.TryGetType(returnValue, context.CancellationToken, out var returnValueType2) &&
131+
returnValueType2.IsAwaitable() &&
131132
ShouldAwait(context, returnValue))
132133
{
133134
context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP013AwaitInUsing, returnValue.GetLocation()));
134135
}
135136
}
136137

138+
private static bool IsInUsingScope(SyntaxNode node)
139+
{
140+
return IsInUsingStatement(node) || HasPrecedingUsingDeclaration(node);
141+
}
142+
143+
private static bool IsInUsingStatement(SyntaxNode node)
144+
{
145+
return node.TryFirstAncestor<UsingStatementSyntax>(out var usingStatement) &&
146+
usingStatement.Statement.Contains(node);
147+
}
148+
149+
private static bool HasPrecedingUsingDeclaration(SyntaxNode node)
150+
{
151+
if (node.TryFirstAncestor<UsingStatementSyntax>(out var usingStatement) &&
152+
usingStatement.Statement.Contains(node))
153+
{
154+
return true;
155+
}
156+
157+
if (node.Parent?.ChildNodes().TakeWhile(x => x != node).OfType<LocalDeclarationStatementSyntax>().Any(x => x.UsingKeyword.Text == "using") ?? false)
158+
{
159+
return true;
160+
}
161+
162+
if (node.Parent is { } parent)
163+
{
164+
return HasPrecedingUsingDeclaration(parent);
165+
}
166+
167+
return false;
168+
}
169+
137170
private static bool IsInUsing(ISymbol symbol, CancellationToken cancellationToken)
138171
{
139172
return symbol.TrySingleDeclaration<SyntaxNode>(cancellationToken, out var declaration) &&

0 commit comments

Comments
 (0)