C#: Improved extraction of type nullability

This commit is contained in:
Tom Hvitved
2021-01-14 11:21:55 +01:00
parent 81ce29c6c8
commit 9a9a57716c
38 changed files with 147 additions and 176 deletions

View File

@@ -52,7 +52,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
if (implicitThis && !symbol.IsStatic)
{
This.CreateImplicit(cx, Entities.Type.Create(cx, symbol.ContainingType), Location, this, -1);
This.CreateImplicit(cx, symbol.ContainingType, Location, this, -1);
}
}

View File

@@ -90,7 +90,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
var info = new ExpressionInfo(
cx,
new AnnotatedType(Entities.Type.Create(cx, type), NullableAnnotation.None),
AnnotatedTypeSymbol.CreateNotAnnotated(type),
location,
ExprKind.ARRAY_CREATION,
parent,

View File

@@ -36,7 +36,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
var info = new ExpressionInfo(
cx,
new AnnotatedType(Entities.Type.Create(cx, type), Microsoft.CodeAnalysis.NullableAnnotation.None),
AnnotatedTypeSymbol.CreateNotAnnotated(type),
location,
ExprKind.CAST,
parent,

View File

@@ -12,7 +12,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
}
private Discard(Context cx, CSharpSyntaxNode syntax, IExpressionParentEntity parent, int child) :
base(new ExpressionInfo(cx, Entities.Type.Create(cx, cx.GetType(syntax)), cx.Create(syntax.GetLocation()), ExprKind.DISCARD, parent, child, false, null))
base(new ExpressionInfo(cx, cx.GetType(syntax), cx.Create(syntax.GetLocation()), ExprKind.DISCARD, parent, child, false, null))
{
}

View File

@@ -12,13 +12,13 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
}
public ImplicitCast(ExpressionNodeInfo info)
: base(new ExpressionInfo(info.Context, Entities.Type.Create(info.Context, info.ConvertedType), info.Location, ExprKind.CAST, info.Parent, info.Child, true, info.ExprValue))
: base(new ExpressionInfo(info.Context, info.ConvertedType, info.Location, ExprKind.CAST, info.Parent, info.Child, true, info.ExprValue))
{
Expr = Factory.Create(new ExpressionNodeInfo(cx, info.Node, this, 0));
}
public ImplicitCast(ExpressionNodeInfo info, IMethodSymbol method)
: base(new ExpressionInfo(info.Context, Entities.Type.Create(info.Context, info.ConvertedType), info.Location, ExprKind.OPERATOR_INVOCATION, info.Parent, info.Child, true, info.ExprValue))
: base(new ExpressionInfo(info.Context, info.ConvertedType, info.Location, ExprKind.OPERATOR_INVOCATION, info.Parent, info.Child, true, info.ExprValue))
{
Expr = Factory.Create(info.SetParent(this, 0));

View File

@@ -14,7 +14,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
internal class ArrayInitializer : Expression<InitializerExpressionSyntax>
{
private ArrayInitializer(ExpressionNodeInfo info) : base(info.SetType(NullType.Create(info.Context)).SetKind(ExprKind.ARRAY_INIT)) { }
private ArrayInitializer(ExpressionNodeInfo info) : base(info.SetType(null).SetKind(ExprKind.ARRAY_INIT)) { }
public static Expression Create(ExpressionNodeInfo info) => new ArrayInitializer(info).TryPopulate();
@@ -40,7 +40,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
var info = new ExpressionInfo(
cx,
NullType.Create(cx),
null,
location,
ExprKind.ARRAY_INIT,
parent,
@@ -135,7 +135,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
var collectionInfo = cx.GetModel(Syntax).GetCollectionInitializerSymbolInfo(i);
var addMethod = Method.Create(cx, collectionInfo.Symbol as IMethodSymbol);
var voidType = Entities.Type.Create(cx, new AnnotatedTypeSymbol(cx.Compilation.GetSpecialType(SpecialType.System_Void), NullableAnnotation.None));
var voidType = AnnotatedTypeSymbol.CreateNotAnnotated(cx.Compilation.GetSpecialType(SpecialType.System_Void));
var invocation = new Expression(new ExpressionInfo(cx, voidType, cx.Create(i.GetLocation()), ExprKind.METHOD_INVOCATION, this, child++, false, null));

View File

@@ -55,7 +55,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
// Implicit `this` qualifier; add explicitly
if (cx.GetModel(Syntax).GetEnclosingSymbol(Location.symbol.SourceSpan.Start) is IMethodSymbol callingMethod)
This.CreateImplicit(cx, Entities.Type.Create(cx, callingMethod.ContainingType), Location, this, child++);
This.CreateImplicit(cx, callingMethod.ContainingType, Location, this, child++);
else
cx.ModelError(Syntax, "Couldn't determine implicit this type");
}

View File

@@ -21,11 +21,11 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
case SyntaxKind.DefaultLiteralExpression:
return ExprKind.DEFAULT;
case SyntaxKind.NullLiteralExpression:
info.Type = Entities.NullType.Create(info.Context); // Don't use converted type.
info.SetType(null); // Don't use converted type.
return ExprKind.NULL_LITERAL;
}
var type = info.Type.Type.symbol;
var type = info.Type?.Symbol;
return GetExprKind(type, info.Node, info.Context);
}
@@ -82,7 +82,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
var info = new ExpressionInfo(
cx,
new AnnotatedType(Entities.Type.Create(cx, type), NullableAnnotation.None),
AnnotatedTypeSymbol.CreateNotAnnotated(type),
location,
GetExprKind(type, null, cx),
parent,
@@ -97,7 +97,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
var info = new ExpressionInfo(
cx,
NullType.Create(cx),
null,
location,
ExprKind.NULL_LITERAL,
parent,

View File

@@ -35,7 +35,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
// Create an "assignment"
var property = cx.GetModel(init).GetDeclaredSymbol(init);
var propEntity = Property.Create(cx, property);
var type = Entities.Type.Create(cx, property.GetAnnotatedType());
var type = property.GetAnnotatedType();
var loc = cx.Create(init.GetLocation());
var assignment = new Expression(new ExpressionInfo(cx, type, loc, ExprKind.SIMPLE_ASSIGN, objectInitializer, child++, false, null));

View File

@@ -46,10 +46,10 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
switch (Syntax.Initializer.Kind())
{
case SyntaxKind.CollectionInitializerExpression:
CollectionInitializer.Create(new ExpressionNodeInfo(cx, Syntax.Initializer, this, -1) { Type = Type });
CollectionInitializer.Create(new ExpressionNodeInfo(cx, Syntax.Initializer, this, -1).SetType(Type));
break;
case SyntaxKind.ObjectInitializerExpression:
ObjectInitializer.Create(new ExpressionNodeInfo(cx, Syntax.Initializer, this, -1) { Type = Type });
ObjectInitializer.Create(new ExpressionNodeInfo(cx, Syntax.Initializer, this, -1).SetType(Type));
break;
default:
cx.ModelError("Unhandled initializer in object creation");

View File

@@ -30,7 +30,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
if (cx.GetModel(syntax).GetDeclaredSymbol(designation) is ILocalSymbol symbol)
{
var type = Type.Create(cx, symbol.GetAnnotatedType());
var type = symbol.GetAnnotatedType();
return VariableDeclaration.Create(cx, symbol, type, declPattern.Type, cx.Create(syntax.GetLocation()), false, parent, child);
}
if (designation is DiscardDesignationSyntax)
@@ -53,7 +53,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
case SingleVariableDesignationSyntax varDesignation:
if (cx.GetModel(syntax).GetDeclaredSymbol(varDesignation) is ILocalSymbol symbol)
{
var type = Type.Create(cx, symbol.GetAnnotatedType());
var type = symbol.GetAnnotatedType();
return VariableDeclaration.Create(cx, symbol, type, null, cx.Create(syntax.GetLocation()), true, parent, child);
}

View File

@@ -7,7 +7,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
internal class PositionalPattern : Expression
{
internal PositionalPattern(Context cx, PositionalPatternClauseSyntax posPc, IExpressionParentEntity parent, int child) :
base(new ExpressionInfo(cx, Entities.NullType.Create(cx), cx.Create(posPc.GetLocation()), ExprKind.POSITIONAL_PATTERN, parent, child, false, null))
base(new ExpressionInfo(cx, null, cx.Create(posPc.GetLocation()), ExprKind.POSITIONAL_PATTERN, parent, child, false, null))
{
child = 0;
foreach (var sub in posPc.Subpatterns)

View File

@@ -7,7 +7,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
internal class PropertyPattern : Expression
{
internal PropertyPattern(Context cx, PropertyPatternClauseSyntax pp, IExpressionParentEntity parent, int child) :
base(new ExpressionInfo(cx, Entities.NullType.Create(cx), cx.Create(pp.GetLocation()), ExprKind.PROPERTY_PATTERN, parent, child, false, null))
base(new ExpressionInfo(cx, null, cx.Create(pp.GetLocation()), ExprKind.PROPERTY_PATTERN, parent, child, false, null))
{
child = 0;
var trapFile = cx.TrapWriter.Writer;

View File

@@ -17,7 +17,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
/// <param name="child">The child index of this pattern.</param>
/// <param name="isTopLevel">If this pattern is in the top level of a case/is. In that case, the variable and type access are populated elsewhere.</param>
public RecursivePattern(Context cx, RecursivePatternSyntax syntax, IExpressionParentEntity parent, int child) :
base(new ExpressionInfo(cx, Entities.NullType.Create(cx), cx.Create(syntax.GetLocation()), ExprKind.RECURSIVE_PATTERN, parent, child, false, null))
base(new ExpressionInfo(cx, null, cx.Create(syntax.GetLocation()), ExprKind.RECURSIVE_PATTERN, parent, child, false, null))
{
// Extract the type access
if (syntax.Type is TypeSyntax t)
@@ -26,7 +26,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
// Extract the local variable declaration
if (syntax.Designation is VariableDesignationSyntax designation && cx.GetModel(syntax).GetDeclaredSymbol(designation) is ILocalSymbol symbol)
{
var type = Entities.Type.Create(cx, symbol.GetAnnotatedType());
var type = symbol.GetAnnotatedType();
VariableDeclaration.Create(cx, symbol, type, null, cx.Create(syntax.GetLocation()), false, this, 0);
}

View File

@@ -7,7 +7,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
internal class UnaryPattern : Expression
{
public UnaryPattern(Context cx, UnaryPatternSyntax syntax, IExpressionParentEntity parent, int child) :
base(new ExpressionInfo(cx, NullType.Create(cx), cx.Create(syntax.GetLocation()), ExprKind.NOT_PATTERN, parent, child, false, null))
base(new ExpressionInfo(cx, null, cx.Create(syntax.GetLocation()), ExprKind.NOT_PATTERN, parent, child, false, null))
{
Pattern.Create(cx, syntax.Pattern, this, 0);
}

View File

@@ -22,7 +22,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
private class QueryCall : Expression
{
public QueryCall(Context cx, IMethodSymbol method, SyntaxNode clause, IExpressionParentEntity parent, int child)
: base(new ExpressionInfo(cx, method is null ? NullType.Create(cx) : Entities.Type.Create(cx, method.GetAnnotatedReturnType()),
: base(new ExpressionInfo(cx, method?.GetAnnotatedReturnType(),
cx.Create(clause.GetLocation()),
ExprKind.METHOD_INVOCATION, parent, child, false, null))
{
@@ -63,21 +63,21 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
protected Expression DeclareRangeVariable(Context cx, IExpressionParentEntity parent, int child, bool getElement, ISymbol variableSymbol, SyntaxToken name)
{
var type = Type.Create(cx, cx.GetType(Expr));
var type = cx.GetType(Expr);
AnnotatedType declType;
AnnotatedTypeSymbol? declType;
TypeSyntax declTypeSyntax = null;
if (getElement)
{
if (node is FromClauseSyntax from && from.Type != null)
{
declTypeSyntax = from.Type;
declType = Type.Create(cx, cx.GetType(from.Type));
declType = cx.GetType(from.Type);
}
else
{
declTypeSyntax = null;
declType = type.Type.ElementType;
declType = GetElementType(cx, type.Symbol);
}
}
else
@@ -104,6 +104,36 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
return decl;
}
private static AnnotatedTypeSymbol? GetEnumerableType(Context cx, INamedTypeSymbol type)
{
return type.SpecialType == SpecialType.System_Collections_IEnumerable
? cx.Compilation.ObjectType.WithAnnotation(NullableAnnotation.NotAnnotated)
: type.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T
? type.GetAnnotatedTypeArguments().First()
: (AnnotatedTypeSymbol?)null;
}
private static AnnotatedTypeSymbol? GetEnumerableElementType(Context cx, INamedTypeSymbol type)
{
var et = GetEnumerableType(cx, type);
if (et != null)
return et;
return type.AllInterfaces
.Where(i => i.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T)
.Concat(type.AllInterfaces.Where(i => i.SpecialType == SpecialType.System_Collections_IEnumerable))
.Select(i => GetEnumerableType(cx, i))
.FirstOrDefault();
}
private static AnnotatedTypeSymbol? GetElementType(Context cx, ITypeSymbol symbol) =>
symbol switch
{
IArrayTypeSymbol a => a.GetAnnotatedElementType(),
INamedTypeSymbol n => GetEnumerableElementType(cx, n),
_ => null
};
protected void PopulateArguments(Context cx, QueryCall callExpr, int child)
{
foreach (var e in arguments)

View File

@@ -29,7 +29,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
internal SwitchCase(Context cx, SwitchExpressionArmSyntax arm, Switch parent, int child) :
base(new ExpressionInfo(
cx, Entities.Type.Create(cx, cx.GetType(arm.Expression)), cx.Create(arm.GetLocation()),
cx, cx.GetType(arm.Expression), cx.Create(arm.GetLocation()),
ExprKind.SWITCH_CASE, parent, child, false, null))
{
Expressions.Pattern.Create(cx, arm.Pattern, this, 0);

View File

@@ -7,8 +7,8 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
private This(IExpressionInfo info) : base(info) { }
public static This CreateImplicit(Context cx, Type @class, Extraction.Entities.Location loc, IExpressionParentEntity parent, int child) =>
new This(new ExpressionInfo(cx, new AnnotatedType(@class, NullableAnnotation.None), loc, Kinds.ExprKind.THIS_ACCESS, parent, child, true, null));
public static This CreateImplicit(Context cx, ITypeSymbol @class, Extraction.Entities.Location loc, IExpressionParentEntity parent, int child) =>
new This(new ExpressionInfo(cx, AnnotatedTypeSymbol.CreateNotAnnotated(@class), loc, Kinds.ExprKind.THIS_ACCESS, parent, child, true, null));
public static This CreateExplicit(ExpressionNodeInfo info) => new This(info.SetKind(ExprKind.THIS_ACCESS));
}

View File

@@ -15,7 +15,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
case SyntaxKind.SimpleMemberAccessExpression:
var maes = (MemberAccessExpressionSyntax)Syntax;
if (Type.Type.ContainingType == null)
if (Type?.Symbol.ContainingType is null)
{
// namespace qualifier
TypeMention.Create(cx, maes.Name, this, Type, Syntax.GetLocation());
@@ -39,7 +39,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
var typeAccessInfo = new ExpressionInfo(
cx,
new AnnotatedType(Entities.Type.Create(cx, type), Microsoft.CodeAnalysis.NullableAnnotation.None),
AnnotatedTypeSymbol.CreateNotAnnotated(type),
location,
ExprKind.TYPE_ACCESS,
parent,

View File

@@ -21,7 +21,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
var info = new ExpressionInfo(
cx,
new AnnotatedType(Entities.Type.Create(cx, type), Microsoft.CodeAnalysis.NullableAnnotation.None),
AnnotatedTypeSymbol.CreateNotAnnotated(type),
location,
ExprKind.TYPEOF,
parent,

View File

@@ -11,7 +11,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
private VariableDeclaration(IExpressionInfo info) : base(info) { }
public static VariableDeclaration Create(Context cx, ISymbol symbol, AnnotatedType type, TypeSyntax optionalSyntax, Extraction.Entities.Location exprLocation, bool isVar, IExpressionParentEntity parent, int child)
public static VariableDeclaration Create(Context cx, ISymbol symbol, AnnotatedTypeSymbol? type, TypeSyntax optionalSyntax, Extraction.Entities.Location exprLocation, bool isVar, IExpressionParentEntity parent, int child)
{
var ret = new VariableDeclaration(new ExpressionInfo(cx, type, exprLocation, ExprKind.LOCAL_VAR_DECL, parent, child, false, null));
cx.Try(null, null, () =>
@@ -30,10 +30,10 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
if (variableSymbol == null)
{
cx.ModelError(node, "Failed to determine local variable");
return Create(cx, node, NullType.Create(cx), parent, child);
return Create(cx, node, (AnnotatedTypeSymbol?)null, parent, child);
}
var type = Entities.Type.Create(cx, variableSymbol.GetAnnotatedType());
var type = variableSymbol.GetAnnotatedType();
var ret = Create(cx, designation, type, parent, child);
cx.Try(null, null, () =>
{
@@ -49,7 +49,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
/// </summary>
public static Expression CreateParenthesized(Context cx, DeclarationExpressionSyntax node, ParenthesizedVariableDesignationSyntax designation, IExpressionParentEntity parent, int child)
{
var type = Entities.NullType.Create(cx); // Should ideally be a corresponding tuple type
AnnotatedTypeSymbol? type = null; // Should ideally be a corresponding tuple type
var tuple = new Expression(new ExpressionInfo(cx, type, cx.Create(node.GetLocation()), ExprKind.TUPLE, parent, child, false, null));
cx.Try(null, null, () =>
@@ -64,7 +64,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
public static Expression CreateParenthesized(Context cx, VarPatternSyntax varPattern, ParenthesizedVariableDesignationSyntax designation, IExpressionParentEntity parent, int child)
{
var type = NullType.Create(cx); // Should ideally be a corresponding tuple type
AnnotatedTypeSymbol? type = null; // Should ideally be a corresponding tuple type
var tuple = new Expression(new ExpressionInfo(cx, type, cx.Create(varPattern.GetLocation()), ExprKind.TUPLE, parent, child, false, null));
cx.Try(null, null, () =>
@@ -80,7 +80,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
case SingleVariableDesignationSyntax single:
if (cx.GetModel(variable).GetDeclaredSymbol(single) is ILocalSymbol local)
{
var decl = Create(cx, variable, Entities.Type.Create(cx, local.GetAnnotatedType()), tuple, child0++);
var decl = Create(cx, variable, local.GetAnnotatedType(), tuple, child0++);
var l = LocalVariable.Create(cx, local);
l.PopulateManual(decl, true);
}
@@ -111,25 +111,24 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
case ParenthesizedVariableDesignationSyntax paren:
return CreateParenthesized(cx, node, paren, parent, child);
case DiscardDesignationSyntax discard:
var ti = cx.GetType(discard);
var type = Entities.Type.Create(cx, ti);
var type = cx.GetType(discard);
return Create(cx, node, type, parent, child);
default:
cx.ModelError(node, "Failed to determine designation type");
return Create(cx, node, NullType.Create(cx), parent, child);
return Create(cx, node, null, parent, child);
}
}
public static Expression Create(Context cx, DeclarationExpressionSyntax node, IExpressionParentEntity parent, int child) =>
Create(cx, node, node.Designation, parent, child);
public static VariableDeclaration Create(Context cx, CSharpSyntaxNode c, AnnotatedType type, IExpressionParentEntity parent, int child) =>
public static VariableDeclaration Create(Context cx, CSharpSyntaxNode c, AnnotatedTypeSymbol? type, IExpressionParentEntity parent, int child) =>
new VariableDeclaration(new ExpressionInfo(cx, type, cx.Create(c.FixedLocation()), ExprKind.LOCAL_VAR_DECL, parent, child, false, null));
public static VariableDeclaration Create(Context cx, CatchDeclarationSyntax d, bool isVar, IExpressionParentEntity parent, int child)
{
var symbol = cx.GetModel(d).GetDeclaredSymbol(d);
var type = Entities.Type.Create(cx, symbol.GetAnnotatedType());
var type = symbol.GetAnnotatedType();
var ret = Create(cx, d, type, parent, child);
cx.Try(d, null, () =>
{
@@ -141,7 +140,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
return ret;
}
public static VariableDeclaration CreateDeclarator(Context cx, VariableDeclaratorSyntax d, AnnotatedType type, bool isVar, IExpressionParentEntity parent, int child)
public static VariableDeclaration CreateDeclarator(Context cx, VariableDeclaratorSyntax d, AnnotatedTypeSymbol type, bool isVar, IExpressionParentEntity parent, int child)
{
var ret = Create(cx, d, type, parent, child);
cx.Try(d, null, () =>
@@ -170,7 +169,7 @@ namespace Semmle.Extraction.CSharp.Entities.Expressions
{
public static void Populate(Context cx, VariableDeclarationSyntax decl, IExpressionParentEntity parent, int child, int childIncrement = 1)
{
var type = Type.Create(cx, cx.GetType(decl.Type));
var type = cx.GetType(decl.Type);
foreach (var v in decl.Variables)
{