Skip to content

Commit

Permalink
Fix errors after re-base
Browse files Browse the repository at this point in the history
  • Loading branch information
martin-strecker-sonarsource committed Sep 27, 2023
1 parent 6df4014 commit 5d8ca51
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -55,19 +55,19 @@ internal sealed partial class Invocation
nameof(string.TrimStart)
};

private static ProgramState[] ProcessSystemStringInvocation(ProgramState state, IInvocationOperationWrapper invocation)
private static ProgramStates ProcessSystemStringInvocation(ProgramState state, IInvocationOperationWrapper invocation)
{
if (invocation.TargetMethod.Name is nameof(string.IsNullOrEmpty) or nameof(string.IsNullOrWhiteSpace))
{
return ProcessIsNotNullWhen(state, invocation.WrappedOperation, invocation.Arguments[0].ToArgument(), false, true);
}
else if (StringMethodReturningNotNull.Contains(invocation.TargetMethod.Name))
{
return state.SetOperationConstraint(invocation, ObjectConstraint.NotNull).ToArray();
return new(state.SetOperationConstraint(invocation, ObjectConstraint.NotNull));
}
else
{
return state.ToArray();
return new(state);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ private static ProgramStates ProcessIsNotNullWhen(ProgramState state, IOperation
ObjectConstraint constraint when constraint == ObjectConstraint.NotNull && argument.Parameter.RefKind == RefKind.None =>
new(state), // The "normal" state handling reflects already what is going on.
ObjectConstraint constraint when constraint == ObjectConstraint.Null && argument.Parameter.RefKind == RefKind.None =>
state.SetOperationConstraint(invocation, whenBoolConstraint.Opposite).ToArray(), // IsNullOrEmpty([NotNullWhen(false)] arg) returns true if arg is null
new(state.SetOperationConstraint(invocation, whenBoolConstraint.Opposite)), // IsNullOrEmpty([NotNullWhen(false)] arg) returns true if arg is null
_ when argument.WrappedOperation.TrackedSymbol(state) is { } argumentSymbol =>
ExplodeStates(argumentSymbol),
_ => new(state)
Expand Down Expand Up @@ -186,28 +186,28 @@ private static ProgramState ProcessAssertedBoolSymbol(ProgramState state, IOpera
private static ProgramStates ProcessReferenceEquals(SymbolicContext context, IInvocationOperationWrapper invocation) =>
invocation.Arguments.Length == 2
? ProcessEqualsObject(context, invocation.Arguments[0].ToArgument().Value, invocation.Arguments[1].ToArgument().Value)
: context.State.ToArray();
: new(context.State);

private static ProgramStates ProcessEquals(SymbolicContext context, IInvocationOperationWrapper invocation) =>
invocation switch
{
{ Arguments.Length: 2, TargetMethod.IsStatic: true } => ProcessEquals(context, invocation.Arguments[0].ToArgument().Value, invocation.Arguments[1].ToArgument().Value),
{ Arguments.Length: 1 } when invocation.TargetMethod.ContainingType.IsNullableValueType() || invocation.TargetMethod.ContainingType.IsStruct() =>
ProcessEquals(context, invocation.Instance, invocation.Arguments[0].ToArgument().Value),
_ => context.State.ToArray()
_ => new(context.State)
};

private static ProgramStates ProcessEquals(SymbolicContext context, IOperation leftOperation, IOperation rightOperation)
{
if (context.State.Constraint<BoolConstraint>(leftOperation) is { } leftBool
&& context.State.Constraint<BoolConstraint>(rightOperation) is { } rightBool)
{
return context.SetOperationConstraint(BoolConstraint.From(leftBool == rightBool)).ToArray();
return new(context.SetOperationConstraint(BoolConstraint.From(leftBool == rightBool)));
}
else if (context.State.Constraint<NumberConstraint>(leftOperation) is { } leftNumber
&& context.State.Constraint<NumberConstraint>(rightOperation) is { } rightNumber)
{
return ProcessNumberConstraints(leftNumber, rightNumber).ToArray();
return new(ProcessNumberConstraints(leftNumber, rightNumber));
}
else
{
Expand All @@ -231,7 +231,7 @@ ProgramState ProcessNumberConstraints(NumberConstraint left, NumberConstraint ri
}
}

private static ProgramState[] ProcessEqualsObject(SymbolicContext context, IOperation leftOperation, IOperation rightOperation)
private static ProgramStates ProcessEqualsObject(SymbolicContext context, IOperation leftOperation, IOperation rightOperation)
{
if (context.State[leftOperation]?.Constraint<ObjectConstraint>() is var leftConstraint
&& context.State[rightOperation]?.Constraint<ObjectConstraint>() is var rightConstraint
Expand Down Expand Up @@ -273,23 +273,21 @@ ProgramState NullableDefaultState()
}
}

private static ProgramState[] ProcessNullableHasValue(ProgramState state, IInvocationOperationWrapper invocation)
private static ProgramStates ProcessNullableHasValue(ProgramState state, IInvocationOperationWrapper invocation)
{
if (state[invocation.Instance]?.Constraint<ObjectConstraint>() is { } objectConstraint)
{
return state.SetOperationConstraint(invocation, BoolConstraint.From(objectConstraint == ObjectConstraint.NotNull)).ToArray();
return new(state.SetOperationConstraint(invocation, BoolConstraint.From(objectConstraint == ObjectConstraint.NotNull)));
}
else if (invocation.Instance.TrackedSymbol(state) is { } symbol)
{
return new[]
{
return new(
state.SetSymbolConstraint(symbol, ObjectConstraint.Null).SetOperationConstraint(invocation, BoolConstraint.False),
state.SetSymbolConstraint(symbol, ObjectConstraint.NotNull).SetOperationConstraint(invocation, BoolConstraint.True),
};
state.SetSymbolConstraint(symbol, ObjectConstraint.NotNull).SetOperationConstraint(invocation, BoolConstraint.True));
}
else
{
return state.ToArray();
return new(state);
}
}

Expand All @@ -303,15 +301,13 @@ private static ProgramState[] ProcessNullableHasValue(ProgramState state, IInvoc
private static ProgramStates ProcessInformationIsNothing(SymbolicContext context, IInvocationOperationWrapper invocation) =>
context.State[invocation.Arguments[0].ToArgument().Value]?.Constraint<ObjectConstraint>() switch
{
ObjectConstraint constraint when constraint == ObjectConstraint.Null => context.SetOperationConstraint(BoolConstraint.True).ToArray(),
ObjectConstraint constraint when constraint == ObjectConstraint.NotNull => context.SetOperationConstraint(BoolConstraint.False).ToArray(),
_ when invocation.Arguments[0].ToArgument().Value.UnwrapConversion().Type is { } type && !type.CanBeNull() => context.SetOperationConstraint(BoolConstraint.False).ToArray(),
_ when invocation.Arguments[0].TrackedSymbol(context.State) is { } argumentSymbol => new[]
{
context.SetOperationConstraint(BoolConstraint.True).SetSymbolConstraint(argumentSymbol, ObjectConstraint.Null),
context.SetOperationConstraint(BoolConstraint.False).SetSymbolConstraint(argumentSymbol, ObjectConstraint.NotNull),
},
_ => context.State.ToArray()
ObjectConstraint constraint when constraint == ObjectConstraint.Null => new(context.SetOperationConstraint(BoolConstraint.True)),
ObjectConstraint constraint when constraint == ObjectConstraint.NotNull => new(context.SetOperationConstraint(BoolConstraint.False)),
_ when invocation.Arguments[0].ToArgument().Value.UnwrapConversion().Type is { } type && !type.CanBeNull() => new(context.SetOperationConstraint(BoolConstraint.False)),
_ when invocation.Arguments[0].TrackedSymbol(context.State) is { } argumentSymbol => new(
context.SetOperationConstraint(BoolConstraint.True).SetSymbolConstraint(argumentSymbol, ObjectConstraint.Null),
context.SetOperationConstraint(BoolConstraint.False).SetSymbolConstraint(argumentSymbol, ObjectConstraint.NotNull)),
_ => new(context.State),
};

private static bool IsNullableGetValueOrDefault(IInvocationOperationWrapper invocation) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,10 @@ public abstract class ConditionEvaluatesToConstantBase : SymbolicRuleCheck
protected abstract bool IsInsideUsingDeclaration(SyntaxNode node);
protected abstract bool IsLockStatement(SyntaxNode syntax);

public override ProgramState[] PreProcess(SymbolicContext context)
public override ProgramStates PreProcess(SymbolicContext context)
{
reachedOperations.Add(context.Operation.Instance);
return context.State.ToArray();
return new(context.State);
}

public override ProgramState ConditionEvaluated(SymbolicContext context)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,25 +73,21 @@ public override bool ShouldExecute()
return collector.LockAcquiredAndReleased;
}

public override ProgramState[] PostProcess(SymbolicContext context)
public override ProgramStates PostProcess(SymbolicContext context)
{
if (context.Operation.Instance.Kind == OperationKindEx.Invocation)
{
if (FindRefParam(context) is { } refParamContext)
{
return new[]
{
return new(
refParamContext.SetRefConstraint(BoolConstraint.True, AddLock(refParamContext.SymbolicContext, refParamContext.LockSymbol)),
refParamContext.SetRefConstraint(BoolConstraint.False, refParamContext.SymbolicContext.State),
};
refParamContext.SetRefConstraint(BoolConstraint.False, refParamContext.SymbolicContext.State));
}
else if (FindLockSymbolWithConditionalReturnValue(context) is { } lockSymbol)
{
return new[]
{
return new(
AddLock(context, lockSymbol).SetOperationConstraint(context.Operation, BoolConstraint.True),
context.SetOperationConstraint(BoolConstraint.False)
};
context.SetOperationConstraint(BoolConstraint.False));
}
}
else if (context.Operation.Instance.AsObjectCreation() is { } objectCreation
Expand All @@ -105,12 +101,10 @@ public override ProgramState[] PostProcess(SymbolicContext context)
lastSymbolLock[symbol] = objectCreation.ToSonar();
return objectCreation.ArgumentValue("createdNew") is { } createdNew
&& createdNew.TrackedSymbol(context.State) is { } trackedCreatedNew
? new[]
{
AddLock(context, objectCreation.WrappedOperation).Preserve(symbol).SetSymbolConstraint(trackedCreatedNew, BoolConstraint.True),
context.SetSymbolConstraint(trackedCreatedNew, BoolConstraint.False),
}
: AddLock(context, objectCreation.WrappedOperation).Preserve(symbol).ToArray();
? new(
AddLock(context, objectCreation.WrappedOperation).Preserve(symbol).SetSymbolConstraint(trackedCreatedNew, BoolConstraint.True),
context.SetSymbolConstraint(trackedCreatedNew, BoolConstraint.False))
: new(AddLock(context, objectCreation.WrappedOperation).Preserve(symbol));
}

return base.PostProcess(context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ public struct Enumerator

public Enumerator(States<T> states) => this.states = states;

public T Current =>
public readonly T Current =>
index switch
{
1 => states.first,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,33 +19,33 @@
*/

namespace SonarAnalyzer.SymbolicExecution.Roslyn;

/// <summary>
/// Lifespan of this class is one method analyzed with SE.
/// </summary>
public class SymbolicCheck
{
/// <summary>
/// Lifespan of this class is one method analyzed with SE.
/// Stop processing this branch of the exploded graph. There will be no follow up states.
/// </summary>
public class SymbolicCheck
{
/// <summary>
/// Stop processing this branch of the exploded graph. There will be no follow up states.
/// </summary>
protected static readonly ProgramStates EmptyStates = new();
protected static readonly ProgramStates EmptyStates = new();

protected SymbolicCheck() { } // Avoid abstract class, fixes S1694

public virtual ProgramState ConditionEvaluated(SymbolicContext context) =>
context.State;

/// <summary>
/// Override this if you need to return multiple states.
/// </summary>
public virtual ProgramStates PreProcess(SymbolicContext context) =>
PreProcessSimple(context) is { } newState ? new(newState) : EmptyStates;
/// <summary>
/// Override this if you need to return multiple states.
/// </summary>
public virtual ProgramStates PreProcess(SymbolicContext context) =>
PreProcessSimple(context) is { } newState ? new(newState) : EmptyStates;

/// <summary>
/// Override this if you need to return multiple states.
/// </summary>
public virtual ProgramStates PostProcess(SymbolicContext context) =>
PostProcessSimple(context) is { } newState ? new(newState) : EmptyStates;
/// <summary>
/// Override this if you need to return multiple states.
/// </summary>
public virtual ProgramStates PostProcess(SymbolicContext context) =>
PostProcessSimple(context) is { } newState ? new(newState) : EmptyStates;

/// <summary>
/// Method is invoked for each execution flow that reaches exit block. Once for each unique state after LVA cleanup.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,9 +345,8 @@ public void Execute_StopsEarly_IfCancellationTokenIsCancelled(bool shouldCancel,
validator.ValidateExitReachCount(expectedExitPoints);
}

private static States<ProgramState> DecorateIntLiteral(SymbolicContext context, SymbolicConstraint first, SymbolicConstraint second) =>
context.Operation.Instance.Kind == OperationKind.Literal && context.Operation.Instance.ConstantValue.Value is int
? new(context.SetOperationConstraint(first), context.SetOperationConstraint(second))
: new(context.State);
}
private static States<ProgramState> DecorateIntLiteral(SymbolicContext context, SymbolicConstraint first, SymbolicConstraint second) =>
context.Operation.Instance.Kind == OperationKind.Literal && context.Operation.Instance.ConstantValue.Value is int
? new(context.SetOperationConstraint(first), context.SetOperationConstraint(second))
: new(context.State);
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ public void Notifications_ExecutedForAll()
var a = new Mock<SymbolicCheck>();
var b = new Mock<SymbolicCheck>();
var context = new SymbolicContext(null, null, ProgramState.Empty, false, 0, Array.Empty<ISymbol>());
a.Setup(x => x.PreProcess(context)).Returns(new[] { context.State });
a.Setup(x => x.PostProcess(context)).Returns(new[] { context.State });
a.Setup(x => x.PreProcess(context)).Returns(new ProgramStates(context.State));
a.Setup(x => x.PostProcess(context)).Returns(new ProgramStates(context.State));
var sut = new SymbolicCheckList(new[] { a.Object, b.Object });

a.Verify(x => x.ExitReached(context), Times.Never);
Expand Down Expand Up @@ -68,20 +68,19 @@ public void Notifications_ExecutedForAll()
b.Verify(x => x.PostProcess(context), Times.Once);
}

[TestMethod]
public void PostProcess_CanReturnMultipleStates()
{
var triple = new PostProcessTestCheck(x => new ProgramStates(x.State, x.State, x.State));
var sut = new SymbolicCheckList(new[] { triple, triple });
sut.PostProcess(new(null, ProgramState.Empty, Array.Empty<ISymbol>())).Length.Should().Be(9);
}
[TestMethod]
public void PostProcess_CanReturnMultipleStates()
{
var triple = new PostProcessTestCheck(x => new ProgramStates(x.State, x.State, x.State));
var sut = new SymbolicCheckList(new[] { triple, triple });
sut.PostProcess(new(null, null, ProgramState.Empty, false, 0, Array.Empty<ISymbol>())).Length.Should().Be(9);
}

[TestMethod]
public void PostProcess_CanReturnNoStates()
{
var empty = new PostProcessTestCheck(x => new ProgramStates());
var sut = new SymbolicCheckList(new[] { empty });
sut.PostProcess(new(null, ProgramState.Empty, Array.Empty<ISymbol>())).Length.Should().Be(0);
}
[TestMethod]
public void PostProcess_CanReturnNoStates()
{
var empty = new PostProcessTestCheck(x => new ProgramStates());
var sut = new SymbolicCheckList(new[] { empty });
sut.PostProcess(new(null, null, ProgramState.Empty, false, 0, Array.Empty<ISymbol>())).Length.Should().Be(0);
}
}

0 comments on commit 5d8ca51

Please sign in to comment.