Skip to content

Commit

Permalink
SE - Nullable: Propagate constraints via nullable constructor (#6856)
Browse files Browse the repository at this point in the history
  • Loading branch information
pavel-mikula-sonarsource committed Mar 31, 2023
1 parent 7f93aee commit 54f7f60
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,26 @@ internal sealed class ObjectCreation : SimpleProcessor<IObjectCreationOperationW
protected override IObjectCreationOperationWrapper Convert(IOperation operation) =>
IObjectCreationOperationWrapper.FromOperation(operation);

protected override ProgramState Process(SymbolicContext context, IObjectCreationOperationWrapper operation) =>
operation.Type.IsNullableValueType() && operation.Arguments.IsEmpty
? context.SetOperationConstraint(ObjectConstraint.Null)
: context.SetOperationConstraint(ObjectConstraint.NotNull);
protected override ProgramState Process(SymbolicContext context, IObjectCreationOperationWrapper operation)
{
if (operation.Type.IsNullableValueType())
{
if (operation.Arguments.IsEmpty)
{
return context.SetOperationConstraint(ObjectConstraint.Null);
}
else if (context.State[operation.Arguments.First().ToArgument().Value] is { } value)
{
return context.State.SetOperationValue(context.Operation, value.WithConstraint(ObjectConstraint.NotNull));
}
else
{
return context.SetOperationConstraint(ObjectConstraint.NotNull);
}
}
else
{
return context.SetOperationConstraint(ObjectConstraint.NotNull);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ public override string ToString() =>
SerializeConstraints();

public SymbolicValue WithConstraint(SymbolicConstraint constraint) =>
this with { Constraints = Constraints.SetItem(constraint.GetType(), constraint) };
HasConstraint(constraint)
? this
: this with { Constraints = Constraints.SetItem(constraint.GetType(), constraint) };

public SymbolicValue WithoutConstraint(SymbolicConstraint constraint) =>
HasConstraint(constraint)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,5 +107,24 @@ public void Main<T>() where T: struct
validator.ValidateTag("TargetTyped", x => x.HasConstraint(ObjectConstraint.NotNull).Should().BeTrue("new() of int produces value 0"));
validator.ValidateTag("GenericValue", x => x.HasConstraint(ObjectConstraint.NotNull).Should().BeTrue("new() of T produces value T"));
validator.ValidateTag("GenericNull", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue());
}
}

[TestMethod]
public void Nullable_Ctor_Argument_PropagateConstraints()
{
const string code = """
var falseValue = false; // This will set additional constraint TestConstraint.First
bool? isTrue = new Nullable<bool>(true);
bool? isFalse = new Nullable<bool>(falseValue);
int? isInt = new Nullable<int>(42);
Tag("IsTrue", isTrue);
Tag("IsFalse", isFalse);
Tag("IsInt", isInt);
""";
var setter = new PreProcessTestCheck(OperationKind.Literal, x => x.Operation.Instance.ConstantValue.Value is false ? x.SetOperationConstraint(TestConstraint.First) : x.State);
var validator = SETestContext.CreateCS(code, setter).Validator;
validator.ValidateTag("IsTrue", x => x.AllConstraints.Select(x => x.Kind).Should().BeEquivalentTo(new[] { ConstraintKind.ObjectNotNull, ConstraintKind.BoolTrue }));
validator.ValidateTag("IsFalse", x => x.AllConstraints.Select(x => x.Kind).Should().BeEquivalentTo(new[] { ConstraintKind.ObjectNotNull, ConstraintKind.BoolFalse, (ConstraintKind)ConstraintKindTest.First }));
validator.ValidateTag("IsInt", x => x.AllConstraints.Select(x => x.Kind).Should().BeEquivalentTo(new[] { ConstraintKind.ObjectNotNull }));
}
}

0 comments on commit 54f7f60

Please sign in to comment.