|
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Testing;
using Test.Utilities;
using Xunit;
using VerifyCS = Test.Utilities.CSharpCodeFixVerifier<
Microsoft.NetCore.Analyzers.Runtime.DisposeMethodsShouldCallBaseClassDispose,
Microsoft.NetCore.CSharp.Analyzers.Runtime.CSharpDisposeMethodsShouldCallBaseClassDisposeFixer>;
using VerifyVB = Test.Utilities.VisualBasicCodeFixVerifier<
Microsoft.NetCore.Analyzers.Runtime.DisposeMethodsShouldCallBaseClassDispose,
Microsoft.NetCore.VisualBasic.Analyzers.Runtime.BasicDisposeMethodsShouldCallBaseClassDisposeFixer>;
namespace Microsoft.NetCore.Analyzers.Runtime.UnitTests
{
public class DisposeMethodsShouldCallBaseClassDisposeTests
{
private static DiagnosticResult GetCSharpResultAt(int line, int column, params string[] arguments)
#pragma warning disable RS0030 // Do not use banned APIs
=> VerifyCS.Diagnostic()
.WithLocation(line, column)
#pragma warning restore RS0030 // Do not use banned APIs
.WithArguments(arguments);
private static DiagnosticResult GetBasicResultAt(int line, int column, params string[] arguments)
#pragma warning disable RS0030 // Do not use banned APIs
=> VerifyVB.Diagnostic()
.WithLocation(line, column)
#pragma warning restore RS0030 // Do not use banned APIs
.WithArguments(arguments);
[Fact]
public async Task NoBaseDisposeImplementation_NoBaseDisposeCall_NoDiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class A
{
}
class B : A, IDisposable
{
public void Dispose()
{
}
}
");
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Class A
End Class
Class B
Inherits A
Implements IDisposable
Public Sub Dispose() Implements IDisposable.Dispose
End Sub
End Class");
}
[Fact]
public async Task NoBaseDisposeImplementation_NoBaseDisposeCall_02_NoDiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class A
{
public virtual void Dispose()
{
}
}
class B : A, IDisposable
{
public override void Dispose()
{
}
}
");
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Class A
Public Overridable Sub Dispose()
End Sub
End Class
Class B
Inherits A
Implements IDisposable
Public Overrides Sub Dispose() Implements IDisposable.Dispose
End Sub
End Class");
}
[Fact]
public async Task BaseDisposeCall_NoDiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class A : IDisposable
{
public virtual void Dispose()
{
}
}
class B : A
{
public override void Dispose()
{
base.Dispose();
}
}
");
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Class A
Implements IDisposable
Public Overridable Sub Dispose() Implements IDisposable.Dispose
End Sub
End Class
Class B
Inherits A
Public Overrides Sub Dispose()
MyBase.Dispose()
End Sub
End Class");
}
[Fact]
public async Task NoBaseDisposeCall_DiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class A : IDisposable
{
public virtual void Dispose()
{
}
}
class B : A
{
public override void Dispose()
{
}
}
",
// Test0.cs(13,26): warning CA2215: Ensure that method 'void B.Dispose()' calls 'base.Dispose()' in all possible control flow paths.
GetCSharpResultAt(13, 26, "void B.Dispose()", "base.Dispose()"));
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Class A
Implements IDisposable
Public Overridable Sub Dispose() Implements IDisposable.Dispose
End Sub
End Class
Class B
Inherits A
Public Overrides Sub Dispose()
End Sub
End Class",
// Test0.vb(14,26): warning CA2215: Ensure that method 'Sub B.Dispose()' calls 'MyBase.Dispose()' in all possible control flow paths.
GetBasicResultAt(14, 26, "Sub B.Dispose()", "MyBase.Dispose()"));
}
[Fact]
public async Task BaseDisposeCall_IgnoreCase_VB_NoDiagnosticAsync()
{
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Class A
Implements IDisposable
Public Overridable Sub Dispose() Implements IDisposable.Dispose
End Sub
End Class
Class B
Inherits A
Public Overrides Sub Dispose()
myBasE.Dispose()
End Sub
End Class");
}
[Fact]
public async Task BaseDisposeBoolCall_NoDiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class A : IDisposable
{
public virtual void Dispose(bool b)
{
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
class B : A
{
public override void Dispose(bool b)
{
base.Dispose(b);
}
}
");
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Class A
Implements IDisposable
Public Overridable Sub Dispose(b As Boolean)
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
End Class
Class B
Inherits A
Public Overrides Sub Dispose(b As Boolean)
MyBase.Dispose(b)
End Sub
End Class");
}
[Fact]
public async Task NoBaseDisposeBoolCall_DiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class A : IDisposable
{
public virtual void Dispose(bool b)
{
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
class B : A
{
public override void Dispose(bool b)
{
}
}
",
// Test0.cs(19,26): warning CA2215: Ensure that method 'void B.Dispose(bool b)' calls 'base.Dispose(bool)' in all possible control flow paths.
GetCSharpResultAt(19, 26, "void B.Dispose(bool b)", "base.Dispose(bool)"));
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Class A
Implements IDisposable
Public Overridable Sub Dispose(b As Boolean)
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
End Class
Class B
Inherits A
Public Overrides Sub Dispose(b As Boolean)
End Sub
End Class",
// Test0.vb(19,26): warning CA2215: Ensure that method 'Sub B.Dispose(b As Boolean)' calls 'MyBase.Dispose(Boolean)' in all possible control flow paths.
GetBasicResultAt(19, 26, "Sub B.Dispose(b As Boolean)", "MyBase.Dispose(Boolean)"));
}
[Fact]
public async Task NoBaseDisposeCloseCall_NoDiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class A : IDisposable
{
public virtual void Close()
{
}
public void Dispose()
{
Close();
GC.SuppressFinalize(this);
}
}
class B : A
{
public override void Close()
{
}
}
");
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Class A
Implements IDisposable
Public Overridable Sub Close()
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Close()
GC.SuppressFinalize(Me)
End Sub
End Class
Class B
Inherits A
Public Overrides Sub Close()
End Sub
End Class");
}
[Fact, WorkItem(1796, "https://github.com/dotnet/roslyn-analyzers/issues/1796")]
public async Task BaseDisposeAsyncCall_NoDiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
using System.Threading.Tasks;
class A : IDisposable
{
public void Dispose() => DisposeAsync();
public virtual Task DisposeAsync()
{
return Task.CompletedTask;
}
}
class B : A
{
public override Task DisposeAsync()
{
return base.DisposeAsync();
}
}
");
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Imports System.Threading.Tasks
Class A
Implements IDisposable
Public Sub Dispose() Implements IDisposable.Dispose
DisposeAsync()
End Sub
Public Overridable Function DisposeAsync() As Task
Return Task.CompletedTask
End Function
End Class
Class B
Inherits A
Public Overrides Function DisposeAsync() As Task
Return MyBase.DisposeAsync()
End Function
End Class");
}
[Fact, WorkItem(1796, "https://github.com/dotnet/roslyn-analyzers/issues/1796")]
public async Task NoBaseDisposeAsyncCall_DiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
using System.Threading.Tasks;
class A : IDisposable
{
public void Dispose() => DisposeAsync();
public virtual Task DisposeAsync()
{
return Task.CompletedTask;
}
}
class B : A
{
public override Task DisposeAsync()
{
return Task.CompletedTask;
}
}
",
// Test0.cs(17,26): warning CA2215: Ensure that method 'Task B.DisposeAsync()' calls 'base.DisposeAsync()' in all possible control flow paths.
GetCSharpResultAt(17, 26, "Task B.DisposeAsync()", "base.DisposeAsync()"));
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Imports System.Threading.Tasks
Class A
Implements IDisposable
Public Sub Dispose() Implements IDisposable.Dispose
DisposeAsync()
End Sub
Public Overridable Function DisposeAsync() As Task
Return Task.CompletedTask
End Function
End Class
Class B
Inherits A
Public Overrides Function DisposeAsync() As Task
Return Task.CompletedTask
End Function
End Class",
// Test0.vb(20,31): warning CA2215: Ensure that method 'Function B.DisposeAsync() As Task' calls 'MyBase.DisposeAsync()' in all possible control flow paths.
GetBasicResultAt(20, 31, "Function B.DisposeAsync() As Task", "MyBase.DisposeAsync()"));
}
[Fact, WorkItem(1796, "https://github.com/dotnet/roslyn-analyzers/issues/1796")]
public async Task BaseDisposeCoreAsyncCall_NoDiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
using System.Threading.Tasks;
abstract class A : IDisposable
{
public void Dispose() => DisposeAsync();
public Task DisposeAsync() => DisposeCoreAsync(true);
protected abstract Task DisposeCoreAsync(bool initialized);
}
class B : A
{
protected override Task DisposeCoreAsync(bool initialized)
{
return Task.CompletedTask;
}
}
class C : B
{
protected override Task DisposeCoreAsync(bool initialized)
{
return base.DisposeCoreAsync(initialized);
}
}
");
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Imports System.Threading.Tasks
MustInherit Class A
Implements IDisposable
Public Sub Dispose() Implements IDisposable.Dispose
DisposeAsync()
End Sub
Public Function DisposeAsync() As Task
Return DisposeCoreAsync(True)
End Function
Protected MustOverride Function DisposeCoreAsync(initialized As Boolean) As Task
End Class
Class B
Inherits A
Protected Overrides Function DisposeCoreAsync(initialized As Boolean) As Task
Return Task.CompletedTask
End Function
End Class
Class C
Inherits B
Protected Overrides Function DisposeCoreAsync(initialized As Boolean) As Task
Return MyBase.DisposeCoreAsync(initialized)
End Function
End Class");
}
[Fact, WorkItem(1796, "https://github.com/dotnet/roslyn-analyzers/issues/1796")]
public async Task NoBaseDisposeCoreAsyncCall_DiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
using System.Threading.Tasks;
abstract class A : IDisposable
{
public void Dispose() => DisposeAsync();
public Task DisposeAsync() => DisposeCoreAsync(true);
protected abstract Task DisposeCoreAsync(bool initialized);
}
class B : A
{
protected override Task DisposeCoreAsync(bool initialized)
{
return Task.CompletedTask;
}
}
class C : B
{
protected override Task DisposeCoreAsync(bool initialized)
{
return Task.CompletedTask;
}
}
",
// Test0.cs(24,29): warning CA2215: Ensure that method 'Task C.DisposeCoreAsync(bool initialized)' calls 'base.DisposeCoreAsync(bool)' in all possible control flow paths.
GetCSharpResultAt(24, 29, "Task C.DisposeCoreAsync(bool initialized)", "base.DisposeCoreAsync(bool)"));
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Imports System.Threading.Tasks
MustInherit Class A
Implements IDisposable
Public Sub Dispose() Implements IDisposable.Dispose
DisposeAsync()
End Sub
Public Function DisposeAsync() As Task
Return DisposeCoreAsync(True)
End Function
Protected MustOverride Function DisposeCoreAsync(initialized As Boolean) As Task
End Class
Class B
Inherits A
Protected Overrides Function DisposeCoreAsync(initialized As Boolean) As Task
Return Task.CompletedTask
End Function
End Class
Class C
Inherits B
Protected Overrides Function DisposeCoreAsync(initialized As Boolean) As Task
Return Task.CompletedTask
End Function
End Class",
// Test0.vb(30,34): warning CA2215: Ensure that method 'Function C.DisposeCoreAsync(initialized As Boolean) As Task' calls 'MyBase.DisposeCoreAsync(Boolean)' in all possible control flow paths.
GetBasicResultAt(30, 34, "Function C.DisposeCoreAsync(initialized As Boolean) As Task", "MyBase.DisposeCoreAsync(Boolean)"));
}
[Fact]
public async Task AbstractBaseDisposeMethod_NoBaseDisposeCall_NoDiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
abstract class A : IDisposable
{
public abstract void Dispose();
}
class B : A
{
public override void Dispose()
{
}
}
");
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
MustInherit Class A
Implements IDisposable
Public MustOverride Sub Dispose() Implements IDisposable.Dispose
End Class
Class B
Inherits A
Public Overrides Sub Dispose()
End Sub
End Class");
}
[Fact]
public async Task ShadowsBaseDisposeMethod_NoBaseDisposeCall_NoDiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class A : IDisposable
{
public virtual void Dispose()
{
}
}
class B : A
{
public new void Dispose()
{
}
}
");
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Class A
Implements IDisposable
Public Overridable Sub Dispose() Implements IDisposable.Dispose
End Sub
End Class
Class B
Inherits A
Public Shadows Sub Dispose()
End Sub
End Class");
}
[Fact]
public async Task Multiple_BaseDisposeCalls_NoDiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class A : IDisposable
{
public virtual void Dispose()
{
}
}
class B : A
{
public override void Dispose()
{
base.Dispose();
base.Dispose();
}
}
");
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Class A
Implements IDisposable
Public Overridable Sub Dispose() Implements IDisposable.Dispose
End Sub
End Class
Class B
Inherits A
Public Overrides Sub Dispose()
MyBase.Dispose()
MyBase.Dispose()
End Sub
End Class");
}
[Fact]
public async Task BaseDisposeCalls_AllPaths_NoDiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class A : IDisposable
{
public virtual void Dispose()
{
}
}
class B : A
{
private readonly bool flag;
public override void Dispose()
{
if (flag)
{
base.Dispose();
}
else
{
base.Dispose();
}
}
}
");
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Class A
Implements IDisposable
Public Overridable Sub Dispose() Implements IDisposable.Dispose
End Sub
End Class
Class B
Inherits A
Private ReadOnly flag As Boolean
Public Overrides Sub Dispose()
If flag Then
MyBase.Dispose()
Else
MyBase.Dispose()
End If
End Sub
End Class");
}
[Fact(Skip = "Analyzer isn't yet flow based."), WorkItem(1654, "https://github.com/dotnet/roslyn-analyzers/issues/1654")]
public async Task BaseDisposeCalls_SomePaths_DiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class A : IDisposable
{
public virtual void Dispose()
{
}
}
class B : A
{
private readonly A a;
public override void Dispose()
{
if (a != null)
{
base.Dispose();
}
}
}
",
// Test0.cs(14,26): warning CA2215: Ensure that method 'void B.Dispose()' calls 'base.Dispose()' in all possible control flow paths.
GetCSharpResultAt(14, 26, "void B.Dispose()", "base.Dispose()"));
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Class A
Implements IDisposable
Public Overridable Sub Dispose() Implements IDisposable.Dispose
End Sub
End Class
Class B
Inherits A
Private ReadOnly a As A
Public Overrides Sub Dispose()
If a IsNot Nothing Then
MyBase.Dispose()
End If
End Sub
End Class",
// Test0.vb(15,26): warning CA2215: Ensure that method 'Sub B.Dispose()' calls 'MyBase.Dispose()' in all possible control flow paths.
GetBasicResultAt(15, 26, "Sub B.Dispose()", "MyBase.Dispose()"));
}
[Fact]
public async Task BaseDisposeCall_GuardedWithBoolField_NoDiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class A : IDisposable
{
public virtual void Dispose()
{
}
}
class B : A
{
private bool disposed;
public override void Dispose()
{
if (disposed)
{
return;
}
base.Dispose();
disposed = true;
}
}
");
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Class A
Implements IDisposable
Public Overridable Sub Dispose() Implements IDisposable.Dispose
End Sub
End Class
Class B
Inherits A
Private disposed As Boolean
Public Overrides Sub Dispose()
If disposed Then
Return
End If
MyBase.Dispose()
disposed = True
End Sub
End Class");
}
[Fact]
public async Task BaseDisposeCall_DifferentOverload_DiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class A : IDisposable
{
public virtual void Dispose()
{
}
public void Dispose(int i)
{
}
}
class B : A
{
public override void Dispose()
{
Dispose(0);
}
}
",
// Test0.cs(17,26): warning CA2215: Ensure that method 'void B.Dispose()' calls 'base.Dispose()' in all possible control flow paths.
GetCSharpResultAt(17, 26, "void B.Dispose()", "base.Dispose()"));
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Class A
Implements IDisposable
Public Overridable Sub Dispose() Implements IDisposable.Dispose
End Sub
Public Overridable Sub Dispose(i As Integer)
End Sub
End Class
Class B
Inherits A
Public Overrides Sub Dispose()
Dispose(0)
End Sub
End Class",
// Test0.vb(17,26): warning CA2215: Ensure that method 'Sub B.Dispose()' calls 'MyBase.Dispose()' in all possible control flow paths.
GetBasicResultAt(17, 26, "Sub B.Dispose()", "MyBase.Dispose()"));
}
[Fact]
public async Task DisposeCall_DifferentInstance_DiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class A : IDisposable
{
public virtual void Dispose()
{
}
}
class B : A
{
private readonly A a;
public override void Dispose()
{
a.Dispose();
}
}
",
// Test0.cs(14,26): warning CA2215: Ensure that method 'void B.Dispose()' calls 'base.Dispose()' in all possible control flow paths.
GetCSharpResultAt(14, 26, "void B.Dispose()", "base.Dispose()"));
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Class A
Implements IDisposable
Public Overridable Sub Dispose() Implements IDisposable.Dispose
End Sub
End Class
Class B
Inherits A
Private ReadOnly a As A
Public Overrides Sub Dispose()
a.Dispose()
End Sub
End Class",
// Test0.vb(15,26): warning CA2215: Ensure that method 'Sub B.Dispose()' calls 'MyBase.Dispose()' in all possible control flow paths.
GetBasicResultAt(15, 26, "Sub B.Dispose()", "MyBase.Dispose()"));
}
[Fact]
public async Task DisposeCall_StaticMethod_DiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class A : IDisposable
{
public virtual void Dispose()
{
}
public static void Dispose(bool b)
{
}
}
class B : A
{
public override void Dispose()
{
A.Dispose(true);
}
}
",
// Test0.cs(17,26): warning CA2215: Ensure that method 'void B.Dispose()' calls 'base.Dispose()' in all possible control flow paths.
GetCSharpResultAt(17, 26, "void B.Dispose()", "base.Dispose()"));
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Class A
Implements IDisposable
Public Overridable Sub Dispose() Implements IDisposable.Dispose
End Sub
Public Shared Sub Dispose(b As Boolean)
End Sub
End Class
Class B
Inherits A
Public Overrides Sub Dispose()
A.Dispose(True)
End Sub
End Class",
// Test0.vb(17,26): warning CA2215: Ensure that method 'Sub B.Dispose()' calls 'MyBase.Dispose()' in all possible control flow paths.
GetBasicResultAt(17, 26, "Sub B.Dispose()", "MyBase.Dispose()"));
}
[Fact]
public async Task DisposeCall_ThisOrMeInstance_DiagnosticAsync()
{
await VerifyCS.VerifyAnalyzerAsync(@"
using System;
class A : IDisposable
{
public virtual void Dispose()
{
}
}
class B : A
{
public override void Dispose()
{
this.Dispose();
}
}
",
// Test0.cs(13,26): warning CA2215: Ensure that method 'void B.Dispose()' calls 'base.Dispose()' in all possible control flow paths.
GetCSharpResultAt(13, 26, "void B.Dispose()", "base.Dispose()"));
await VerifyVB.VerifyAnalyzerAsync(@"
Imports System
Class A
Implements IDisposable
Public Overridable Sub Dispose() Implements IDisposable.Dispose
End Sub
End Class
Class B
Inherits A
Public Overrides Sub Dispose()
Me.Dispose()
End Sub
End Class",
// Test0.vb(14,26): warning CA2215: Ensure that method 'Sub B.Dispose()' calls 'MyBase.Dispose()' in all possible control flow paths.
GetBasicResultAt(14, 26, "Sub B.Dispose()", "MyBase.Dispose()"));
}
}
}
|