Skip to content

Commit d52f31a

Browse files
committed
Return nullable.
1 parent a50bf33 commit d52f31a

9 files changed

Lines changed: 57 additions & 51 deletions

File tree

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[*.cs]
2+
dotnet_diagnostic.IDE1000.severity = none
3+
dotnet_diagnostic.IDE1006.severity = none

IDisposableAnalyzers.NetCoreTests/IDISP005ReturnTypeShouldBeIDisposableTests/Valid.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ public class C
2525
}
2626
}";
2727

28-
RoslynAssert.Valid(Analyzer, code);
28+
RoslynAssert.Valid(Analyzer, Descriptor, code);
2929
}
3030
}
3131
}

IDisposableAnalyzers.NetCoreTests/IDisposableAnalyzers.NetCoreTests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
<SignAssembly>true</SignAssembly>
1010
<AssemblyOriginatorKeyFile>..\IDisposableAnalyzers.snk</AssemblyOriginatorKeyFile>
1111
</PropertyGroup>
12-
12+
1313
<ItemGroup>
1414
<ProjectReference Include="..\IDisposableAnalyzers\IDisposableAnalyzers.csproj" />
1515
</ItemGroup>
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
[*.cs]
2+
dotnet_diagnostic.IDE1000.severity = none
23
dotnet_diagnostic.IDE1006.severity = none

IDisposableAnalyzers.Test/Helpers/DisposeMethodTests.cs

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ public static class DisposeMethodTests
99
{
1010
[TestCase(Search.TopLevel)]
1111
[TestCase(Search.Recursive)]
12-
public static void TryFindIDisposableDispose(Search search)
12+
public static void Find(Search search)
1313
{
1414
var code = @"
1515
namespace N
@@ -43,14 +43,13 @@ private void ThrowIfDisposed()
4343
var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
4444
var semanticModel = compilation.GetSemanticModel(syntaxTree);
4545
var method = semanticModel.GetDeclaredSymbol(syntaxTree.FindClassDeclaration("C"));
46-
Assert.AreEqual(true, DisposeMethod.TryFind(method, compilation, search, out var match));
47-
Assert.AreEqual("N.C.Dispose()", match.ToString());
46+
Assert.AreEqual("N.C.Dispose()", DisposeMethod.Find(method, compilation, search).ToString());
4847
}
4948

5049
[Ignore("Not sure if we want to find explicit.")]
5150
[TestCase(Search.TopLevel)]
5251
[TestCase(Search.Recursive)]
53-
public static void TryFindIDisposableDisposeWhenExplicit(Search search)
52+
public static void FindWhenExplicit(Search search)
5453
{
5554
var code = @"
5655
namespace N
@@ -84,8 +83,7 @@ private void ThrowIfDisposed()
8483
var compilation = CSharpCompilation.Create("test", new[] { syntaxTree }, MetadataReferences.FromAttributes());
8584
var semanticModel = compilation.GetSemanticModel(syntaxTree);
8685
var method = semanticModel.GetDeclaredSymbol(syntaxTree.FindClassDeclaration("C"));
87-
Assert.AreEqual(true, DisposeMethod.TryFind(method, compilation, search, out var match));
88-
Assert.AreEqual("N.C.Dispose()", match.ToString());
86+
Assert.AreEqual("N.C.Dispose()", DisposeMethod.Find(method, compilation, search).ToString());
8987
}
9088

9189
[TestCase(Search.TopLevel)]

IDisposableAnalyzers/Analyzers/ClassDeclarationAnalyzer.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ private static void Handle(SyntaxNodeAnalysisContext context)
2525
if (context.Node is ClassDeclarationSyntax { } classDeclaration &&
2626
context.ContainingSymbol is INamedTypeSymbol { IsSealed: false } type &&
2727
type.IsAssignableTo(KnownSymbol.IDisposable, context.SemanticModel.Compilation) &&
28-
DisposeMethod.TryFind(type, context.Compilation, Search.TopLevel, out var disposeMethod) &&
29-
disposeMethod is { IsVirtual: false, IsOverride: false } &&
28+
DisposeMethod.Find(type, context.Compilation, Search.TopLevel) is { IsVirtual: false, IsOverride: false } disposeMethod &&
3029
!DisposeMethod.TryFindVirtual(type, context.Compilation, Search.TopLevel, out _))
3130
{
3231
context.ReportDiagnostic(

IDisposableAnalyzers/CodeFixes/DisposeMemberFix.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,14 @@ void Dispose(DocumentEditor editor, CancellationToken cancellationToken)
7878
}
7979
}
8080
}
81-
else if (DisposeMethod.TryFind(symbol.ContainingType, semanticModel.Compilation, Search.TopLevel, out disposeSymbol) &&
82-
disposeSymbol.TrySingleDeclaration(context.CancellationToken, out disposeDeclaration))
81+
else if (DisposeMethod.Find(symbol.ContainingType, semanticModel.Compilation, Search.TopLevel) is { } disposeMethod &&
82+
disposeMethod.TrySingleDeclaration(context.CancellationToken, out disposeDeclaration))
8383
{
8484
switch (disposeDeclaration)
8585
{
8686
case { ExpressionBody: { Expression: { } expression } }:
8787
context.RegisterCodeFix(
88-
$"{symbol.Name}.Dispose() in {disposeSymbol}",
88+
$"{symbol.Name}.Dispose() in {disposeMethod}",
8989
(editor, cancellationToken) => editor.ReplaceNode(
9090
disposeDeclaration,
9191
x => x.AsBlockBody(
@@ -96,7 +96,7 @@ void Dispose(DocumentEditor editor, CancellationToken cancellationToken)
9696
break;
9797
case { Body: { } body }:
9898
context.RegisterCodeFix(
99-
$"{symbol.Name}.Dispose() in {disposeSymbol}",
99+
$"{symbol.Name}.Dispose() in {disposeMethod}",
100100
(editor, cancellationToken) => Dispose(editor, cancellationToken),
101101
"Dispose member.",
102102
diagnostic);

IDisposableAnalyzers/CodeFixes/Helpers/IDisposableFactory.cs

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,7 @@ ExpressionSyntax Normalize(ExpressionSyntax e)
9191
if (semanticModel.ClassifyConversion(e, KnownSymbol.IDisposable.GetTypeSymbol(semanticModel.Compilation)).IsImplicit)
9292
{
9393
if (semanticModel.TryGetType(e, cancellationToken, out var type) &&
94-
DisposeMethod.TryFind(type, semanticModel.Compilation, Search.Recursive, out var disposeMethod) &&
95-
disposeMethod.ExplicitInterfaceImplementations.IsEmpty)
94+
DisposeMethod.Find(type, semanticModel.Compilation, Search.Recursive) is { ExplicitInterfaceImplementations: { IsEmpty: true } })
9695
{
9796
return e.WithoutTrivia()
9897
.WithLeadingElasticLineFeed();
@@ -113,8 +112,7 @@ internal static ExpressionStatementSyntax DisposeStatement(FieldOrProperty dispo
113112
if (IsNeverNull(out var neverNull))
114113
{
115114
if (disposable.Type.IsAssignableTo(KnownSymbol.IDisposable, semanticModel.Compilation) &&
116-
DisposeMethod.TryFind(disposable.Type, semanticModel.Compilation, Search.Recursive, out var disposeMethod) &&
117-
disposeMethod.ExplicitInterfaceImplementations.IsEmpty)
115+
DisposeMethod.Find(disposable.Type, semanticModel.Compilation, Search.Recursive) is { ExplicitInterfaceImplementations: { IsEmpty: true } })
118116
{
119117
return DisposeStatement(neverNull.WithoutTrivia()).WithLeadingElasticLineFeed();
120118
}

IDisposableAnalyzers/Helpers/DisposeMethod.cs

Lines changed: 40 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -9,40 +9,31 @@
99

1010
internal static class DisposeMethod
1111
{
12-
internal static bool TryFindFirst(ITypeSymbol type, Compilation compilation, Search search, [NotNullWhen(true)] out IMethodSymbol? disposeMethod)
12+
internal static IMethodSymbol? Find(ITypeSymbol type, Compilation compilation, Search search)
1313
{
14-
if (search == Search.TopLevel)
14+
if (!type.IsAssignableTo(KnownSymbol.IDisposable, compilation))
1515
{
16-
return TryFind(type, compilation, search, out disposeMethod) ||
17-
TryFindVirtual(type, compilation, search, out disposeMethod);
16+
return null;
1817
}
1918

20-
while (type.IsAssignableTo(KnownSymbol.IDisposable, compilation))
19+
if (search == Search.TopLevel)
2120
{
22-
if (TryFindFirst(type, compilation, Search.TopLevel, out disposeMethod))
23-
{
24-
return true;
25-
}
26-
27-
type = type.BaseType;
21+
return type.TryFindFirstMethod("Dispose", x => IsMatch(x), out var topLevel)
22+
? topLevel
23+
: null;
2824
}
2925

30-
disposeMethod = null;
31-
return false;
32-
}
26+
return type.TryFindFirstMethodRecursive("Dispose", x => IsMatch(x), out var recursive)
27+
? recursive
28+
: null;
3329

34-
internal static bool IsAccessibleOn(ITypeSymbol type, Compilation compilation)
35-
{
36-
if (type.TypeKind == TypeKind.Interface)
30+
static bool IsMatch(IMethodSymbol candidate)
3731
{
38-
return type.IsAssignableTo(KnownSymbol.IDisposable, compilation);
32+
return candidate is { DeclaredAccessibility: Accessibility.Public, ReturnsVoid: true, Name: "Dispose", Parameters: { Length: 0 } };
3933
}
40-
41-
return TryFind(type, compilation, Search.Recursive, out var disposeMethod) &&
42-
disposeMethod.ExplicitInterfaceImplementations.IsEmpty;
4334
}
4435

45-
internal static bool TryFind(ITypeSymbol type, Compilation compilation, Search search, [NotNullWhen(true)] out IMethodSymbol? disposeMethod)
36+
internal static bool TryFindVirtual(ITypeSymbol type, Compilation compilation, Search search, [NotNullWhen(true)] out IMethodSymbol? disposeMethod)
4637
{
4738
disposeMethod = null;
4839
if (!type.IsAssignableTo(KnownSymbol.IDisposable, compilation))
@@ -59,30 +50,46 @@ internal static bool TryFind(ITypeSymbol type, Compilation compilation, Search s
5950

6051
static bool IsMatch(IMethodSymbol candidate)
6152
{
62-
return candidate is { DeclaredAccessibility: Accessibility.Public, ReturnsVoid: true, Name: "Dispose", Parameters: { Length: 0 } };
53+
return IsOverrideDispose(candidate) ||
54+
IsVirtualDispose(candidate);
6355
}
6456
}
6557

66-
internal static bool TryFindVirtual(ITypeSymbol type, Compilation compilation, Search search, [NotNullWhen(true)] out IMethodSymbol? disposeMethod)
58+
internal static bool TryFindFirst(ITypeSymbol type, Compilation compilation, Search search, [NotNullWhen(true)] out IMethodSymbol? disposeMethod)
6759
{
68-
disposeMethod = null;
69-
if (!type.IsAssignableTo(KnownSymbol.IDisposable, compilation))
60+
if (search == Search.TopLevel)
7061
{
71-
return false;
62+
if (Find(type, compilation, search) is { } match)
63+
{
64+
disposeMethod = match;
65+
return true;
66+
}
67+
68+
return TryFindVirtual(type, compilation, search, out disposeMethod);
7269
}
7370

74-
if (search == Search.TopLevel)
71+
while (type.IsAssignableTo(KnownSymbol.IDisposable, compilation))
7572
{
76-
return type.TryFindFirstMethod("Dispose", x => IsMatch(x), out disposeMethod);
73+
if (TryFindFirst(type, compilation, Search.TopLevel, out disposeMethod))
74+
{
75+
return true;
76+
}
77+
78+
type = type.BaseType;
7779
}
7880

79-
return type.TryFindFirstMethodRecursive("Dispose", x => IsMatch(x), out disposeMethod);
81+
disposeMethod = null;
82+
return false;
83+
}
8084

81-
static bool IsMatch(IMethodSymbol candidate)
85+
internal static bool IsAccessibleOn(ITypeSymbol type, Compilation compilation)
86+
{
87+
if (type.TypeKind == TypeKind.Interface)
8288
{
83-
return IsOverrideDispose(candidate) ||
84-
IsVirtualDispose(candidate);
89+
return type.IsAssignableTo(KnownSymbol.IDisposable, compilation);
8590
}
91+
92+
return Find(type, compilation, Search.Recursive) is { ExplicitInterfaceImplementations: { IsEmpty: true } };
8693
}
8794

8895
internal static bool TryFindBaseCall(MethodDeclarationSyntax virtualDispose, SemanticModel semanticModel, CancellationToken cancellationToken, [NotNullWhen(true)] out InvocationExpressionSyntax? baseCall)

0 commit comments

Comments
 (0)