diff --git a/src/nunit.analyzers.tests/DiagnosticSuppressors/NonNullableFieldOrPropertyIsUninitializedSuppressorTests.cs b/src/nunit.analyzers.tests/DiagnosticSuppressors/NonNullableFieldOrPropertyIsUninitializedSuppressorTests.cs index 3f6e54bc..3de7fd0e 100644 --- a/src/nunit.analyzers.tests/DiagnosticSuppressors/NonNullableFieldOrPropertyIsUninitializedSuppressorTests.cs +++ b/src/nunit.analyzers.tests/DiagnosticSuppressors/NonNullableFieldOrPropertyIsUninitializedSuppressorTests.cs @@ -123,6 +123,51 @@ public void Test() RoslynAssert.Suppressed(suppressor, testCode); } + [Test] + public void FieldAssignedInTryFinally() + { + var testCode = TestUtility.WrapClassInNamespaceAndAddUsing(@" + public class BaseClass + { + [SetUp] + public virtual void SetUp() + { + } + + [TearDown] + public virtual void TearDown() + { + } + } + + public class TestClass : BaseClass + { + private string ↓field; + + public override void SetUp() + { + base.SetUp(); + try + { + field = string.Empty; + } + catch + { + base.TearDown(); + } + } + + [Test] + public void Test() + { + Assert.That(field, Is.Not.Null); + } + } + "); + + RoslynAssert.Suppressed(suppressor, testCode); + } + [Test] public void PropertyNotAssigned() { diff --git a/src/nunit.analyzers/DiagnosticSuppressors/NonNullableFieldOrPropertyIsUninitializedSuppressor.cs b/src/nunit.analyzers/DiagnosticSuppressors/NonNullableFieldOrPropertyIsUninitializedSuppressor.cs index 04ce52ad..0c67bbae 100644 --- a/src/nunit.analyzers/DiagnosticSuppressors/NonNullableFieldOrPropertyIsUninitializedSuppressor.cs +++ b/src/nunit.analyzers/DiagnosticSuppressors/NonNullableFieldOrPropertyIsUninitializedSuppressor.cs @@ -127,18 +127,49 @@ private static bool IsAssignedIn( return IsAssignedIn(model, classDeclaration, method.ExpressionBody.Expression, fieldOrPropertyName); } - if (method.Body is null) + if (method.Body is not null) { - return false; + return IsAssignedIn(model, classDeclaration, method.Body, fieldOrPropertyName); } - foreach (var statement in method.Body.Statements) + return false; + } + + private static bool IsAssignedIn( + SemanticModel model, + ClassDeclarationSyntax classDeclaration, + StatementSyntax statement, + string fieldOrPropertyName) + { + switch (statement) { - if (statement is ExpressionStatementSyntax expressionStatement && - IsAssignedIn(model, classDeclaration, expressionStatement.Expression, fieldOrPropertyName)) - { + case ExpressionStatementSyntax expressionStatement: + return IsAssignedIn(model, classDeclaration, expressionStatement.Expression, fieldOrPropertyName); + + case BlockSyntax block: + return IsAssignedIn(model, classDeclaration, block.Statements, fieldOrPropertyName); + + case TryStatementSyntax tryStatement: + return IsAssignedIn(model, classDeclaration, tryStatement.Block, fieldOrPropertyName) || + (tryStatement.Finally is not null && + IsAssignedIn(model, classDeclaration, tryStatement.Finally.Block, fieldOrPropertyName)); + + default: + // Any conditional statement does not guarantee assignment. + return false; + } + } + + private static bool IsAssignedIn( + SemanticModel model, + ClassDeclarationSyntax classDeclaration, + SyntaxList statements, + string fieldOrPropertyName) + { + foreach (var statement in statements) + { + if (IsAssignedIn(model, classDeclaration, statement, fieldOrPropertyName)) return true; - } } return false;