Skip to content

Commit

Permalink
SE: Move CollectionConstraint from S4158 to the engine (part 5)
Browse files Browse the repository at this point in the history
  • Loading branch information
gregory-paidis-sonarsource committed Feb 9, 2024
1 parent b5ac206 commit 0c15e9a
Show file tree
Hide file tree
Showing 4 changed files with 60 additions and 32 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ internal static class OperationDispatcher
{ OperationKindEx.Increment, new IncrementOrDecrement() },
{ OperationKindEx.InstanceReference, new InstanceReference() },
{ OperationKindEx.LocalReference, new LocalReference() },
{ OperationKindEx.MethodReference, new MethodReference() },
{ OperationKindEx.ObjectCreation, new ObjectCreation() },
{ OperationKindEx.ParameterReference, new ParameterReference() },
{ OperationKindEx.RecursivePattern, new RecursivePattern() },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using System.Collections.ObjectModel;
using SonarAnalyzer.SymbolicExecution.Constraints;

namespace SonarAnalyzer.SymbolicExecution.Roslyn.OperationProcessors;
Expand All @@ -43,6 +44,21 @@ internal static class CollectionTracker
KnownType.System_Collections_Generic_IDictionary_TKey_TValue,
KnownType.System_Collections_Immutable_IImmutableDictionary_TKey_TValue);

public static readonly HashSet<string> AddMethods = new()
{
nameof(ICollection<int>.Add),
nameof(List<int>.AddRange),
nameof(List<int>.Insert),
nameof(List<int>.InsertRange),
nameof(HashSet<int>.UnionWith),
nameof(HashSet<int>.SymmetricExceptWith), // This can add and/or remove items => It should remove all CollectionConstraints.
// However, just learning NotEmpty (and thus unlearning Empty) is good enough for now.
nameof(Queue<int>.Enqueue),
nameof(Stack<int>.Push),
nameof(Collection<int>.Insert),
"TryAdd"
};

public static CollectionConstraint ObjectCreationConstraint(ProgramState state, IObjectCreationOperationWrapper operation)
{
if (operation.Type.IsAny(CollectionTypes))
Expand All @@ -65,6 +81,11 @@ public static CollectionConstraint ArrayCreationConstraint(IArrayCreationOperati
? CollectionConstraint.Empty
: CollectionConstraint.NotEmpty;

public static CollectionConstraint MethodReferenceConstraint(IMethodReferenceOperationWrapper operation) =>
operation.Instance is not null && AddMethods.Contains(operation.Method.Name)
? CollectionConstraint.NotEmpty
: null;

public static ProgramState ApplyConstraints(ProgramState state, IPropertyReferenceOperationWrapper operation, ISymbol instanceSymbol)
{
if (operation.Property.IsIndexer)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* SonarAnalyzer for .NET
* Copyright (C) 2015-2024 SonarSource SA
* mailto: contact AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

namespace SonarAnalyzer.SymbolicExecution.Roslyn.OperationProcessors;

internal sealed class MethodReference : SimpleProcessor<IMethodReferenceOperationWrapper>
{
protected override IMethodReferenceOperationWrapper Convert(IOperation operation) =>
IMethodReferenceOperationWrapper.FromOperation(operation);

protected override ProgramState Process(SymbolicContext context, IMethodReferenceOperationWrapper operation) =>
CollectionTracker.MethodReferenceConstraint(operation) is { } constraint
&& operation.Instance.TrackedSymbol(context.State) is { } symbol
? context.State.SetSymbolConstraint(symbol, constraint)
: context.State;
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,21 +84,6 @@ public abstract class EmptyCollectionsShouldNotBeEnumeratedBase : SymbolicRuleCh
nameof(Dictionary<int, int>.TryGetValue)
};

private static readonly HashSet<string> AddMethods = new()
{
nameof(ICollection<int>.Add),
nameof(List<int>.AddRange),
nameof(List<int>.Insert),
nameof(List<int>.InsertRange),
nameof(HashSet<int>.UnionWith),
nameof(HashSet<int>.SymmetricExceptWith), // This can add and/or remove items => It should remove all CollectionConstraints.
// However, just learning NotEmpty (and thus unlearning Empty) is good enough for now.
nameof(Queue<int>.Enqueue),
nameof(Stack<int>.Push),
nameof(Collection<int>.Insert),
"TryAdd"
};

private static readonly HashSet<string> RemoveMethods = new()
{
nameof(ICollection<int>.Remove),
Expand All @@ -115,22 +100,10 @@ public abstract class EmptyCollectionsShouldNotBeEnumeratedBase : SymbolicRuleCh
private readonly HashSet<IOperation> emptyAccess = new();
private readonly HashSet<IOperation> nonEmptyAccess = new();

protected override ProgramState PreProcessSimple(SymbolicContext context)
{
var operation = context.Operation.Instance;
if (operation.AsInvocation() is { } invocation)
{
return ProcessInvocation(context, invocation);
}
else if (operation.AsMethodReference() is { Instance: not null } methodReference)
{
return ProcessAddMethod(context.State, methodReference.Method, methodReference.Instance) ?? context.State;
}
else
{
return context.State;
}
}
protected override ProgramState PreProcessSimple(SymbolicContext context) =>
context.Operation.Instance.AsInvocation() is { } invocation
? ProcessInvocation(context, invocation)
: context.State;

public override void ExecutionCompleted()
{
Expand Down Expand Up @@ -176,7 +149,7 @@ bool HasFilteringPredicate() =>
}

private static ProgramState ProcessAddMethod(ProgramState state, IMethodSymbol method, IOperation instance) =>
AddMethods.Contains(method.Name)
CollectionTracker.AddMethods.Contains(method.Name)
? SetOperationAndSymbolConstraint(state, instance, CollectionConstraint.NotEmpty)
: null;

Expand Down

0 comments on commit 0c15e9a

Please sign in to comment.