Skip to content

Commit

Permalink
Set Null constraint for default in SE (#5899)
Browse files Browse the repository at this point in the history
  • Loading branch information
pavel-mikula-sonarsource authored Jul 27, 2022
1 parent 64e389e commit ca721bd
Show file tree
Hide file tree
Showing 5 changed files with 105 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,28 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/

using Microsoft.CodeAnalysis;
using StyleCop.Analyzers.Lightup;

namespace SonarAnalyzer.SymbolicExecution.Roslyn.OperationProcessors
{
internal static class Literal
{
public static ProgramState Process(SymbolicContext context, ILiteralOperationWrapper literal) =>
literal.WrappedOperation.ConstantValue is { HasValue: true, Value: null }
Process(context, literal.Type);

public static ProgramState Process(SymbolicContext context, IDefaultValueOperationWrapper defaultValue) =>
Process(context, defaultValue.Type);

private static ProgramState Process(SymbolicContext context, ITypeSymbol type) =>
context.Operation.Instance.ConstantValue is { HasValue: true, Value: null }
&& (type ?? ConvertedType(context.Operation.Parent)) is { IsReferenceType: true }
? context.State.SetOperationValue(context.Operation, SymbolicValue.Null)
: context.State;

private static ITypeSymbol ConvertedType(IOperation operation) =>
IConversionOperationWrapper.IsInstance(operation)
? IConversionOperationWrapper.FromOperation(operation).Type
: null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,7 @@ private static ProgramState ProcessOperation(SymbolicContext context)
OperationKindEx.AnonymousObjectCreation => Creation.Process(context),
OperationKindEx.Binary => Binary.Process(context, As(IBinaryOperationWrapper.FromOperation)),
OperationKindEx.Conversion => Conversion.Process(context, As(IConversionOperationWrapper.FromOperation)),
OperationKindEx.DefaultValue => Literal.Process(context, As(IDefaultValueOperationWrapper.FromOperation)),
OperationKindEx.DelegateCreation => Creation.Process(context),
OperationKindEx.DynamicObjectCreation => Creation.Process(context),
OperationKindEx.EventReference => References.Process(context, As(IEventReferenceOperationWrapper.FromOperation)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,7 @@ public void PreProcess_OptionalArgument_DoesNotSetConstraint()
public void Main(bool arg = true)
{
Tag(""Arg"", arg);
}
private void Tag(string name, object arg) { }";
}";
SETestContext.CreateCSMethod(code).Validator.ValidateTag("Arg", x => x.Should().BeNull());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -573,38 +573,107 @@ public void TypeParameterObjectCreation_SetsNotNull()
{
var value = new T();
Tag(""Value"", value);
}
private void Tag(string name, object arg) { }";
}";
var validator = SETestContext.CreateCSMethod(code).Validator;
validator.ValidateContainsOperation(OperationKind.TypeParameterObjectCreation);
validator.ValidateTag("Value", x => x.HasConstraint(ObjectConstraint.NotNull).Should().BeTrue());
}

[TestMethod]
public void Literal_Null_SetsNull_CS()
public void Literal_NullAndDefault_SetsNull_CS()
{
const string code = @"
Tag(""Before"", arg);
arg = null;
Tag(""After"", arg);";
var validator = SETestContext.CreateCS(code, ", object arg").Validator;
Tag(""BeforeObjNull"", argObjNull);
Tag(""BeforeObjDefault"", argObjDefault);
Tag(""BeforeInt"", argInt);
argObjNull = null;
argObjDefault = default;
argInt = default;
Tag(""AfterObjNull"", argObjNull);
Tag(""AfterObjDefault"", argObjDefault);
Tag(""AfterInt"", argInt);";
var validator = SETestContext.CreateCS(code, ", object argObjNull, object argObjDefault, int argInt").Validator;
validator.ValidateContainsOperation(OperationKind.Literal);
validator.ValidateTag("Before", x => x.Should().BeNull());
validator.ValidateTag("After", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue());
validator.ValidateTag("BeforeObjNull", x => x.Should().BeNull());
validator.ValidateTag("BeforeObjDefault", x => x.Should().BeNull());
validator.ValidateTag("BeforeInt", x => x.Should().BeNull());
validator.ValidateTag("AfterObjNull", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue());
validator.ValidateTag("AfterObjDefault", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue());
validator.ValidateTag("AfterInt", x => x.Should().BeNull());
}

[TestMethod]
public void Literal_Null_SetsNull_VB()
{
const string code = @"
Tag(""Before"", Arg)
Arg = Nothing
Tag(""After"", Arg)";
var validator = SETestContext.CreateVB(code, ", Arg As Object").Validator;
Tag(""BeforeObj"", ArgObj)
Tag(""BeforeInt"", ArgInt)
ArgObj = Nothing
ArgInt = Nothing
Tag(""AfterObj"", ArgObj)
Tag(""AfterInt"", ArgInt)";
var validator = SETestContext.CreateVB(code, ", ArgObj As Object, ArgInt As Integer").Validator;
validator.ValidateContainsOperation(OperationKind.Literal);
validator.ValidateTag("Before", x => x.Should().BeNull());
validator.ValidateTag("After", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue());
validator.ValidateTag("BeforeObj", x => x.Should().BeNull());
validator.ValidateTag("BeforeInt", x => x.Should().BeNull());
validator.ValidateTag("AfterObj", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue());
validator.ValidateTag("AfterInt", x => x.Should().BeNull());
}

[TestMethod]
public void Literal_Default_ForGenericType()
{
const string code = @"
public void Main<TClass, TStruct, TUnknown, TType, TInterface, TUnmanaged, TEnum, TDelegate>
(TClass argClass, TStruct argStruct, TUnknown argUnknown, TType argType, TInterface argInterface, TUnmanaged argUnmanaged, TEnum argEnum, TDelegate argDelegate)
where TClass : class
where TStruct: struct
where TType : EventArgs
where TInterface : IDisposable
where TUnmanaged : unmanaged
where TEnum : Enum
where TDelegate : Delegate
{
argClass = default;
argStruct = default;
argUnknown = default;
argType = default;
argInterface = default;
argUnmanaged = default;
argEnum = default;
argDelegate = default;
Tag(""Class"", argClass);
Tag(""Struct"", argStruct);
Tag(""Unknown"", argUnknown);
Tag(""Type"", argType);
Tag(""Interface"", argInterface);
Tag(""Unmanaged"", argUnmanaged);
Tag(""Enum"", argEnum);
Tag(""Delegate"", argDelegate);
}";
var validator = SETestContext.CreateCSMethod(code).Validator;
validator.ValidateContainsOperation(OperationKind.Literal);
validator.ValidateTag("Class", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue());
validator.ValidateTag("Struct", x => x.Should().BeNull("struct cannot be null."));
validator.ValidateTag("Unknown", x => x.Should().BeNull("it can be struct."));
validator.ValidateTag("Type", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue());
validator.ValidateTag("Interface", x => x.Should().BeNull("interfaces can be implemented by a struct."));
validator.ValidateTag("Unmanaged", x => x.Should().BeNull("unmanaged implies struct and cannot be null."));
validator.ValidateTag("Enum", x => x.Should().BeNull("Enum cannot be null."));
validator.ValidateTag("Delegate", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue());
}

[TestMethod]
public void Literal_Default_ConversionsFromAnotherType()
{
const string code = @"
object o = default(Exception);
int i = default(byte);
Tag(""ObjectFromException"", o);
Tag(""IntegerFromByte"", i);";
var validator = SETestContext.CreateCS(code).Validator;
validator.ValidateTag("ObjectFromException", x => x.HasConstraint(ObjectConstraint.Null).Should().BeTrue());
validator.ValidateTag("IntegerFromByte", x => x.Should().BeNull());
}

[TestMethod]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,9 +96,13 @@ public class PersonBase

public static SETestContext CreateCSMethod(string method, params SymbolicCheck[] additionalChecks) =>
new($@"
using System;
public class Sample
{{
{method}
private void Tag(string name, object arg) {{ }}
}}", AnalyzerLanguage.CSharp, additionalChecks);

public static SETestContext CreateVB(string methodBody, params SymbolicCheck[] additionalChecks) =>
Expand Down

0 comments on commit ca721bd

Please sign in to comment.