Skip to content

Commit 853ed8e

Browse files
committed
Simplify.
1 parent da1f7ae commit 853ed8e

5 files changed

Lines changed: 314 additions & 131 deletions

File tree

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
namespace IDisposableAnalyzers.NetCoreTests.IDISP002DisposeMemberTests
2+
{
3+
using Gu.Roslyn.Asserts;
4+
using Microsoft.CodeAnalysis.CodeFixes;
5+
using Microsoft.CodeAnalysis.Diagnostics;
6+
using NUnit.Framework;
7+
8+
[Ignore("Not sure how we want the code gen.")]
9+
public static class CodeFix
10+
{
11+
private static readonly DiagnosticAnalyzer Analyzer = new FieldAndPropertyDeclarationAnalyzer();
12+
private static readonly CodeFixProvider Fix = new DisposeMemberFix();
13+
private static readonly ExpectedDiagnostic ExpectedDiagnostic = ExpectedDiagnostic.Create(Descriptors.IDISP002DisposeMember);
14+
15+
[Test]
16+
public static void FieldIAsyncDisposable()
17+
{
18+
var before = @"
19+
namespace N
20+
{
21+
using System;
22+
using System.IO;
23+
using System.Threading.Tasks;
24+
25+
public class C : IAsyncDisposable
26+
{
27+
↓private readonly IAsyncDisposable disposable = File.OpenRead(string.Empty);
28+
29+
public ValueTask DisposeAsync()
30+
{
31+
}
32+
}
33+
}";
34+
35+
var code = @"
36+
namespace N
37+
{
38+
using System;
39+
using System.IO;
40+
using System.Threading.Tasks;
41+
42+
public class C : IAsyncDisposable
43+
{
44+
private readonly IAsyncDisposable disposable = File.OpenRead(string.Empty);
45+
46+
public async ValueTask DisposeAsync()
47+
{
48+
await this.disposable.DisposeAsync().ConfigureAwait(false);
49+
}
50+
}
51+
}";
52+
RoslynAssert.CodeFix(Analyzer, Fix, ExpectedDiagnostic, before, code);
53+
}
54+
55+
[Test]
56+
public static void FieldOfTypeObjectIAsyncDisposable()
57+
{
58+
var before = @"
59+
namespace N
60+
{
61+
using System;
62+
using System.IO;
63+
using System.Threading.Tasks;
64+
65+
public class C : IAsyncDisposable
66+
{
67+
↓private readonly object disposable = File.OpenRead(string.Empty);
68+
69+
public ValueTask DisposeAsync()
70+
{
71+
}
72+
}
73+
}";
74+
75+
var code = @"
76+
namespace N
77+
{
78+
using System;
79+
using System.IO;
80+
using System.Threading.Tasks;
81+
82+
public class C : IAsyncDisposable
83+
{
84+
private readonly object disposable = File.OpenRead(string.Empty);
85+
86+
public async ValueTask DisposeAsync()
87+
{
88+
await ((IAsyncDisposable)this.disposable).DisposeAsync().ConfigureAwait(false);
89+
}
90+
}
91+
}";
92+
RoslynAssert.CodeFix(Analyzer, Fix, ExpectedDiagnostic, before, code);
93+
}
94+
95+
[Test]
96+
public static void FieldIAsyncDisposableAndIDisposable1()
97+
{
98+
var before = @"
99+
namespace N
100+
{
101+
using System;
102+
using System.IO;
103+
using System.Threading.Tasks;
104+
105+
sealed class C : IDisposable, IAsyncDisposable
106+
{
107+
↓private readonly Stream disposable = File.OpenRead(string.Empty);
108+
109+
public void Dispose()
110+
{
111+
this.disposable.Dispose();
112+
}
113+
114+
public ValueTask DisposeAsync()
115+
{
116+
}
117+
}
118+
}";
119+
120+
var code = @"
121+
namespace N
122+
{
123+
using System;
124+
using System.IO;
125+
using System.Threading.Tasks;
126+
127+
sealed class C : IDisposable, IAsyncDisposable
128+
{
129+
private readonly IAsyncDisposable disposable = File.OpenRead(string.Empty);
130+
131+
public void Dispose()
132+
{
133+
this.disposable.Dispose();
134+
}
135+
136+
public async ValueTask DisposeAsync()
137+
{
138+
await this.disposable.DisposeAsync().ConfigureAwait(false);
139+
}
140+
}
141+
}";
142+
RoslynAssert.CodeFix(Analyzer, Fix, ExpectedDiagnostic, before, code);
143+
}
144+
145+
[Test]
146+
public static void FieldIAsyncDisposableAndIDisposable2()
147+
{
148+
var before = @"
149+
namespace N
150+
{
151+
using System;
152+
using System.IO;
153+
using System.Threading.Tasks;
154+
155+
sealed class C : IDisposable, IAsyncDisposable
156+
{
157+
↓private readonly Stream disposable = File.OpenRead(string.Empty);
158+
159+
public void Dispose()
160+
{
161+
}
162+
163+
public async ValueTask DisposeAsync()
164+
{
165+
await this.disposable.DisposeAsync();
166+
}
167+
}
168+
}";
169+
170+
var code = @"
171+
namespace N
172+
{
173+
using System;
174+
using System.IO;
175+
using System.Threading.Tasks;
176+
177+
sealed class C : IDisposable, IAsyncDisposable
178+
{
179+
private readonly Stream disposable = File.OpenRead(string.Empty);
180+
181+
public void Dispose()
182+
{
183+
this.disposable.Dispose();
184+
}
185+
186+
public async ValueTask DisposeAsync()
187+
{
188+
await this.disposable.DisposeAsync();
189+
}
190+
}
191+
}";
192+
RoslynAssert.CodeFix(Analyzer, Fix, ExpectedDiagnostic, before, code);
193+
}
194+
}
195+
}

IDisposableAnalyzers/Analyzers/FieldAndPropertyDeclarationAnalyzer.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,21 @@ private static void HandleFieldOrProperty(SyntaxNodeAnalysisContext context, Fie
8787
additionalLocations: disposeAsync.Locations));
8888
}
8989

90-
if (DisposableMember.IsDisposed(member, context.SemanticModel, context.CancellationToken).IsEither(Result.No, Result.AssumeNo))
90+
if (DisposeMethod.FindFirst(member.FieldOrProperty.ContainingType, context.Compilation, Search.TopLevel) is { } dispose &&
91+
!DisposableMember.IsDisposed(member.FieldOrProperty, dispose, context.SemanticModel, context.CancellationToken))
9192
{
92-
context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP002DisposeMember, context.Node.GetLocation()));
93+
dispose = DisposeMethod.FindVirtual(member.FieldOrProperty.ContainingType, context.Compilation, Search.TopLevel) ?? dispose;
94+
context.ReportDiagnostic(
95+
Diagnostic.Create(
96+
Descriptors.IDISP002DisposeMember,
97+
context.Node.GetLocation(),
98+
additionalLocations: dispose.Locations));
99+
}
93100

94-
if (DisposeMethod.FindFirst(member.FieldOrProperty.ContainingType, context.Compilation, Search.TopLevel) is null)
95-
{
96-
context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP006ImplementIDisposable, member.Declaration.GetLocation()));
97-
}
101+
if (DisposableMember.IsDisposed(member, context.SemanticModel, context.CancellationToken).IsEither(Result.No, Result.AssumeNo) &&
102+
DisposeMethod.FindFirst(member.FieldOrProperty.ContainingType, context.Compilation, Search.TopLevel) is null)
103+
{
104+
context.ReportDiagnostic(Diagnostic.Create(Descriptors.IDISP006ImplementIDisposable, member.Declaration.GetLocation()));
98105
}
99106
}
100107
}

0 commit comments

Comments
 (0)