From 6dceab44988421cbbd095aff4c090fcc04a1b83a Mon Sep 17 00:00:00 2001 From: Tim Pohlmann Date: Fri, 16 Jun 2023 15:31:24 +0200 Subject: [PATCH] Migrate S4158: Passing as argument removes constraint (#7433) --- .../Automapper/AutoMapper--net461-S4158.json | 30 ----------- .../AutoMapper--netstandard2.0-S4158.json | 30 ----------- .../Roslyn/Extensions/IOperationExtensions.cs | 3 ++ ...ptyCollectionsShouldNotBeEnumeratedBase.cs | 6 +++ .../SymbolicExecution/Roslyn/SymbolicValue.cs | 2 +- .../Roslyn/ProgramStateTest.cs | 9 +++- .../RoslynSymbolicExecutionTest.Invocation.cs | 50 +++++++++---------- ...mbolicExecutionTest.InvocationAttribute.cs | 38 +++++++------- .../Roslyn/SymbolicValueTest.cs | 6 +-- .../EmptyCollectionsShouldNotBeEnumerated.cs | 34 +++++++++---- .../EmptyCollectionsShouldNotBeEnumerated.vb | 9 ++-- 11 files changed, 94 insertions(+), 123 deletions(-) delete mode 100644 analyzers/its/expected/Automapper/AutoMapper--net461-S4158.json delete mode 100644 analyzers/its/expected/Automapper/AutoMapper--netstandard2.0-S4158.json diff --git a/analyzers/its/expected/Automapper/AutoMapper--net461-S4158.json b/analyzers/its/expected/Automapper/AutoMapper--net461-S4158.json deleted file mode 100644 index 01574597d94..00000000000 --- a/analyzers/its/expected/Automapper/AutoMapper--net461-S4158.json +++ /dev/null @@ -1,30 +0,0 @@ -{ -"issues": [ -{ -"id": "S4158", -"message": "Remove this call, the collection is known to be empty here.", -"location": { -"uri": "sources\Automapper\src\AutoMapper\Configuration\MappingExpressionBase.cs", -"region": { -"startLine": 174, -"startColumn": 21, -"endLine": 174, -"endColumn": 42 -} -} -}, -{ -"id": "S4158", -"message": "Remove this call, the collection is known to be empty here.", -"location": { -"uri": "sources\Automapper\src\AutoMapper\TypeMap.cs", -"region": { -"startLine": 44, -"startColumn": 17, -"endLine": 44, -"endColumn": 38 -} -} -} -] -} diff --git a/analyzers/its/expected/Automapper/AutoMapper--netstandard2.0-S4158.json b/analyzers/its/expected/Automapper/AutoMapper--netstandard2.0-S4158.json deleted file mode 100644 index 01574597d94..00000000000 --- a/analyzers/its/expected/Automapper/AutoMapper--netstandard2.0-S4158.json +++ /dev/null @@ -1,30 +0,0 @@ -{ -"issues": [ -{ -"id": "S4158", -"message": "Remove this call, the collection is known to be empty here.", -"location": { -"uri": "sources\Automapper\src\AutoMapper\Configuration\MappingExpressionBase.cs", -"region": { -"startLine": 174, -"startColumn": 21, -"endLine": 174, -"endColumn": 42 -} -} -}, -{ -"id": "S4158", -"message": "Remove this call, the collection is known to be empty here.", -"location": { -"uri": "sources\Automapper\src\AutoMapper\TypeMap.cs", -"region": { -"startLine": 44, -"startColumn": 17, -"endLine": 44, -"endColumn": 38 -} -} -} -] -} diff --git a/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/Extensions/IOperationExtensions.cs b/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/Extensions/IOperationExtensions.cs index da9df770381..dfad7843286 100644 --- a/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/Extensions/IOperationExtensions.cs +++ b/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/Extensions/IOperationExtensions.cs @@ -43,6 +43,9 @@ static bool IsTryDownCast(IConversionOperationWrapper conversion) => conversion.IsTryCast && !conversion.Operand.Type.DerivesOrImplements(conversion.Type); } + internal static IArgumentOperationWrapper? AsArgument(this IOperation operation) => + operation.As(OperationKindEx.Argument, IArgumentOperationWrapper.FromOperation); + internal static IAssignmentOperationWrapper? AsAssignment(this IOperation operation) => operation.As(OperationKindEx.SimpleAssignment, IAssignmentOperationWrapper.FromOperation); diff --git a/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/RuleChecks/EmptyCollectionsShouldNotBeEnumeratedBase.cs b/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/RuleChecks/EmptyCollectionsShouldNotBeEnumeratedBase.cs index c0a6e62d146..4e2d6ac9ecc 100644 --- a/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/RuleChecks/EmptyCollectionsShouldNotBeEnumeratedBase.cs +++ b/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/RuleChecks/EmptyCollectionsShouldNotBeEnumeratedBase.cs @@ -141,6 +141,12 @@ protected override ProgramState PreProcessSimple(SymbolicContext context) { return context.State.SetOperationConstraint(operation, constraint); } + else if (operation.AsArgument() is { } argument + && context.State.ResolveCaptureAndUnwrapConversion(argument.Value).TrackedSymbol() is { } symbol + && context.State[symbol] is { } symbolValue) + { + return context.State.SetSymbolValue(symbol, symbolValue.WithoutConstraint()); + } else { return context.State; diff --git a/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/SymbolicValue.cs b/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/SymbolicValue.cs index cc654f03f14..b96fbbb4ad8 100644 --- a/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/SymbolicValue.cs +++ b/analyzers/src/SonarAnalyzer.Common/SymbolicExecution/Roslyn/SymbolicValue.cs @@ -108,7 +108,7 @@ private string SerializeConstraints() => private SymbolicValue RemoveConstraint(Type type) => Constraints.Count switch { - 1 => Empty, + 1 => null, 2 => OtherSingle(type), 3 => OtherPair(type), _ => this with { Constraints = Constraints.Remove(type) }, diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/ProgramStateTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/ProgramStateTest.cs index c61de56904c..c52092393b5 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/ProgramStateTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/ProgramStateTest.cs @@ -360,7 +360,14 @@ private static void ResetFieldConstraintTests(SymbolicConstraint constraint, boo symbolValue.HasConstraint(constraint).Should().BeTrue(); sut = sut.ResetFieldConstraints(); symbolValue = sut[field]; - symbolValue.HasConstraint(constraint).Should().Be(expectIsPreserved); + if (expectIsPreserved) + { + symbolValue.HasConstraint(constraint).Should().BeTrue(); + } + else + { + symbolValue.Should().BeNull(); + } } private static ISymbol[] CreateSymbols() diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs index 86593a39987..a407a201fd2 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.Invocation.cs @@ -191,10 +191,10 @@ public static void Tag(string name, object arg) {{ }} }}"; var validator = new SETestContext(code, AnalyzerLanguage.CSharp, Array.Empty()).Validator; validator.ValidateContainsOperation(OperationKind.Invocation); - validator.ValidateTag("BeforeField", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue()); - validator.ValidateTag("BeforeStaticField", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue()); - validator.ValidateTag("AfterField", x => x.Constraint().Should().BeNull()); - validator.ValidateTag("AfterStaticField", x => x.Constraint().Should().BeNull()); + validator.TagValue("BeforeField").HasConstraint(ObjectConstraint.Null).Should().BeTrue(); + validator.TagValue("BeforeStaticField").HasConstraint(ObjectConstraint.Null).Should().BeTrue(); + validator.TagValue("AfterField").Should().BeNull(); + validator.TagValue("AfterStaticField").Should().BeNull(); } [DataTestMethod] @@ -277,8 +277,8 @@ public static void Tag(string name) {{ }} x => // Branch for "this" { x[condition].Should().BeNull(); // Should have BoolConstraint.True - x[field].AllConstraints.Should().BeEmpty(); - x[staticField].AllConstraints.Should().BeEmpty(); + x[field].Should().BeNull(); + x[staticField].Should().BeNull(); }, x => // Branch for "otherInstance" { @@ -387,7 +387,7 @@ public void Instance_InstanceMethodCall_ClearsField() var validator = SETestContext.CreateCS(code).Validator; validator.TagValues("After").Should().Equal( SymbolicValue.Empty.WithConstraint(ObjectConstraint.NotNull), - SymbolicValue.Empty); + null); } [TestMethod] @@ -398,7 +398,7 @@ public void Instance_InstanceMethodCall_ClearsFieldWithBranchInArgument() this.InstanceMethod(boolParameter ? 1 : 0); Tag(""After"", this.ObjectField);"; var validator = SETestContext.CreateCS(code).Validator; - validator.ValidateTag("After", x => x.AllConstraints.Should().BeEmpty()); + validator.TagValue("After").Should().BeNull(); } [TestMethod] @@ -465,28 +465,28 @@ public class Other public static void OtherMethod() { } }"; var validator = new SETestContext(code, AnalyzerLanguage.CSharp, Array.Empty()).Validator; - validator.ValidateTag("Start_Base_BaseField", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue()); - validator.ValidateTag("Start_Other_OtherField", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue()); - validator.ValidateTag("Start_Sample_SampleField1", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue()); - validator.ValidateTag("Start_Sample_SampleField2", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue()); + validator.TagValue("Start_Base_BaseField").HasConstraint(ObjectConstraint.Null).Should().BeTrue(); + validator.TagValue("Start_Other_OtherField").HasConstraint(ObjectConstraint.Null).Should().BeTrue(); + validator.TagValue("Start_Sample_SampleField1").HasConstraint(ObjectConstraint.Null).Should().BeTrue(); + validator.TagValue("Start_Sample_SampleField2").HasConstraint(ObjectConstraint.Null).Should().BeTrue(); // SampleMethod() resets own field and base class fields, but not other class fields - validator.ValidateTag("SampleMethod_Base_BaseField", x => x.HasConstraint(ObjectConstraint.Null).Should().BeFalse()); - validator.ValidateTag("SampleMethod_Other_OtherField", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue()); - validator.ValidateTag("SampleMethod_Sample_SampleField1", x => x.HasConstraint(ObjectConstraint.Null).Should().BeFalse()); - validator.ValidateTag("SampleMethod_Sample_SampleField2", x => x.HasConstraint(ObjectConstraint.Null).Should().BeFalse()); + validator.TagValue("SampleMethod_Base_BaseField").Should().BeNull(); + validator.TagValue("SampleMethod_Other_OtherField").HasConstraint(ObjectConstraint.Null).Should().BeTrue(); + validator.TagValue("SampleMethod_Sample_SampleField1").Should().BeNull(); + validator.TagValue("SampleMethod_Sample_SampleField2").Should().BeNull(); // OtherMethod() resets only its own constraints - validator.ValidateTag("OtherMethod_Base_BaseField", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue()); - validator.ValidateTag("OtherMethod_Other_OtherField", x => x.HasConstraint(ObjectConstraint.Null).Should().BeFalse()); - validator.ValidateTag("OtherMethod_Sample_SampleField1", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue()); - validator.ValidateTag("OtherMethod_Sample_SampleField2", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue()); + validator.TagValue("OtherMethod_Base_BaseField").HasConstraint(ObjectConstraint.Null).Should().BeTrue(); + validator.TagValue("OtherMethod_Other_OtherField").Should().BeNull(); + validator.TagValue("OtherMethod_Sample_SampleField1").HasConstraint(ObjectConstraint.Null).Should().BeTrue(); + validator.TagValue("OtherMethod_Sample_SampleField2").HasConstraint(ObjectConstraint.Null).Should().BeTrue(); // BaseMethod() called from Sample only resets Base field - validator.ValidateTag("BaseMethod_Base_BaseField", x => x.HasConstraint(ObjectConstraint.Null).Should().BeFalse()); - validator.ValidateTag("BaseMethod_Other_OtherField", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue()); - validator.ValidateTag("BaseMethod_Sample_SampleField1", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue()); - validator.ValidateTag("BaseMethod_Sample_SampleField2", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue()); + validator.TagValue("BaseMethod_Base_BaseField").Should().BeNull(); + validator.TagValue("BaseMethod_Other_OtherField").HasConstraint(ObjectConstraint.Null).Should().BeTrue(); + validator.TagValue("BaseMethod_Sample_SampleField1").HasConstraint(ObjectConstraint.Null).Should().BeTrue(); + validator.TagValue("BaseMethod_Sample_SampleField2").HasConstraint(ObjectConstraint.Null).Should().BeTrue(); } [DataTestMethod] @@ -507,7 +507,7 @@ public void Invocation_StaticMethodCall_DoesNotClearInstanceFields(string invoca validator.ValidateTag("BeforeField", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue()); validator.ValidateTag("BeforeStaticField", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue()); validator.ValidateTag("AfterField", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue()); - validator.ValidateTag("AfterStaticField", x => x.HasConstraint(ObjectConstraint.Null).Should().BeFalse()); + validator.ValidateTag("AfterStaticField", x => x.Should().BeNull()); } [TestMethod] diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.InvocationAttribute.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.InvocationAttribute.cs index 1d644269f62..ab7800726b4 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.InvocationAttribute.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/RoslynSymbolicExecutionTest.InvocationAttribute.cs @@ -185,32 +185,32 @@ public void Invocation_NotNullWhen_Unknown() [TestMethod] public void Invocation_NotNullWhen_Unknown_InstanceMethodResetsFieldConstraints() { - const string code = @" -private object ObjectField; - -public void Test() -{ -this.ObjectField = null; -string byteString = Unknown(); -var success = TryParse(byteString, out var result); -Tag(""End"", null); -} -public bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string s, out object o) { o = null; return true; }"; + const string code = """ + private object ObjectField; + public void Test() + { + this.ObjectField = null; + string byteString = Unknown(); + var success = TryParse(byteString, out var result); + Tag("End", null); + } + public bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string s, out object o) { o = null; return true; } + """; var validator = SETestContext.CreateCSMethod(code).Validator; validator.TagStates("End").Should().SatisfyRespectively( x => { - x[validator.Symbol("ObjectField")].HasConstraint().Should().BeFalse(); - x[validator.Symbol("byteString")].HasConstraint(ObjectConstraint.NotNull).Should().BeTrue(); - x[validator.Symbol("success")].HasConstraint(BoolConstraint.True).Should().BeTrue(); - x[validator.Symbol("result")].Should().BeNull(); + x[validator.Symbol("ObjectField")].Should().HaveNoConstraints(); + x[validator.Symbol("byteString")].Should().HaveOnlyConstraint(ObjectConstraint.NotNull); + x[validator.Symbol("success")].Should().HaveOnlyConstraints(ObjectConstraint.NotNull, BoolConstraint.True); + x[validator.Symbol("result")].Should().HaveNoConstraints(); }, x => { - x[validator.Symbol("ObjectField")].HasConstraint().Should().BeFalse(); - x[validator.Symbol("byteString")].Should().BeNull(); - x[validator.Symbol("success")].HasConstraint(BoolConstraint.False).Should().BeTrue(); - x[validator.Symbol("result")].Should().BeNull(); + x[validator.Symbol("ObjectField")].Should().HaveNoConstraints(); + x[validator.Symbol("byteString")].Should().HaveNoConstraints(); + x[validator.Symbol("success")].Should().HaveOnlyConstraints(ObjectConstraint.NotNull, BoolConstraint.False); + x[validator.Symbol("result")].Should().HaveNoConstraints(); }); } diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/SymbolicValueTest.cs b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/SymbolicValueTest.cs index ee0a0f51d8b..e7e5f3a55f3 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/SymbolicValueTest.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/SymbolicExecution/Roslyn/SymbolicValueTest.cs @@ -78,7 +78,7 @@ public void WithoutConstraint_RemovesOnlyTheSame() .WithoutConstraint(TestConstraint.Second); // Do nothing sut.HasConstraint(TestConstraint.First).Should().BeTrue(); sut = sut.WithoutConstraint(TestConstraint.First); - sut.HasConstraint(TestConstraint.First).Should().BeFalse(); + sut.Should().BeNull(); } [TestMethod] @@ -242,14 +242,14 @@ public void TripletCache_ReplaceExistingConstraintType() public void SingleCache_RemoveLastEntry_Kind() { var sut = SymbolicValue.Null; - sut.WithoutConstraint(ObjectConstraint.Null).Should().BeSameAs(SymbolicValue.Empty); + sut.WithoutConstraint(ObjectConstraint.Null).Should().BeNull(); } [TestMethod] public void SingleCache_RemoveLastEntry_Type() { var sut = SymbolicValue.Null; - sut.WithoutConstraint().Should().BeSameAs(SymbolicValue.Empty); + sut.WithoutConstraint().Should().BeNull(); } [TestMethod] diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Roslyn/EmptyCollectionsShouldNotBeEnumerated.cs b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Roslyn/EmptyCollectionsShouldNotBeEnumerated.cs index ae713de7ec9..7d481b52b12 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Roslyn/EmptyCollectionsShouldNotBeEnumerated.cs +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Roslyn/EmptyCollectionsShouldNotBeEnumerated.cs @@ -105,15 +105,19 @@ public void ConstructorWithEnumerableWithConstraint(bool condition) var set = new HashSet(baseCollection); set.Clear(); // Noncompliant + baseCollection = new List(); set = new HashSet(baseCollection, EqualityComparer.Default); set.Clear(); // Noncompliant + baseCollection = new List(); set = new HashSet(comparer: EqualityComparer.Default, collection: baseCollection); set.Clear(); // Noncompliant + baseCollection = new List(); set = new HashSet(condition ? baseCollection : baseCollection); set.Clear(); // Noncompliant + baseCollection = new List(); baseCollection.Add(1); set = new HashSet(baseCollection); set.Clear(); // Compliant @@ -438,7 +442,7 @@ public void UnknownExtensionMethods() { var list = new List(); list.CustomExtensionMethod(); // Compliant - list.Clear(); // Noncompliant FP + list.Clear(); // Compliant } public void WellKnownExtensionMethods() @@ -489,14 +493,22 @@ public void WellKnownExtensionMethods() list.Where(x => true); // FN list.Zip(list, (x, y) => x); // FN Enumerable.Reverse(list); // FN - list.Clear(); // Noncompliant + list.Clear(); // FN, should raise, because the methods above should not reset the state } - public void PassingAsArgument_Removes_Constraints() + public void PassingAsArgument_Removes_Constraints(bool condition) { var list = new List(); Foo(list); - list.Clear(); // Noncompliant FP + list.Clear(); // Compliant + + list = new List(); + Foo(condition ? list : null); + list.Clear(); // Compliant + + list = new List(); + Foo((condition ? list : null) as List); + list.Clear(); // Compliant } public void HigherRank_And_Jagged_Array() @@ -558,7 +570,7 @@ public void LearnConditions_Size(bool condition, List arg) if (empty.Count() == 0) { - empty.Clear(); // Noncompliant + empty.Clear(); // FN } else { @@ -567,7 +579,7 @@ public void LearnConditions_Size(bool condition, List arg) if (empty.Count(x => condition) == 0) { - empty.Clear(); // Noncompliant + empty.Clear(); // FN } else { @@ -576,16 +588,16 @@ public void LearnConditions_Size(bool condition, List arg) if (notEmpty.Count(x => condition) == 0) { - empty.Clear(); // Noncompliant + empty.Clear(); // FN } else { - empty.Clear(); // Noncompliant + empty.Clear(); // FN } if (Enumerable.Count(empty) == 0) { - empty.Clear(); // Noncompliant + empty.Clear(); // FN } else { @@ -621,7 +633,7 @@ public void LearnConditions_Size_Array(bool condition) if (empty.Count() == 0) { - empty.Clone(); // Noncompliant + empty.Clone(); // FN } else { @@ -631,7 +643,7 @@ public void LearnConditions_Size_Array(bool condition) notEmpty.Clone(); // Compliant, prevents LVA from throwing notEmpty away during reference capture } - void Foo(IEnumerable items) { } + void Foo(List items) { } } static class CustomExtensions diff --git a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Roslyn/EmptyCollectionsShouldNotBeEnumerated.vb b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Roslyn/EmptyCollectionsShouldNotBeEnumerated.vb index 606760c19ba..1fd48029eb6 100644 --- a/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Roslyn/EmptyCollectionsShouldNotBeEnumerated.vb +++ b/analyzers/tests/SonarAnalyzer.UnitTest/TestCases/SymbolicExecution/Roslyn/EmptyCollectionsShouldNotBeEnumerated.vb @@ -63,12 +63,15 @@ Public Class CollectionTests Dim HS As New HashSet(Of Integer)(BaseCollection) HS.Clear() ' Noncompliant + BaseCollection = New List(Of Integer) HS = New HashSet(Of Integer)(BaseCollection, EqualityComparer(Of Integer).Default) HS.Clear() ' Noncompliant + BaseCollection = New List(Of Integer) HS = New HashSet(Of Integer)(comparer:=EqualityComparer(Of Integer).Default, collection:=BaseCollection) HS.Clear() ' Noncompliant + BaseCollection = New List(Of Integer) HS = New HashSet(Of Integer)(If(Condition, BaseCollection, BaseCollection)) HS.Clear() ' Noncompliant @@ -230,7 +233,7 @@ Public Class CollectionTests Empty.Clear() ' Noncompliant End If If Enumerable.Count(Empty) = 0 Then - Empty.Clear() ' Noncompliant + Empty.Clear() ' FN Else Empty.Clear() ' Compliant, unreachable End If @@ -262,13 +265,13 @@ Public Class AdvancedTests List.All(Function(X) True) ' FN List.Any() ' FN Enumerable.Reverse(List) ' FN - List.Clear() ' Noncompliant + List.Clear() ' FN, should raise, because the methods above should not reset the state End Sub Public Sub PassingAsArgument_Removes_Constraints() Dim List As New List(Of Integer) Foo(List) - List.Clear() ' Noncompliant FP + List.Clear() ' Compliant End Sub Public Sub HigherRank_And_Jagged_Array()