diff --git a/src/nunit.analyzers.tests/DisposeFieldsAndPropertiesInTearDown/DisposeFieldsAndPropertiesInTearDownAnalyzerTests.cs b/src/nunit.analyzers.tests/DisposeFieldsAndPropertiesInTearDown/DisposeFieldsAndPropertiesInTearDownAnalyzerTests.cs index 76ce0981..3fc1d600 100644 --- a/src/nunit.analyzers.tests/DisposeFieldsAndPropertiesInTearDown/DisposeFieldsAndPropertiesInTearDownAnalyzerTests.cs +++ b/src/nunit.analyzers.tests/DisposeFieldsAndPropertiesInTearDown/DisposeFieldsAndPropertiesInTearDownAnalyzerTests.cs @@ -14,6 +14,15 @@ sealed class DummyDisposable : IDisposable public void Dispose() {} }"; + private const string DummyExplicitDisposable = @" + sealed class DummyExplicitDisposable : IAnotherInterface, IDisposable + { + void IDisposable.Dispose() {} + } + public interface IAnotherInterface + { + }"; + // CA2000 allows transfer of ownership using ICollection.Add private const string Disposer = @" private sealed class Disposer : IDisposable @@ -114,6 +123,29 @@ public void TearDownMethod() RoslynAssert.Valid(analyzer, testCode); } + [Test] + public void AnalyzeWhenExplicitlyInterfaceImplementedDisposableFieldSetInConstructorIsDisposedInOneTimeTearDownMethod() + { + var testCode = TestUtility.WrapMethodInClassNamespaceAndAddUsings($@" + private IAnotherInterface field; + + public TestClass() + {{ + field = new DummyExplicitDisposable(); + }} + + [OneTimeTearDown] + public void TearDownMethod() + {{ + ((IDisposable)field).Dispose(); + }} + + {DummyExplicitDisposable} + "); + + RoslynAssert.Valid(analyzer, testCode); + } + [TestCase("", "OneTime")] [TestCase("OneTime", "")] public void AnalyzeWhenFieldIsDisposedInWrongMethod(string attributePrefix1, string attributePrefix2) @@ -1005,10 +1037,10 @@ public void TearDown() public partial class TestFixture { [Test] - public void SomeTest() - { - SomeAsserts(); - } + public void SomeTest() + { + SomeAsserts(); + } }"); RoslynAssert.Valid(analyzer, testCodePart1, testCodePart2); diff --git a/src/nunit.analyzers/DisposeFieldsAndPropertiesInTearDown/DisposeFieldsAndPropertiesInTearDownAnalyzer.cs b/src/nunit.analyzers/DisposeFieldsAndPropertiesInTearDown/DisposeFieldsAndPropertiesInTearDownAnalyzer.cs index 3be0f389..74f2ce2c 100644 --- a/src/nunit.analyzers/DisposeFieldsAndPropertiesInTearDown/DisposeFieldsAndPropertiesInTearDownAnalyzer.cs +++ b/src/nunit.analyzers/DisposeFieldsAndPropertiesInTearDown/DisposeFieldsAndPropertiesInTearDownAnalyzer.cs @@ -555,6 +555,16 @@ private static void DisposedIn(Parameters parameters, HashSet disposals, return memberAccessExpression.Name.Identifier.Text; } + // considering cast to IDisposable, e.g. in case of explicit interface implementation of IDisposable.Dispose() + else if (expression is ParenthesizedExpressionSyntax parenthesizedExpression && + parenthesizedExpression.Expression is CastExpressionSyntax castExpression && + castExpression.Expression is IdentifierNameSyntax castIdentifierName && + castExpression.Type is IdentifierNameSyntax typeIdentifierName && + typeIdentifierName.Identifier.Text.Equals("IDisposable", StringComparison.Ordinal)) + { + return castIdentifierName.Identifier.Text; + } + return null; }