Skip to content

Commit 85f6e5f

Browse files
authored
Merge pull request #2450 from calumgrant/cs/expr-nullability
C#: Expression nullability
2 parents 3072e9c + bc1b2c3 commit 85f6e5f

30 files changed

+9467
-5187
lines changed

change-notes/1.24/analysis-csharp.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@ The following changes in version 1.24 affect C# analysis in all applications.
2020
## Changes to code extraction
2121

2222
* Tuple expressions, for example `(int,bool)` in `default((int,bool))` are now extracted correctly.
23+
* Expression nullability flow state is extracted.
2324

2425
## Changes to libraries
2526

2627
* The taint tracking library now tracks flow through (implicit or explicit) conversion operator calls.
2728
* [Code contracts](https://docs.microsoft.com/en-us/dotnet/framework/debug-trace-profile/code-contracts) are now recognized, and are treated like any other assertion methods.
29+
* Expression nullability flow state is given by the predicates `Expr.hasNotNullFlowState()` and `Expr.hasMaybeNullFlowState()`.
2830

2931
## Changes to autobuilder
3032

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

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

6060
var initInfo = new ExpressionInfo(Context,
61-
new AnnotatedType(initializerType, Kinds.TypeAnnotation.NotAnnotated),
61+
new AnnotatedType(initializerType, NullableAnnotation.None),
6262
Context.Create(initializer.ThisOrBaseKeyword.GetLocation()),
6363
Kinds.ExprKind.CONSTRUCTOR_INIT,
6464
this,

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

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,18 @@ protected sealed override void Populate(TextWriter trapFile)
4646
trapFile.expr_parent(this, Info.Child, Info.Parent);
4747
trapFile.expr_location(this, Location);
4848

49+
var annotatedType = Type.Symbol;
50+
if (!annotatedType.HasObliviousNullability())
51+
{
52+
var n = NullabilityEntity.Create(cx, Nullability.Create(annotatedType));
53+
trapFile.type_nullability(this, n);
54+
}
55+
56+
if(Info.FlowState != NullableFlowState.None)
57+
{
58+
trapFile.expr_flowstate(this, (int)Info.FlowState);
59+
}
60+
4961
if (Info.IsCompilerGenerated)
5062
trapFile.expr_compiler_generated(this);
5163

@@ -280,7 +292,7 @@ abstract class Expression<SyntaxNode> : Expression
280292
protected Expression(ExpressionNodeInfo info)
281293
: base(info)
282294
{
283-
Syntax = (SyntaxNode)info.Node;
295+
Syntax = (SyntaxNode)info.Node;
284296
}
285297

286298
/// <summary>
@@ -344,6 +356,8 @@ interface IExpressionInfo
344356
/// is null.
345357
/// </summary>
346358
string ExprValue { get; }
359+
360+
NullableFlowState FlowState { get; }
347361
}
348362

349363
/// <summary>
@@ -360,7 +374,8 @@ class ExpressionInfo : IExpressionInfo
360374
public bool IsCompilerGenerated { get; }
361375
public string ExprValue { get; }
362376

363-
public ExpressionInfo(Context cx, AnnotatedType type, Extraction.Entities.Location location, ExprKind kind, IExpressionParentEntity parent, int child, bool isCompilerGenerated, string value)
377+
public ExpressionInfo(Context cx, AnnotatedType type, Extraction.Entities.Location location, ExprKind kind,
378+
IExpressionParentEntity parent, int child, bool isCompilerGenerated, string value)
364379
{
365380
Context = cx;
366381
Type = type;
@@ -371,6 +386,9 @@ public ExpressionInfo(Context cx, AnnotatedType type, Extraction.Entities.Locati
371386
ExprValue = value;
372387
IsCompilerGenerated = isCompilerGenerated;
373388
}
389+
390+
// Synthetic expressions don't have a flow state.
391+
public NullableFlowState FlowState => NullableFlowState.None;
374392
}
375393

376394
/// <summary>
@@ -533,5 +551,7 @@ public SymbolInfo SymbolInfo
533551
return cachedSymbolInfo;
534552
}
535553
}
554+
555+
public NullableFlowState FlowState => TypeInfo.Nullability.FlowState;
536556
}
537557
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ protected override void PopulateExpression(TextWriter trapFile)
4343

4444
var info = new ExpressionInfo(
4545
cx,
46-
new AnnotatedType(Entities.Type.Create(cx, cx.Compilation.GetSpecialType(Microsoft.CodeAnalysis.SpecialType.System_Int32)), Kinds.TypeAnnotation.NotAnnotated),
46+
new AnnotatedType(Entities.Type.Create(cx, cx.Compilation.GetSpecialType(Microsoft.CodeAnalysis.SpecialType.System_Int32)), NullableAnnotation.None),
4747
Location,
4848
ExprKind.INT_LITERAL,
4949
this,

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ private MemberAccess(ExpressionNodeInfo info, ExpressionSyntax qualifier, ISymbo
3030
public static Expression Create(ExpressionNodeInfo info, ConditionalAccessExpressionSyntax node) =>
3131
// The qualifier is located by walking the syntax tree.
3232
// `node.WhenNotNull` will contain a MemberBindingExpressionSyntax, calling the method below.
33-
Create(info.Context, node.WhenNotNull, info.Parent, info.Child);
33+
CreateFromNode(new ExpressionNodeInfo(info.Context, node.WhenNotNull, info.Parent, info.Child, info.TypeInfo));
3434

3535
public static Expression Create(ExpressionNodeInfo info, MemberBindingExpressionSyntax node)
3636
{

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Semmle.Extraction.Entities;
1+
using Microsoft.CodeAnalysis;
22
using Semmle.Extraction.Kinds;
33

44
namespace Semmle.Extraction.CSharp.Entities.Expressions
@@ -7,8 +7,8 @@ class This : Expression
77
{
88
This(IExpressionInfo info) : base(info) { }
99

10-
public static This CreateImplicit(Context cx, Type @class, Location loc, IExpressionParentEntity parent, int child) =>
11-
new This(new ExpressionInfo(cx, new AnnotatedType(@class, Kinds.TypeAnnotation.NotAnnotated), loc, Kinds.ExprKind.THIS_ACCESS, parent, child, true, null));
10+
public static This CreateImplicit(Context cx, Type @class, Extraction.Entities.Location loc, IExpressionParentEntity parent, int child) =>
11+
new This(new ExpressionInfo(cx, new AnnotatedType(@class, NullableAnnotation.None), loc, Kinds.ExprKind.THIS_ACCESS, parent, child, true, null));
1212

1313
public static This CreateExplicit(ExpressionNodeInfo info) => new This(info.SetKind(ExprKind.THIS_ACCESS));
1414
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ public override void Populate(TextWriter trapFile)
7575
Context.PopulateLater(() =>
7676
{
7777
var loc = Context.Create(initializer.GetLocation());
78-
var annotatedType = new AnnotatedType(type, TypeAnnotation.None);
78+
var annotatedType = new AnnotatedType(type, NullableAnnotation.None);
7979
var simpleAssignExpr = new Expression(new ExpressionInfo(Context, annotatedType, loc, ExprKind.SIMPLE_ASSIGN, this, child++, false, null));
8080
Expression.CreateFromNode(new ExpressionNodeInfo(Context, initializer.Value, simpleAssignExpr, 0));
8181
var access = new Expression(new ExpressionInfo(Context, annotatedType, Location, ExprKind.PROPERTY_ACCESS, simpleAssignExpr, 1, false, null));

csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/NullType.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ public override bool Equals(object obj)
2727
return obj != null && obj.GetType() == typeof(NullType);
2828
}
2929

30-
public static AnnotatedType Create(Context cx) => new AnnotatedType(NullTypeFactory.Instance.CreateEntity(cx, null), Kinds.TypeAnnotation.None);
30+
public static AnnotatedType Create(Context cx) => new AnnotatedType(NullTypeFactory.Instance.CreateEntity(cx, null), NullableAnnotation.None);
3131

3232
class NullTypeFactory : ICachedEntityFactory<ITypeSymbol, NullType>
3333
{

csharp/extractor/Semmle.Extraction.CSharp/Entities/Types/Type.cs

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
using Microsoft.CodeAnalysis;
22
using Microsoft.CodeAnalysis.CSharp.Syntax;
3-
using Semmle.Extraction.CSharp.Populators;
43
using Semmle.Util;
5-
using System;
64
using System.Collections.Generic;
75
using System.IO;
86
using System.Linq;
@@ -14,14 +12,23 @@ namespace Semmle.Extraction.CSharp.Entities
1412
/// </summary>
1513
public struct AnnotatedType
1614
{
17-
public AnnotatedType(Type t, Kinds.TypeAnnotation a)
15+
public AnnotatedType(Type t, NullableAnnotation n)
1816
{
1917
Type = t;
20-
Annotation = a;
18+
annotation = n;
2119
}
2220

21+
/// <summary>
22+
/// The underlying type.
23+
/// </summary>
2324
public Type Type;
24-
public Kinds.TypeAnnotation Annotation;
25+
26+
readonly private NullableAnnotation annotation;
27+
28+
/// <summary>
29+
/// Gets the annotated type symbol of this annotated type.
30+
/// </summary>
31+
public AnnotatedTypeSymbol Symbol => new AnnotatedTypeSymbol(Type.symbol, annotation);
2532
}
2633

2734
public abstract class Type : CachedSymbol<ITypeSymbol>
@@ -108,7 +115,7 @@ protected void PopulateType(TextWriter trapFile)
108115

109116
if (!(base.symbol is IArrayTypeSymbol))
110117
{
111-
foreach (var t in base.symbol.Interfaces.Select(i=>Create(Context, i)))
118+
foreach (var t in base.symbol.Interfaces.Select(i => Create(Context, i)))
112119
{
113120
trapFile.implement(this, t.TypeRef);
114121
baseTypes.Add(t);
@@ -274,7 +281,7 @@ public static Type Create(Context cx, ITypeSymbol type)
274281
}
275282

276283
public static AnnotatedType Create(Context cx, AnnotatedTypeSymbol type) =>
277-
new AnnotatedType(Create(cx, type.Symbol), type.Nullability.GetTypeAnnotation());
284+
new AnnotatedType(Create(cx, type.Symbol), type.Nullability);
278285

279286
public virtual int Dimension => 0;
280287

csharp/extractor/Semmle.Extraction.CSharp/Tuples.cs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -201,16 +201,6 @@ internal static void explicitly_sized_array_creation(this TextWriter trapFile, E
201201
trapFile.WriteTuple("explicitly_sized_array_creation", array);
202202
}
203203

204-
internal static void expr_compiler_generated(this TextWriter trapFile, Expression expr)
205-
{
206-
trapFile.WriteTuple("expr_compiler_generated", expr);
207-
}
208-
209-
internal static void expr_location(this TextWriter trapFile, Expression exprKey, Location location)
210-
{
211-
trapFile.WriteTuple("expr_location", exprKey, location);
212-
}
213-
214204
internal static void expr_access(this TextWriter trapFile, Expression expr, IEntity access)
215205
{
216206
trapFile.WriteTuple("expr_access", expr, access);
@@ -231,19 +221,34 @@ internal static void expr_call(this TextWriter trapFile, Expression expr, Method
231221
trapFile.WriteTuple("expr_call", expr, target);
232222
}
233223

234-
internal static void expr_parent(this TextWriter trapFile, Expression exprKey, int child, IExpressionParentEntity parent)
224+
internal static void expr_compiler_generated(this TextWriter trapFile, Expression expr)
225+
{
226+
trapFile.WriteTuple("expr_compiler_generated", expr);
227+
}
228+
229+
internal static void expr_flowstate(this TextWriter trapFile, Expression expr, int flowState)
230+
{
231+
trapFile.WriteTuple("expr_flowstate", expr, flowState);
232+
}
233+
234+
internal static void expr_location(this TextWriter trapFile, Expression expr, Location location)
235+
{
236+
trapFile.WriteTuple("expr_location", expr, location);
237+
}
238+
239+
internal static void expr_parent(this TextWriter trapFile, Expression expr, int child, IExpressionParentEntity parent)
235240
{
236-
trapFile.WriteTuple("expr_parent", exprKey, child, parent);
241+
trapFile.WriteTuple("expr_parent", expr, child, parent);
237242
}
238243

239-
internal static void expr_parent_top_level(this TextWriter trapFile, Expression exprKey, int child, IExpressionParentEntity parent)
244+
internal static void expr_parent_top_level(this TextWriter trapFile, Expression expr, int child, IExpressionParentEntity parent)
240245
{
241-
trapFile.WriteTuple("expr_parent_top_level", exprKey, child, parent);
246+
trapFile.WriteTuple("expr_parent_top_level", expr, child, parent);
242247
}
243248

244-
internal static void expr_value(this TextWriter trapFile, Expression exprKey, string value)
249+
internal static void expr_value(this TextWriter trapFile, Expression expr, string value)
245250
{
246-
trapFile.WriteTuple("expr_value", exprKey, value);
251+
trapFile.WriteTuple("expr_value", expr, value);
247252
}
248253

249254
internal static void expressions(this TextWriter trapFile, Expression expr, ExprKind kind, Type exprType)

0 commit comments

Comments
 (0)