Skip to content

Commit fa27eaa

Browse files
authored
Merge pull request #21309 from michaelnebel/csharp14/field
C# 14: Support the `field` keyword.
2 parents 94e3d86 + c3a1eb1 commit fa27eaa

File tree

11 files changed

+432
-2
lines changed

11 files changed

+432
-2
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,9 @@ internal static Expression Create(ExpressionNodeInfo info)
160160
case SyntaxKind.ThisExpression:
161161
return This.CreateExplicit(info);
162162

163+
case SyntaxKind.FieldExpression:
164+
return PropertyFieldAccess.Create(info);
165+
163166
case SyntaxKind.AddressOfExpression:
164167
return Unary.Create(info.SetKind(ExprKind.ADDRESS_OF));
165168

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System.IO;
2+
using Microsoft.CodeAnalysis;
3+
using Microsoft.CodeAnalysis.CSharp.Syntax;
4+
using Semmle.Extraction.Kinds;
5+
6+
namespace Semmle.Extraction.CSharp.Entities.Expressions
7+
{
8+
internal class PropertyFieldAccess : Expression<FieldExpressionSyntax>
9+
{
10+
private PropertyFieldAccess(ExpressionNodeInfo info) : base(info.SetKind(ExprKind.FIELD_ACCESS)) { }
11+
12+
public static Expression Create(ExpressionNodeInfo info) => new PropertyFieldAccess(info).TryPopulate();
13+
14+
protected override void PopulateExpression(TextWriter trapFile)
15+
{
16+
var symbolInfo = Context.GetSymbolInfo(Syntax);
17+
if (symbolInfo.Symbol is IFieldSymbol field)
18+
{
19+
var target = PropertyField.Create(Context, field);
20+
trapFile.expr_access(this, target);
21+
if (!field.IsStatic)
22+
{
23+
This.CreateImplicit(Context, field.ContainingType, Location, this, -1);
24+
}
25+
}
26+
}
27+
}
28+
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Semmle.Extraction.CSharp.Entities
1010
{
1111
internal class Field : CachedSymbol<IFieldSymbol>, IExpressionParentEntity
1212
{
13-
private Field(Context cx, IFieldSymbol init)
13+
protected Field(Context cx, IFieldSymbol init)
1414
: base(cx, init)
1515
{
1616
type = new Lazy<Type>(() => Entities.Type.Create(cx, Symbol.Type));
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
using System.IO;
2+
using Microsoft.CodeAnalysis;
3+
using Semmle.Extraction.CSharp.Util;
4+
using Semmle.Extraction.Kinds;
5+
6+
namespace Semmle.Extraction.CSharp.Entities
7+
{
8+
/// <summary>
9+
/// Represents the autogenerated backing field `field` for a property.
10+
/// It is only created for properties that use the `field` keyword in their getter or setter, and
11+
/// is not created for auto-properties.
12+
/// </summary>
13+
internal class PropertyField : Field
14+
{
15+
protected PropertyField(Context cx, IFieldSymbol init)
16+
: base(cx, init)
17+
{
18+
}
19+
20+
public static new PropertyField Create(Context cx, IFieldSymbol field) => PropertyFieldFactory.Instance.CreateEntity(cx, (field, field.AssociatedSymbol), field);
21+
22+
public override bool NeedsPopulation => true;
23+
24+
public override void Populate(TextWriter trapFile)
25+
{
26+
PopulateNullability(trapFile, Symbol.GetAnnotatedType());
27+
28+
var unboundFieldKey = PropertyField.Create(Context, Symbol.OriginalDefinition);
29+
var name = Symbol.AssociatedSymbol is not null ? $"{Symbol.AssociatedSymbol.GetName()}.field" : Symbol.Name;
30+
trapFile.fields(this, VariableKind.None, name, ContainingType!, Type.TypeRef, unboundFieldKey);
31+
trapFile.compiler_generated(this);
32+
33+
PopulateModifiers(trapFile);
34+
35+
if (Context.OnlyScaffold)
36+
{
37+
return;
38+
}
39+
40+
if (Context.ExtractLocation(Symbol))
41+
{
42+
WriteLocationsToTrap(trapFile.field_location, this, Locations);
43+
}
44+
}
45+
46+
private class PropertyFieldFactory : CachedEntityFactory<IFieldSymbol, PropertyField>
47+
{
48+
public static PropertyFieldFactory Instance { get; } = new PropertyFieldFactory();
49+
50+
public override PropertyField Create(Context cx, IFieldSymbol init) => new PropertyField(cx, init);
51+
}
52+
}
53+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* C# 14: Added support for the `field` keyword in properties.

csharp/ql/test/library-tests/dataflow/fields/D.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,65 @@ public static void Sink(object o) { }
8989

9090
static T Source<T>(object source) => throw null;
9191
}
92+
93+
public class DFieldProps
94+
{
95+
object FieldProp0
96+
{
97+
get { return field; }
98+
set { field = value; }
99+
} = Source<object>(0);
100+
101+
object FieldProp1
102+
{
103+
get { return field; }
104+
set { field = value; }
105+
}
106+
107+
object FieldProp2
108+
{
109+
get { return field; }
110+
set
111+
{
112+
var x = value;
113+
field = x;
114+
}
115+
}
116+
117+
static object StaticFieldProp
118+
{
119+
get { return field; }
120+
set { field = value; }
121+
}
122+
123+
private void M()
124+
{
125+
var d0 = new DFieldProps();
126+
Sink(d0.FieldProp0); // $ hasValueFlow=0
127+
Sink(d0.FieldProp1); // no flow
128+
Sink(d0.FieldProp2); // no flow
129+
130+
var d1 = new DFieldProps();
131+
var o1 = Source<object>(1);
132+
d1.FieldProp1 = o1;
133+
Sink(d1.FieldProp0); // $ hasValueFlow=0
134+
Sink(d1.FieldProp1); // $ hasValueFlow=1
135+
Sink(d1.FieldProp2); // no flow
136+
137+
var d2 = new DFieldProps();
138+
var o2 = Source<object>(2);
139+
d2.FieldProp2 = o2;
140+
Sink(d2.FieldProp0); // $ hasValueFlow=0
141+
Sink(d2.FieldProp1); // no flow
142+
Sink(d2.FieldProp2); // $ hasValueFlow=2
143+
144+
var o3 = Source<object>(3);
145+
DFieldProps.StaticFieldProp = o3;
146+
Sink(DFieldProps.StaticFieldProp); // $ hasValueFlow=3
147+
}
148+
149+
public static void Sink(object o) { }
150+
151+
static T Source<T>(object source) => throw null;
152+
153+
}

0 commit comments

Comments
 (0)