Skip to content

Commit b75905d

Browse files
committed
Figure out if member is disposed.
Fix #192.
1 parent df0ff18 commit b75905d

5 files changed

Lines changed: 122 additions & 5 deletions

File tree

IDisposableAnalyzers.Test/Helpers/DisposableMemberTests.cs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,42 @@ public void Dispose()
2727
this.stream.Dispose();
2828
}
2929
}
30+
}");
31+
var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
32+
var semanticModel = compilation.GetSemanticModel(syntaxTree);
33+
var field = syntaxTree.FindFieldDeclaration("stream");
34+
var fieldSymbol = semanticModel.GetDeclaredSymbolSafe(field, CancellationToken.None);
35+
Assert.AreEqual(Result.Yes, DisposableMember.IsDisposed(new FieldOrProperty(fieldSymbol), (TypeDeclarationSyntax)field.Parent, semanticModel, CancellationToken.None));
36+
}
37+
38+
[Test]
39+
public static void DiposedInOverridden()
40+
{
41+
var syntaxTree = CSharpSyntaxTree.ParseText(@"
42+
namespace N
43+
{
44+
using System;
45+
using System.IO;
46+
47+
abstract class BaseClass : IDisposable
48+
{
49+
public void Dispose()
50+
{
51+
this.M();
52+
}
53+
54+
protected abstract void M();
55+
}
56+
57+
class C : BaseClass
58+
{
59+
private readonly IDisposable stream = File.OpenRead(string.Empty);
60+
61+
protected override void M()
62+
{
63+
this.stream.Dispose();
64+
}
65+
}
3066
}");
3167
var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
3268
var semanticModel = compilation.GetSemanticModel(syntaxTree);

IDisposableAnalyzers.Test/IDISP002DisposeMemberTests/Valid.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1024,5 +1024,45 @@ public void Dispose()
10241024

10251025
RoslynAssert.Valid(Analyzer, pairOfT, staticPairCode, code);
10261026
}
1027+
1028+
[Test]
1029+
public static void WhenOverriddenIsNotVirtualDispose()
1030+
{
1031+
var baseClass = @"
1032+
namespace N
1033+
{
1034+
using System;
1035+
using System.IO;
1036+
1037+
abstract class BaseClass : IDisposable
1038+
{
1039+
public void Dispose()
1040+
{
1041+
this.M();
1042+
}
1043+
1044+
protected abstract void M();
1045+
}
1046+
}";
1047+
1048+
var code = @"
1049+
namespace N
1050+
{
1051+
using System;
1052+
using System.IO;
1053+
1054+
class C : BaseClass
1055+
{
1056+
private readonly Stream stream = File.OpenRead(string.Empty);
1057+
1058+
protected override void M()
1059+
{
1060+
this.stream.Dispose();
1061+
}
1062+
}
1063+
}";
1064+
1065+
RoslynAssert.Valid(Analyzer, baseClass, code);
1066+
}
10271067
}
10281068
}

IDisposableAnalyzers.Test/IDISP006ImplementIDisposableTests/ValidCode.Inheritance.cs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,46 @@ protected override void Dispose(bool disposing)
173173

174174
RoslynAssert.Valid(Analyzer, Descriptor, DisposableCode, baseClass, code);
175175
}
176+
177+
[Test]
178+
public static void WhenOverriddenIsNotVirtualDispose()
179+
{
180+
var baseClass = @"
181+
namespace N
182+
{
183+
using System;
184+
using System.IO;
185+
186+
abstract class BaseClass : IDisposable
187+
{
188+
public void Dispose()
189+
{
190+
this.M();
191+
}
192+
193+
protected abstract void M();
194+
}
195+
}";
196+
197+
var code = @"
198+
namespace N
199+
{
200+
using System;
201+
using System.IO;
202+
203+
class C : BaseClass
204+
{
205+
private readonly Stream stream = File.OpenRead(string.Empty);
206+
207+
protected override void M()
208+
{
209+
this.stream.Dispose();
210+
}
211+
}
212+
}";
213+
214+
RoslynAssert.Valid(Analyzer, Descriptor, baseClass, code);
215+
}
176216
}
177217
}
178218
}

IDisposableAnalyzers/Helpers/DisposableMember.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@ internal static class DisposableMember
99
{
1010
internal static Result IsDisposed(FieldOrProperty member, TypeDeclarationSyntax context, SemanticModel semanticModel, CancellationToken cancellationToken)
1111
{
12-
if (semanticModel.TryGetSymbol(context, cancellationToken, out var symbol))
12+
if (semanticModel.TryGetNamedType(context, cancellationToken, out var symbol))
1313
{
1414
return IsDisposed(member, symbol, semanticModel, cancellationToken);
1515
}
1616

1717
return Result.Unknown;
1818
}
1919

20-
internal static Result IsDisposed(FieldOrProperty member, ITypeSymbol context, SemanticModel semanticModel, CancellationToken cancellationToken)
20+
internal static Result IsDisposed(FieldOrProperty member, INamedTypeSymbol context, SemanticModel semanticModel, CancellationToken cancellationToken)
2121
{
2222
if (context == null)
2323
{

IDisposableAnalyzers/Helpers/Walkers/DisposeWalker.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,13 @@ public override void VisitIdentifierName(IdentifierNameSyntax node)
3737
base.VisitIdentifierName(node);
3838
}
3939

40-
internal static DisposeWalker Borrow(ITypeSymbol type, SemanticModel semanticModel, CancellationToken cancellationToken)
40+
internal static DisposeWalker Borrow(INamedTypeSymbol type, SemanticModel semanticModel, CancellationToken cancellationToken)
4141
{
4242
if (type.IsAssignableTo(KnownSymbol.IDisposable, semanticModel.Compilation) &&
43-
DisposeMethod.TryFindFirst(type, semanticModel.Compilation, Search.Recursive, out var disposeMethod))
43+
DisposeMethod.TryFindFirst(type, semanticModel.Compilation, Search.Recursive, out var disposeMethod) &&
44+
disposeMethod.TrySingleDeclaration(cancellationToken, out MethodDeclarationSyntax? declaration))
4445
{
45-
return Borrow(disposeMethod, semanticModel, cancellationToken);
46+
return BorrowAndVisit(declaration, SearchScope.Instance, type, semanticModel, () => new DisposeWalker(), cancellationToken);
4647
}
4748

4849
return Borrow(() => new DisposeWalker());

0 commit comments

Comments
 (0)