From 474a363bd3c77600264d0314439912f1836964a1 Mon Sep 17 00:00:00 2001 From: Pavel Mikula <57188685+pavel-mikula-sonarsource@users.noreply.github.com> Date: Wed, 5 Apr 2023 16:46:38 +0200 Subject: [PATCH] Fix S3900 FP: After coalesce with assignment. (#7023) --- ...tence.Query.Sql--netstandard2.0-S3900.json | 26 ---- ...istence.TestKit--netstandard2.0-S2259.json | 17 ++ .../Akka.TestKit--netstandard2.0-S3900.json | 78 ---------- .../Roslyn/OperationProcessors/IsNull.cs | 10 +- ...ymbolicExecutionTest.ExceptionCandidate.cs | 10 +- .../RoslynSymbolicExecutionTest.IsNull.cs | 147 +++++++++++++----- .../RoslynSymbolicExecutionTest.Loops.cs | 11 +- .../Roslyn/NullPointerDereference.cs | 18 +++ ...icMethodArgumentsShouldBeCheckedForNull.cs | 13 +- 9 files changed, 173 insertions(+), 157 deletions(-) create mode 100644 analyzers/its/expected/akka.net/Akka.Persistence.TestKit--netstandard2.0-S2259.json diff --git a/analyzers/its/expected/akka.net/Akka.Persistence.Query.Sql--netstandard2.0-S3900.json b/analyzers/its/expected/akka.net/Akka.Persistence.Query.Sql--netstandard2.0-S3900.json index 65a5db7f00f..d5f2b205b14 100644 --- a/analyzers/its/expected/akka.net/Akka.Persistence.Query.Sql--netstandard2.0-S3900.json +++ b/analyzers/its/expected/akka.net/Akka.Persistence.Query.Sql--netstandard2.0-S3900.json @@ -12,32 +12,6 @@ "endColumn": 38 } } -}, -{ -"id": "S3900", -"message": "Refactor this method to add validation of parameter 'offset' before using it.", -"location": { -"uri": "sources\akka.net\src\contrib\persistence\Akka.Persistence.Query.Sql\SqlReadJournal.cs", -"region": { -"startLine": 211, -"startColumn": 84, -"endLine": 211, -"endColumn": 90 -} -} -}, -{ -"id": "S3900", -"message": "Refactor this method to add validation of parameter 'offset' before using it.", -"location": { -"uri": "sources\akka.net\src\contrib\persistence\Akka.Persistence.Query.Sql\SqlReadJournal.cs", -"region": { -"startLine": 232, -"startColumn": 84, -"endLine": 232, -"endColumn": 90 -} -} } ] } diff --git a/analyzers/its/expected/akka.net/Akka.Persistence.TestKit--netstandard2.0-S2259.json b/analyzers/its/expected/akka.net/Akka.Persistence.TestKit--netstandard2.0-S2259.json new file mode 100644 index 00000000000..8415f070e7c --- /dev/null +++ b/analyzers/its/expected/akka.net/Akka.Persistence.TestKit--netstandard2.0-S2259.json @@ -0,0 +1,17 @@ +{ +"issues": [ +{ +"id": "S2259", +"message": "'(IEnumerable)w.Payload' is null on at least one execution path.", +"location": { +"uri": "sources\akka.net\src\core\Akka.Persistence.TestKit\Journal\TestJournal.cs", +"region": { +"startLine": 52, +"startColumn": 39, +"endLine": 52, +"endColumn": 88 +} +} +} +] +} diff --git a/analyzers/its/expected/akka.net/Akka.TestKit--netstandard2.0-S3900.json b/analyzers/its/expected/akka.net/Akka.TestKit--netstandard2.0-S3900.json index 7294d4729e6..b1436bd9469 100644 --- a/analyzers/its/expected/akka.net/Akka.TestKit--netstandard2.0-S3900.json +++ b/analyzers/its/expected/akka.net/Akka.TestKit--netstandard2.0-S3900.json @@ -28,19 +28,6 @@ }, { "id": "S3900", -"message": "Refactor this method to add validation of parameter 'matchedEventHandler' before using it.", -"location": { -"uri": "sources\akka.net\src\core\Akka.TestKit\EventFilter\Internal\EventFilterApplier.cs", -"region": { -"startLine": 287, -"startColumn": 44, -"endLine": 287, -"endColumn": 63 -} -} -}, -{ -"id": "S3900", "message": "Refactor this method to add validation of parameter 'func' before using it.", "location": { "uri": "sources\akka.net\src\core\Akka.TestKit\EventFilter\Internal\EventFilterApplier.cs", @@ -54,32 +41,6 @@ }, { "id": "S3900", -"message": "Refactor this method to add validation of parameter 'matchedEventHandler' before using it.", -"location": { -"uri": "sources\akka.net\src\core\Akka.TestKit\EventFilter\Internal\EventFilterApplier.cs", -"region": { -"startLine": 293, -"startColumn": 48, -"endLine": 293, -"endColumn": 67 -} -} -}, -{ -"id": "S3900", -"message": "Refactor this method to add validation of parameter 'matchedEventHandler' before using it.", -"location": { -"uri": "sources\akka.net\src\core\Akka.TestKit\EventFilter\Internal\EventFilterApplier.cs", -"region": { -"startLine": 320, -"startColumn": 44, -"endLine": 320, -"endColumn": 63 -} -} -}, -{ -"id": "S3900", "message": "Refactor this method to add validation of parameter 'system' before using it.", "location": { "uri": "sources\akka.net\src\core\Akka.TestKit\EventFilter\Internal\EventFilterApplier.cs", @@ -93,19 +54,6 @@ }, { "id": "S3900", -"message": "Refactor this method to add validation of parameter 'matchedEventHandler' before using it.", -"location": { -"uri": "sources\akka.net\src\core\Akka.TestKit\EventFilter\Internal\EventFilterApplier.cs", -"region": { -"startLine": 350, -"startColumn": 44, -"endLine": 350, -"endColumn": 63 -} -} -}, -{ -"id": "S3900", "message": "Refactor this method to add validation of parameter 'func' before using it.", "location": { "uri": "sources\akka.net\src\core\Akka.TestKit\EventFilter\Internal\EventFilterApplier.cs", @@ -123,32 +71,6 @@ "location": { "uri": "sources\akka.net\src\core\Akka.TestKit\EventFilter\Internal\EventFilterApplier.cs", "region": { -"startLine": 356, -"startColumn": 48, -"endLine": 356, -"endColumn": 67 -} -} -}, -{ -"id": "S3900", -"message": "Refactor this method to add validation of parameter 'matchedEventHandler' before using it.", -"location": { -"uri": "sources\akka.net\src\core\Akka.TestKit\EventFilter\Internal\EventFilterApplier.cs", -"region": { -"startLine": 383, -"startColumn": 44, -"endLine": 383, -"endColumn": 63 -} -} -}, -{ -"id": "S3900", -"message": "Refactor this method to add validation of parameter 'matchedEventHandler' before using it.", -"location": { -"uri": "sources\akka.net\src\core\Akka.TestKit\EventFilter\Internal\EventFilterApplier.cs", -"region": { "startLine": 404, "startColumn": 28, "endLine": 404, diff --git a/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/OperationProcessors/IsNull.cs b/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/OperationProcessors/IsNull.cs index 463759a1a54..25e04eb594c 100644 --- a/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/OperationProcessors/IsNull.cs +++ b/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/OperationProcessors/IsNull.cs @@ -32,9 +32,13 @@ protected override SymbolicConstraint BoolConstraintFromOperation(ProgramState s ? BoolConstraint.From(value.HasConstraint(ObjectConstraint.Null)) : null; - protected override ProgramState LearnBranchingConstraint(ProgramState state, IIsNullOperationWrapper operation, bool falseBranch) => - state.ResolveCapture(operation.Operand).TrackedSymbol() is { } testedSymbol + protected override ProgramState LearnBranchingConstraint(ProgramState state, IIsNullOperationWrapper operation, bool falseBranch) + { + var constraint = falseBranch ? ObjectConstraint.NotNull : ObjectConstraint.Null; + state = state.SetOperationConstraint(operation.Operand, constraint); + return state.ResolveCapture(operation.Operand).TrackedSymbol() is { } testedSymbol // Can't use ObjectConstraint.ApplyOpposite() because here, we are sure that it is either Null or NotNull - ? state.SetSymbolConstraint(testedSymbol, falseBranch ? ObjectConstraint.NotNull : ObjectConstraint.Null) + ? state.SetSymbolConstraint(testedSymbol, constraint) : state; + } } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.ExceptionCandidate.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.ExceptionCandidate.cs index 07f010e11f3..0847f1829cf 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.ExceptionCandidate.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.ExceptionCandidate.cs @@ -373,18 +373,16 @@ public void ExceptionCandidate_ForEachLoop() } tag = ""AfterCatch"";"; var validator = SETestContext.CreateCS(code, ", IEnumerable collection").Validator; - validator.ValidateTagOrder("BeforeTry", "InTry", "InCatch", "AfterCatch", "InCatch", "InCatch", "AfterCatch"); - + validator.ValidateTagOrder("BeforeTry", "InTry", "InCatch", "AfterCatch", "InCatch", "InCatch", "InCatch", "AfterCatch", "AfterCatch", "InCatch"); // IForEachLoopOperation is not generated. It doesn't seem to be used. - // In the case of foreach, there are implicit method calls that in the current implementation can throw: // - IEnumerable<>.GetEnumerator() // - System.Collections.IEnumerator.MoveNext() // - System.IDisposable.Dispose() - validator.TagStates("InCatch").Should().HaveCount(3) + validator.TagStates("InCatch").Should().HaveCount(5) .And.Contain(x => HasExceptionOfType(x, "NullReferenceException")) - .And.Subject.Where(HasUnknownException).Should().HaveCount(2); - validator.ExitStates.Should().HaveCount(2).And.OnlyContain(x => HasNoException(x)); + .And.Subject.Where(HasUnknownException).Should().HaveCount(3); + validator.ExitStates.Should().HaveCount(3).And.OnlyContain(x => HasNoException(x)); } [TestMethod] diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.IsNull.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.IsNull.cs index b94c43ea68e..9abe85861cf 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.IsNull.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.IsNull.cs @@ -45,40 +45,87 @@ public void IsNull_Coalesce_SetsObjectConstraint() Tag(""NotNullToUnknown"", notNullToUnknown);"; var validator = SETestContext.CreateCS(code, ", object arg").Validator; validator.ValidateContainsOperation(OperationKind.IsNull); - validator.ValidateTag("NullToNull", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue()); - validator.ValidateTag("NullToNotNull", x => x.HasConstraint(ObjectConstraint.NotNull).Should().BeTrue()); - validator.ValidateTag("NullToUnknown", x => x.Should().BeNull()); - validator.ValidateTag("NotNullToNull", x => x.HasConstraint(ObjectConstraint.NotNull).Should().BeTrue()); - validator.ValidateTag("NotNullToNotNull", x => x.HasConstraint(ObjectConstraint.NotNull).Should().BeTrue()); - validator.ValidateTag("NotNullToUnknown", x => x.HasConstraint(ObjectConstraint.NotNull).Should().BeTrue()); + validator.ValidateTag("NullToNull", x => x.Should().HaveOnlyConstraint(ObjectConstraint.Null)); + validator.ValidateTag("NullToNotNull", x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull)); + validator.ValidateTag("NullToUnknown", x => x.Should().HaveNoConstraints()); + validator.ValidateTag("NotNullToNull", x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull)); + validator.ValidateTag("NotNullToNotNull", x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull)); + validator.ValidateTag("NotNullToUnknown", x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull)); } [TestMethod] public void IsNull_Coalesce_UnknownToNull() { - const string code = @" -object nullValue = null; -var unknownToNull = arg ?? nullValue; -Tag(""UnknownToNull"", unknownToNull);"; - var validator = SETestContext.CreateCS(code, ", object arg").Validator; + const string code = """ + object nullValue = null; + var result = arg ?? nullValue; + Tag("End"); + """; + var validator = SETestContext.CreateCS(code, ", object arg", new PreserveTestCheck("arg", "result")).Validator; + var arg = validator.Symbol("arg"); + var result = validator.Symbol("result"); validator.ValidateContainsOperation(OperationKind.IsNull); - validator.TagValues("UnknownToNull").Should().HaveCount(2) - .And.ContainSingle(x => x == null) - .And.ContainSingle(x => x != null && x.HasConstraint(ObjectConstraint.Null)); + validator.TagStates("End").Should().SatisfyRespectively( + x => + { + x[arg].Should().HaveOnlyConstraint(ObjectConstraint.Null); + x[result].Should().HaveOnlyConstraint(ObjectConstraint.Null); // It's from nullValue + }, + x => + { + x[arg].Should().HaveOnlyConstraint(ObjectConstraint.NotNull); + x[result].Should().HaveOnlyConstraint(ObjectConstraint.NotNull); + }); } [TestMethod] public void IsNull_Coalesce_UnknownToNotNull() { - const string code = @" -object notNullValue = new object(); -var unknownToNotNull = arg ?? notNullValue; -Tag(""UnknownToNotNull"", unknownToNotNull);"; - var validator = SETestContext.CreateCS(code, ", object arg").Validator; + const string code = """ + object notNullValue = new object(); + var result = arg ?? notNullValue; + Tag("End"); + """; + var validator = SETestContext.CreateCS(code, ", object arg", new PreserveTestCheck("arg", "result")).Validator; + var arg = validator.Symbol("arg"); + var result = validator.Symbol("result"); validator.ValidateContainsOperation(OperationKind.IsNull); - validator.TagValues("UnknownToNotNull").Should().HaveCount(2) - .And.ContainSingle(x => x == null) - .And.ContainSingle(x => x != null && x.HasConstraint(ObjectConstraint.NotNull)); + validator.TagStates("End").Should().SatisfyRespectively( + x => + { + x[arg].Should().HaveOnlyConstraint(ObjectConstraint.Null); + x[result].Should().HaveOnlyConstraint(ObjectConstraint.NotNull); + }, + x => + { + x[arg].Should().HaveOnlyConstraint(ObjectConstraint.NotNull); + x[result].Should().HaveOnlyConstraint(ObjectConstraint.NotNull); + }); + } + + [TestMethod] + public void IsNull_Coalesce_UnknownToNotNull_WithConversion() + { + const string code = """ + var notNullValue = new ArgumentException(); + var result = arg ?? notNullValue; + Tag("End"); + """; + var validator = SETestContext.CreateCS(code, ", Exception arg", new PreserveTestCheck("arg", "result")).Validator; + var arg = validator.Symbol("arg"); + var result = validator.Symbol("result"); + validator.ValidateContainsOperation(OperationKind.IsNull); + validator.TagStates("End").Should().SatisfyRespectively( + x => + { + x[arg].Should().HaveOnlyConstraint(ObjectConstraint.NotNull); + x[result].Should().HaveOnlyConstraint(ObjectConstraint.NotNull); + }, + x => + { + x[arg].Should().HaveOnlyConstraint(ObjectConstraint.Null); + x[result].Should().HaveOnlyConstraint(ObjectConstraint.NotNull); + }); } [TestMethod] @@ -89,8 +136,31 @@ public void IsNull_Coalesce_UnknownToUnknown() Tag(""UnknownToUnknown"", unknownToUnknown);"; var validator = SETestContext.CreateCS(code, ", object arg1, object arg2").Validator; validator.ValidateContainsOperation(OperationKind.IsNull); - validator.TagValues("UnknownToUnknown").Should().HaveCount(1) - .And.ContainSingle(x => x == null); + validator.TagValues("UnknownToUnknown").Should().SatisfyRespectively( + x => x.Should().HaveNoConstraints(), + x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull)); + } + + [TestMethod] + public void IsNull_Coalesce_AssignedToVariable() + { + const string code = """ + var value = arg ?? "N/A"; + Tag("Value", value); + """; + var validator = SETestContext.CreateCS(code, ", string arg").Validator; + validator.ValidateTag("Value", x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull)); + } + + [TestMethod] + public void IsNull_Coalesce_AssignedToSelf() + { + const string code = """ + arg = arg ?? "N/A"; + Tag("Arg", arg); + """; + var validator = SETestContext.CreateCS(code, ", string arg").Validator; + validator.ValidateTag("Arg", x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull)); } [TestMethod] @@ -120,12 +190,12 @@ public void IsNull_CoalesceAssignment_SetsObjectConstraint() "; var validator = SETestContext.CreateCS(code, ", object arg").Validator; validator.ValidateContainsOperation(OperationKind.IsNull); - validator.ValidateTag("NullToNull", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue()); - validator.ValidateTag("NullToNotNull", x => x.HasConstraint(ObjectConstraint.NotNull).Should().BeTrue()); - validator.ValidateTag("NullToUnknown", x => x.Should().BeNull()); - validator.ValidateTag("NotNullToNull", x => x.HasConstraint(ObjectConstraint.NotNull).Should().BeTrue()); - validator.ValidateTag("NotNullToNotNull", x => x.HasConstraint(ObjectConstraint.NotNull).Should().BeTrue()); - validator.ValidateTag("NotNullToUnknown", x => x.HasConstraint(ObjectConstraint.NotNull).Should().BeTrue()); + validator.ValidateTag("NullToNull", x => x.Should().HaveOnlyConstraint(ObjectConstraint.Null)); + validator.ValidateTag("NullToNotNull", x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull)); + validator.ValidateTag("NullToUnknown", x => x.Should().HaveNoConstraints()); + validator.ValidateTag("NotNullToNull", x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull)); + validator.ValidateTag("NotNullToNotNull", x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull)); + validator.ValidateTag("NotNullToUnknown", x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull)); } [TestMethod] @@ -137,9 +207,9 @@ public void IsNull_CoalesceAssignment_UnknownToNull() Tag(""Arg"", arg);"; var validator = SETestContext.CreateCS(code, ", object arg").Validator; validator.ValidateContainsOperation(OperationKind.IsNull); - validator.TagValues("Arg").Should().HaveCount(2) - .And.ContainSingle(x => x.HasConstraint(ObjectConstraint.NotNull)) - .And.ContainSingle(x => x.HasConstraint(ObjectConstraint.Null)); + validator.TagValues("Arg").Should().SatisfyRespectively( + x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull), + x => x.Should().HaveOnlyConstraint(ObjectConstraint.Null)); } [TestMethod] @@ -151,8 +221,9 @@ public void IsNull_CoalesceAssignment_UnknownToNotNull() Tag(""Arg"", arg);"; var validator = SETestContext.CreateCS(code, ", object arg").Validator; validator.ValidateContainsOperation(OperationKind.IsNull); - validator.TagValues("Arg").Should().HaveCount(2) - .And.OnlyContain(x => x.HasConstraint(ObjectConstraint.NotNull)); + validator.TagValues("Arg").Should().SatisfyRespectively( + x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull), // This should be here just once, one state persists notNullValue without LVA removing it + x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull)); } [TestMethod] @@ -163,9 +234,9 @@ public void IsNull_CoalesceAssignment_UnknownToUnknown() Tag(""Arg"", arg1);"; var validator = SETestContext.CreateCS(code, ", object arg1, object arg2").Validator; validator.ValidateContainsOperation(OperationKind.IsNull); - validator.TagValues("Arg").Should().HaveCount(2) - .And.ContainSingle(x => x == null) - .And.ContainSingle(x => x != null && x.HasConstraint(ObjectConstraint.NotNull)); + validator.TagValues("Arg").Should().SatisfyRespectively( + x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull), + x => x.Should().HaveNoConstraints()); } [TestMethod] diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Loops.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Loops.cs index 263fb3ef3a1..f3d55d251ba 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Loops.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Loops.cs @@ -64,11 +64,12 @@ public void Loops_InstructionVisitedMaxTwice_ForEach() // - IEnumerable<>.GetEnumerator() // - System.Collections.IEnumerator.MoveNext() // - System.IDisposable.Dispose() - validator.ValidateExitReachCount(6); // foreach produces implicit TryFinally region where it can throw and changes the flow - validator.TagValues("End").Should().HaveCount(2) // These Exception flows do not reach the Tag("End") line - .And.SatisfyRespectively( - x => x.AllConstraints.Should().BeEquivalentTo(new SymbolicConstraint[] { ObjectConstraint.NotNull }), - x => x.AllConstraints.Should().BeEquivalentTo(new SymbolicConstraint[] { ObjectConstraint.NotNull, TestConstraint.First })); + validator.ValidateExitReachCount(12); // foreach produces implicit TryFinally region where it can throw - these flows do not reach the Tag("End") line + validator.TagValues("End").Should().SatisfyRespectively( + x => x.Should().HaveOnlyConstraints(ObjectConstraint.NotNull), // items with Null flow generated by implicty Finally block that has items?.Dispose() + x => x.Should().HaveOnlyConstraints(ObjectConstraint.NotNull), // items with NotNull flow + x => x.Should().HaveOnlyConstraints(ObjectConstraint.NotNull, TestConstraint.First), + x => x.Should().HaveOnlyConstraints(ObjectConstraint.NotNull, TestConstraint.First)); } [TestMethod] diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Roslyn/NullPointerDereference.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Roslyn/NullPointerDereference.cs index c5590c8f5b3..686abaf4758 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Roslyn/NullPointerDereference.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Roslyn/NullPointerDereference.cs @@ -1313,6 +1313,24 @@ public void WithObject(string first, object second) public class Akka_Repro { + public void NestedForEachTryForEach(IEnumerable exceptions) + { + foreach (var ex in exceptions) + { + try + { + foreach (var ch in ex.Message) // Noncompliant FP + { + // Do something + } + } + catch + { + // Do something + } + } + } + public void IsType(object message) { var first = message as IFirst; diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Roslyn/PublicMethodArgumentsShouldBeCheckedForNull.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Roslyn/PublicMethodArgumentsShouldBeCheckedForNull.cs index 81228dd8c94..d6b3fc100f8 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Roslyn/PublicMethodArgumentsShouldBeCheckedForNull.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Roslyn/PublicMethodArgumentsShouldBeCheckedForNull.cs @@ -47,7 +47,7 @@ public void CompliantCases(bool b, object o1, object o2, object o3, object o4, E } o2 = o2 ?? new object(); - o2.ToString(); // Noncompliant - FP + o2.ToString(); // Compliant if (o3 == null) { @@ -769,3 +769,14 @@ public void UsedTwiceOnSameLevel(string s) s.IndexOf("b")); // Compliant - IndexOf("a") was called before this method call, so s is not null here } } + +public class Nancy_Repro +{ + public void NullCoalesce(Sample arg = null) + { + arg = arg ?? new Sample(); + arg.ToString(); // Compliant + } + + public class Sample { } +}