From b0e8edb25368cb3e345fbdbe722dda2591e96020 Mon Sep 17 00:00:00 2001 From: Pavel Mikula Date: Mon, 20 Mar 2023 12:13:40 +0100 Subject: [PATCH 1/4] SE - Nullable: Add UTs for Debug.Assert --- .../RoslynSymbolicExecutionTest.Invocation.cs | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs index 7a158d2caf5..7ceaeb25a75 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs @@ -750,11 +750,17 @@ public void Invocation_InformationIsNothing_NoTrackedSymbol() } [DataTestMethod] - [DataRow("arg != null")] - [DataRow("arg is not null")] - [DataRow("arg is { }")] - public void Invocation_DebugAssert_LearnsNotNull_Simple(string expression) => - DebugAssertValues(expression).Should().HaveCount(1).And.ContainSingle(x => x.HasConstraint(ObjectConstraint.NotNull)); + [DataRow("arg != null", "object")] + [DataRow("arg != null", "int?")] + [DataRow("arg != null", "bool?")] + [DataRow("arg is not null", "object")] + [DataRow("arg is not null", "int?")] + [DataRow("arg is not null", "bool?")] + [DataRow("arg is { }", "object")] + [DataRow("arg is { }", "int?")] + [DataRow("arg is { }", "bool?")] + public void Invocation_DebugAssert_LearnsNotNull_Simple(string expression, string argType) => + DebugAssertValues(expression, argType).Should().SatisfyRespectively(x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull)); [TestMethod] public void Invocation_DebugAssert_LearnsNotNull_AndAlso() => @@ -823,6 +829,18 @@ public static void Assert() { } new SETestContext(code, AnalyzerLanguage.CSharp, Array.Empty()).Validator.ValidateTagOrder("End"); } + [DataTestMethod] + [DataRow("int?")] + [DataRow("bool?")] + public void Invocation_DebugAssert_NullableHasValue_Simple(string argType) => + DebugAssertValues("arg.HasValue", argType).Should().SatisfyRespectively(x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull)); + + [DataTestMethod] + [DataRow("int?")] + [DataRow("bool?")] + public void Invocation_DebugAssert_NullableHasValue_Binary(string argType) => + DebugAssertValues("arg.HasValue == true", argType).Should().SatisfyRespectively(x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull)); + private static SymbolicValue[] DebugAssertValues(string expression, string argType = "object") { var code = $@" From 086a8287b1e243a3513704e387712b3cf2e7c5c5 Mon Sep 17 00:00:00 2001 From: Pavel Mikula Date: Tue, 21 Mar 2023 13:44:25 +0100 Subject: [PATCH 2/4] Review update 1 --- .../Roslyn/OperationProcessors/Invocation.cs | 2 +- .../RoslynSymbolicExecutionTest.Invocation.cs | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/OperationProcessors/Invocation.cs b/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/OperationProcessors/Invocation.cs index ae80f56a061..39c0b8e01c6 100644 --- a/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/OperationProcessors/Invocation.cs +++ b/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/OperationProcessors/Invocation.cs @@ -165,7 +165,7 @@ private static ProgramState ProcessAssertedBoolSymbol(ProgramState state, IOpera else { return operation.TrackedSymbol() is { } symbol - ? state.SetSymbolConstraint(symbol, BoolConstraint.From(!isNegated)) + ? state.SetSymbolConstraint(symbol, BoolConstraint.From(!isNegated)).SetSymbolConstraint(symbol, ObjectConstraint.NotNull) : state; } } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs index 7ceaeb25a75..a193e208de9 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs @@ -749,6 +749,14 @@ public void Invocation_InformationIsNothing_NoTrackedSymbol() validator.ValidateTag("Result", x => x.AllConstraints.Should().ContainSingle().Which.Should().Be(ObjectConstraint.NotNull)); } + [DataTestMethod] + [DataRow("arg is not true", "bool?")] + [DataRow("arg is not false", "bool?")] + [DataRow("arg is not 42", "object")] + [DataRow("arg is not { Length: 0 }", "string")] + public void Invocation_DebugAssert_DoesNotLearn(string expression, string argType) => + DebugAssertValues(expression, argType).Should().SatisfyRespectively(x => x.Should().HaveNoConstraints()); + [DataTestMethod] [DataRow("arg != null", "object")] [DataRow("arg != null", "int?")] @@ -792,6 +800,12 @@ public void Invocation_DebugAssert_LearnsBoolConstraint_Simple() => public void Invocation_DebugAssert_LearnsBoolConstraint_Binary() => DebugAssertValues("arg == true", "bool").Should().SatisfyRespectively(x => x.Should().HaveOnlyConstraints(ObjectConstraint.NotNull, BoolConstraint.True)); + [DataTestMethod] + [DataRow("arg is true", true)] + [DataRow("arg is false", false)] + public void Invocation_DebugAssert_LearnsBoolConstraint_Nullable(string expression, bool expected) => + DebugAssertValues(expression, "bool?").Should().SatisfyRespectively(x => x.Should().HaveOnlyConstraints(BoolConstraint.From(expected))); + [TestMethod] public void Invocation_DebugAssert_LearnsBoolConstraint_AlwaysEnds() => DebugAssertValues("false", "bool").Should().BeEmpty(); From 2b2f3518469f7189de1644060a5f52e6cdb0a977 Mon Sep 17 00:00:00 2001 From: Pavel Mikula Date: Tue, 21 Mar 2023 15:17:20 +0100 Subject: [PATCH 3/4] Review update: Add GetValueOrDefault --- .../Roslyn/RoslynSymbolicExecutionTest.Invocation.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs index a193e208de9..74357b08c9b 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs @@ -754,6 +754,7 @@ public void Invocation_InformationIsNothing_NoTrackedSymbol() [DataRow("arg is not false", "bool?")] [DataRow("arg is not 42", "object")] [DataRow("arg is not { Length: 0 }", "string")] + [DataRow("arg.GetValueOrDefault()", "bool?")] public void Invocation_DebugAssert_DoesNotLearn(string expression, string argType) => DebugAssertValues(expression, argType).Should().SatisfyRespectively(x => x.Should().HaveNoConstraints()); From 4818ec6f1fa20c58a95c60bdcd351e1928bcfeb4 Mon Sep 17 00:00:00 2001 From: Pavel Mikula Date: Fri, 24 Mar 2023 15:17:39 +0100 Subject: [PATCH 4/4] Review update: Rewrite assertions --- .../RoslynSymbolicExecutionTest.Invocation.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs index 74357b08c9b..545948391c4 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs @@ -773,7 +773,7 @@ public void Invocation_DebugAssert_LearnsNotNull_Simple(string expression, strin [TestMethod] public void Invocation_DebugAssert_LearnsNotNull_AndAlso() => - DebugAssertValues("arg != null && condition").Should().HaveCount(1).And.ContainSingle(x => x.HasConstraint(ObjectConstraint.NotNull)); + DebugAssertValues("arg != null && condition").Should().SatisfyRespectively(x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull)); [TestMethod] public void Invocation_DebugAssert_LearnsNotNullForAll_AndAlso() @@ -783,19 +783,19 @@ public void Invocation_DebugAssert_LearnsNotNullForAll_AndAlso() Tag(""Arg1"", arg1); Tag(""Arg2"", arg2);"; var validator = SETestContext.CreateCS(code, $", object arg1, object arg2").Validator; - validator.ValidateTag("Arg1", x => x.HasConstraint(ObjectConstraint.NotNull).Should().BeTrue()); - validator.ValidateTag("Arg2", x => x.HasConstraint(ObjectConstraint.NotNull).Should().BeTrue()); + validator.ValidateTag("Arg1", x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull)); + validator.ValidateTag("Arg2", x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull)); } [TestMethod] public void Invocation_DebugAssert_LearnsNotNull_OrElse() => - DebugAssertValues("arg != null || condition").Should().HaveCount(2) - .And.ContainSingle(x => x != null && x.HasConstraint(ObjectConstraint.Null)) - .And.ContainSingle(x => x != null && x.HasConstraint(ObjectConstraint.NotNull)); + DebugAssertValues("arg != null || condition").Should().SatisfyRespectively( + x => x.Should().HaveOnlyConstraint(ObjectConstraint.NotNull), + x => x.Should().HaveOnlyConstraint(ObjectConstraint.Null)); [TestMethod] public void Invocation_DebugAssert_LearnsBoolConstraint_Simple() => - DebugAssertValues("arg", "bool").Should().HaveCount(1).And.ContainSingle(x => x.HasConstraint(BoolConstraint.True)); + DebugAssertValues("arg", "bool").Should().SatisfyRespectively(x => x.Should().HaveOnlyConstraints(BoolConstraint.True, ObjectConstraint.NotNull)); [TestMethod] public void Invocation_DebugAssert_LearnsBoolConstraint_Binary() => @@ -815,7 +815,7 @@ public void Invocation_DebugAssert_LearnsBoolConstraint_AlwaysEnds() => [DataRow("!arg")] [DataRow("!!!arg")] public void Invocation_DebugAssert_LearnsBoolConstraint_Negated(string expression) => - DebugAssertValues(expression, "bool").Should().HaveCount(1).And.ContainSingle(x => x.HasConstraint(BoolConstraint.False)); + DebugAssertValues(expression, "bool").Should().SatisfyRespectively(x => x.Should().HaveOnlyConstraints(BoolConstraint.False, ObjectConstraint.NotNull)); [TestMethod] public void Invocation_DebugAssert_CustomNoParameters_DoesNotFail()