Skip to content

Commit af68fd4

Browse files
authored
Merge pull request #1408 from calumgrant/cs/suppress-null-expr
C#: C#8 Nullable expressions and type annotations
2 parents 0102881 + 4d38300 commit af68fd4

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+14725
-9515
lines changed

change-notes/1.22/analysis-csharp.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,18 @@
88

99
## Changes to code extraction
1010

11+
* The following C# 8 features are now extracted:
12+
- Suppress-nullable-warning expressions, for example `x!`
13+
- Nullable reference types, for example `string?`
14+
1115
## Changes to QL libraries
1216

17+
* The new class `AnnotatedType` models types with type annotations, including nullability information, return kinds (`ref` and `readonly ref`), and parameter kinds (`in`, `out`, and `ref`)
18+
- The new predicate `Assignable.getAnnotatedType()` gets the annotated type of an assignable (such as a variable or a property)
19+
- The new predicates `Callable.getAnnotatedReturnType()` and `DelegateType.getAnnotatedReturnType()` get the annotated type of the return value
20+
- The new predicate `ArrayType.getAnnotatedElementType()` gets the annotated type of the array element
21+
- The new predicate `ConstructedGeneric.getAnnotatedTypeArgument()` gets the annotated type of a type argument
22+
- The new predicate `TypeParameterConstraints.getAnAnnotatedTypeConstraint()` gets a type constraint with type annotations
23+
* The new class `SuppressNullableWarningExpr` models suppress-nullable-warning expressions such as `x!`
24+
1325
## Changes to autobuilder

csharp/extractor/Semmle.Extraction.CSharp/Entities/Constructor.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ protected override void ExtractInitializers()
5757
}
5858

5959
var initInfo = new ExpressionInfo(Context,
60-
initializerType,
60+
new AnnotatedType(initializerType, Kinds.TypeAnnotation.NotAnnotated),
6161
Context.Create(initializer.ThisOrBaseKeyword.GetLocation()),
6262
Kinds.ExprKind.CONSTRUCTOR_INIT,
6363
this,

csharp/extractor/Semmle.Extraction.CSharp/Entities/Event.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public override IId Id
2626

2727
public override void Populate()
2828
{
29+
ExtractNullability(symbol.NullableAnnotation);
30+
2931
var type = Type.Create(Context, symbol.Type);
3032
Context.Emit(Tuples.events(this, symbol.GetName(), ContainingType, type.TypeRef, Create(Context, symbol.OriginalDefinition)));
3133

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expression.cs

Lines changed: 24 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public interface IExpressionParentEntity : IEntity
1818

1919
class Expression : FreshEntity, IExpressionParentEntity
2020
{
21-
public readonly Type Type;
21+
public readonly AnnotatedType Type;
2222
public readonly Extraction.Entities.Location Location;
2323
public readonly ExprKind Kind;
2424

@@ -27,9 +27,12 @@ internal Expression(IExpressionInfo info)
2727
{
2828
Location = info.Location;
2929
Kind = info.Kind;
30-
Type = info.Type ?? NullType.Create(cx);
30+
Type = info.Type;
3131

32-
cx.Emit(Tuples.expressions(this, Kind, Type.TypeRef));
32+
if (Type.Type is null)
33+
Type = NullType.Create(cx);
34+
35+
cx.Emit(Tuples.expressions(this, Kind, Type.Type.TypeRef));
3336
if (info.Parent.IsTopLevelParent)
3437
cx.Emit(Tuples.expr_parent_top_level(this, info.Child, info.Parent));
3538
else
@@ -42,7 +45,7 @@ internal Expression(IExpressionInfo info)
4245
if (info.ExprValue is string value)
4346
cx.Emit(Tuples.expr_value(this, value));
4447

45-
Type.ExtractGenerics();
48+
Type.Type.ExtractGenerics();
4649
}
4750

4851
public override Microsoft.CodeAnalysis.Location ReportingLocation => Location.symbol;
@@ -301,7 +304,7 @@ interface IExpressionInfo
301304
/// <summary>
302305
/// The type of the expression.
303306
/// </summary>
304-
Type Type { get; }
307+
AnnotatedType Type { get; }
305308

306309
/// <summary>
307310
/// The location of the expression.
@@ -343,15 +346,15 @@ interface IExpressionInfo
343346
class ExpressionInfo : IExpressionInfo
344347
{
345348
public Context Context { get; }
346-
public Type Type { get; }
349+
public AnnotatedType Type { get; }
347350
public Extraction.Entities.Location Location { get; }
348351
public ExprKind Kind { get; }
349352
public IExpressionParentEntity Parent { get; }
350353
public int Child { get; }
351354
public bool IsCompilerGenerated { get; }
352355
public string ExprValue { get; }
353356

354-
public ExpressionInfo(Context cx, Type type, Extraction.Entities.Location location, ExprKind kind, IExpressionParentEntity parent, int child, bool isCompilerGenerated, string value)
357+
public ExpressionInfo(Context cx, AnnotatedType type, Extraction.Entities.Location location, ExprKind kind, IExpressionParentEntity parent, int child, bool isCompilerGenerated, string value)
355358
{
356359
Context = cx;
357360
Type = type;
@@ -384,42 +387,37 @@ public ExpressionNodeInfo(Context cx, ExpressionSyntax node, IExpressionParentEn
384387
Conversion = cx.Model(node).GetConversion(node);
385388
}
386389

387-
public ExpressionNodeInfo(Context cx, ExpressionSyntax node, IExpressionParentEntity parent, int child, ITypeSymbol type) :
388-
this(cx, node, parent, child)
389-
{
390-
Type = Type.Create(cx, type);
391-
}
392-
393390
public Context Context { get; }
394391
public ExpressionSyntax Node { get; private set; }
395392
public IExpressionParentEntity Parent { get; set; }
396393
public int Child { get; set; }
397394
public TypeInfo TypeInfo { get; }
398395
public Microsoft.CodeAnalysis.CSharp.Conversion Conversion { get; }
399396

400-
public ITypeSymbol ResolvedType => Context.DisambiguateType(TypeInfo.Type);
401-
public ITypeSymbol ConvertedType => Context.DisambiguateType(TypeInfo.ConvertedType);
397+
public AnnotatedTypeSymbol ResolvedType => new AnnotatedTypeSymbol(TypeInfo.Type.DisambiguateType(), TypeInfo.Nullability.Annotation);
398+
public AnnotatedTypeSymbol ConvertedType => new AnnotatedTypeSymbol(TypeInfo.ConvertedType.DisambiguateType(), TypeInfo.ConvertedNullability.Annotation);
402399

403-
public ITypeSymbol ExpressionType
400+
public AnnotatedTypeSymbol ExpressionType
404401
{
405402
get
406403
{
407404
var type = ResolvedType;
408405

409-
if (type == null)
410-
type = Context.DisambiguateType(TypeInfo.Type ?? TypeInfo.ConvertedType);
406+
if (type.Symbol == null)
407+
type.Symbol = (TypeInfo.Type ?? TypeInfo.ConvertedType).DisambiguateType();
411408

412409
// Roslyn workaround: It can't work out the type of "new object[0]"
413410
// Clearly a bug.
414-
if (type != null && type.TypeKind == Microsoft.CodeAnalysis.TypeKind.Error)
411+
if (type.Symbol?.TypeKind == Microsoft.CodeAnalysis.TypeKind.Error)
415412
{
416413
var arrayCreation = Node as ArrayCreationExpressionSyntax;
417414
if (arrayCreation != null)
418415
{
419416
var elementType = Context.GetType(arrayCreation.Type.ElementType);
420417

421-
if (elementType != null)
422-
return Context.Compilation.CreateArrayTypeSymbol(elementType, arrayCreation.Type.RankSpecifiers.Count);
418+
if (elementType.Symbol != null)
419+
// There seems to be no way to create an array with a nullable element at present.
420+
return new AnnotatedTypeSymbol(Context.Compilation.CreateArrayTypeSymbol(elementType.Symbol, arrayCreation.Type.RankSpecifiers.Count), NullableAnnotation.NotAnnotated);
423421
}
424422

425423
Context.ModelError(Node, "Failed to determine type");
@@ -456,14 +454,14 @@ public string ExprValue
456454
}
457455
}
458456

459-
Type cachedType;
457+
AnnotatedType cachedType;
460458

461-
public Type Type
459+
public AnnotatedType Type
462460
{
463461
get
464462
{
465-
if (cachedType == null)
466-
cachedType = Type.Create(Context, ExpressionType);
463+
if (cachedType.Type == null)
464+
cachedType = Entities.Type.Create(Context, ExpressionType);
467465
return cachedType;
468466
}
469467
set
@@ -506,7 +504,7 @@ public ExpressionNodeInfo SetKind(ExprKind kind)
506504
return this;
507505
}
508506

509-
public ExpressionNodeInfo SetType(Type type)
507+
public ExpressionNodeInfo SetType(AnnotatedType type)
510508
{
511509
Type = type;
512510
return this;

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Access.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ static ExprKind AccessKind(Context cx, ISymbol symbol)
4646

4747
if (implicitThis && !symbol.IsStatic)
4848
{
49-
This.CreateImplicit(cx, Type.Create(cx, symbol.ContainingType), Location, this, -1);
49+
This.CreateImplicit(cx, Entities.Type.Create(cx, symbol.ContainingType), Location, this, -1);
5050
}
5151
}
5252

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ArrayCreation.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using Microsoft.CodeAnalysis;
12
using Microsoft.CodeAnalysis.CSharp.Syntax;
23
using Semmle.Extraction.Kinds;
34
using System.Linq;
@@ -41,7 +42,7 @@ protected override void Populate()
4142

4243
var info = new ExpressionInfo(
4344
cx,
44-
Type.Create(cx, cx.Compilation.GetSpecialType(Microsoft.CodeAnalysis.SpecialType.System_Int32)),
45+
new AnnotatedType(Entities.Type.Create(cx, cx.Compilation.GetSpecialType(Microsoft.CodeAnalysis.SpecialType.System_Int32)), Kinds.TypeAnnotation.NotAnnotated),
4546
Location,
4647
ExprKind.INT_LITERAL,
4748
this,

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Assignment.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ static ExprKind GetKind(Context cx, AssignmentExpressionSyntax syntax)
8686
var kind = GetAssignmentOperation(cx, syntax);
8787
var leftType = cx.GetType(syntax.Left);
8888

89-
if (leftType != null && leftType.SpecialType != SpecialType.None)
89+
if (leftType.Symbol != null && leftType.Symbol.SpecialType != SpecialType.None)
9090
{
9191
// In Mono, the builtin types did not specify their operator invocation
9292
// even though EVERY operator has an invocation in C#. (This is a flaw in the dbscheme and should be fixed).

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/Discard.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
using Microsoft.CodeAnalysis.CSharp.Syntax;
2-
using Semmle.Extraction.Kinds;
3-
using Semmle.Extraction.Entities;
4-
using Microsoft.CodeAnalysis;
1+
using Microsoft.CodeAnalysis;
52
using Microsoft.CodeAnalysis.CSharp;
3+
using Microsoft.CodeAnalysis.CSharp.Syntax;
4+
using Semmle.Extraction.Entities;
5+
using Semmle.Extraction.Kinds;
66

77
namespace Semmle.Extraction.CSharp.Entities.Expressions
88
{
@@ -13,7 +13,7 @@ public Discard(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.DISCARD))
1313
}
1414

1515
Discard(Context cx, CSharpSyntaxNode syntax, IExpressionParentEntity parent, int child) :
16-
base(new ExpressionInfo(cx, Type.Create(cx, cx.Model(syntax).GetTypeInfo(syntax).Type), cx.Create(syntax.GetLocation()), ExprKind.DISCARD, parent, child, false, null))
16+
base(new ExpressionInfo(cx, Entities.Type.Create(cx, cx.GetType(syntax)), cx.Create(syntax.GetLocation()), ExprKind.DISCARD, parent, child, false, null))
1717
{
1818
}
1919

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ElementAccess.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,17 +55,17 @@ static ExprKind GetKind(Context cx, ExpressionSyntax qualifier)
5555
var qualifierType = cx.GetType(qualifier);
5656

5757
// This is a compilation error, so make a guess and continue.
58-
if (qualifierType == null) return ExprKind.ARRAY_ACCESS;
58+
if (qualifierType.Symbol == null) return ExprKind.ARRAY_ACCESS;
5959

60-
if (qualifierType.TypeKind == Microsoft.CodeAnalysis.TypeKind.Pointer)
60+
if (qualifierType.Symbol.TypeKind == Microsoft.CodeAnalysis.TypeKind.Pointer)
6161
{
6262
// Convert expressions of the form a[b] into *(a+b)
6363
return ExprKind.POINTER_INDIRECTION;
6464
}
6565

6666
return IsDynamic(cx, qualifier) ?
6767
ExprKind.DYNAMIC_ELEMENT_ACCESS :
68-
qualifierType.TypeKind == Microsoft.CodeAnalysis.TypeKind.Array ?
68+
qualifierType.Symbol.TypeKind == Microsoft.CodeAnalysis.TypeKind.Array ?
6969
ExprKind.ARRAY_ACCESS :
7070
ExprKind.INDEXER_ACCESS;
7171
}

csharp/extractor/Semmle.Extraction.CSharp/Entities/Expressions/ImplicitCast.cs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@ public Expression Expr
1515
}
1616

1717
public ImplicitCast(ExpressionNodeInfo info)
18-
: base(new ExpressionInfo(info.Context, Type.Create(info.Context, info.ConvertedType), info.Location, ExprKind.CAST, info.Parent, info.Child, true, info.ExprValue))
18+
: base(new ExpressionInfo(info.Context, Entities.Type.Create(info.Context, info.ConvertedType), info.Location, ExprKind.CAST, info.Parent, info.Child, true, info.ExprValue))
1919
{
2020
Expr = Factory.Create(new ExpressionNodeInfo(cx, info.Node, this, 0));
2121
}
2222

2323
public ImplicitCast(ExpressionNodeInfo info, IMethodSymbol method)
24-
: base(new ExpressionInfo(info.Context, Type.Create(info.Context, info.ConvertedType), info.Location, ExprKind.OPERATOR_INVOCATION, info.Parent, info.Child, true, info.ExprValue) )
24+
: base(new ExpressionInfo(info.Context, Entities.Type.Create(info.Context, info.ConvertedType), info.Location, ExprKind.OPERATOR_INVOCATION, info.Parent, info.Child, true, info.ExprValue) )
2525
{
2626
Expr = Factory.Create(info.SetParent(this, 0));
2727

@@ -49,7 +49,7 @@ public static Expression Create(ExpressionNodeInfo info)
4949

5050
if (conversion.MethodSymbol != null)
5151
{
52-
bool convertedToDelegate = Type.IsDelegate(convertedType);
52+
bool convertedToDelegate = Entities.Type.IsDelegate(convertedType.Symbol);
5353

5454
if (convertedToDelegate)
5555
{
@@ -67,17 +67,17 @@ public static Expression Create(ExpressionNodeInfo info)
6767
return Factory.Create(info);
6868
}
6969

70-
if (resolvedType != null)
70+
if (resolvedType.Symbol != null)
7171
return new ImplicitCast(info, conversion.MethodSymbol);
7272
}
7373

7474
bool implicitUpcast = conversion.IsImplicit &&
75-
convertedType != null &&
75+
convertedType.Symbol != null &&
7676
!conversion.IsBoxing &&
7777
(
78-
resolvedType == null ||
78+
resolvedType.Symbol == null ||
7979
conversion.IsReference ||
80-
convertedType.SpecialType == SpecialType.System_Object)
80+
convertedType.Symbol.SpecialType == SpecialType.System_Object)
8181
;
8282

8383
if (!conversion.IsIdentity && !implicitUpcast)

0 commit comments

Comments
 (0)