Skip to content

Commit 55f787b

Browse files
authored
Merge pull request #7605 from michaelnebel/csharp/record-struct
C#: Support for record structs
2 parents 994fcf5 + b927aad commit 55f787b

File tree

15 files changed

+169
-81
lines changed

15 files changed

+169
-81
lines changed

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,9 @@ public static void ExtractModifiers(Context cx, TextWriter trapFile, IEntity key
111111
if (nt is null)
112112
throw new InternalError(symbol, "Symbol kind is inconsistent with its type");
113113

114+
if (nt.IsRecord)
115+
HasModifier(cx, trapFile, key, "record");
116+
114117
if (nt.TypeKind == TypeKind.Struct)
115118
{
116119
if (nt.IsReadOnly)

csharp/ql/lib/semmle/code/cil/Types.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ class Class extends ValueOrRefType {
6161
}
6262

6363
/** A `record`. */
64-
class Record extends Class {
64+
deprecated class Record extends Class {
6565
Record() { this.isRecord() }
6666
}
6767

csharp/ql/lib/semmle/code/csharp/Type.qll

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,8 @@ class ValueOrRefType extends DotNet::ValueOrRefType, Type, Attributable, @value_
372372
nested_types(this, _, result)
373373
}
374374

375+
override predicate isRecord() { this.hasModifier("record") }
376+
375377
override string toString() { result = Type.super.toString() }
376378
}
377379

@@ -449,6 +451,14 @@ class SimpleType extends ValueType, @simple_type {
449451
override SystemNamespace getDeclaringNamespace() { any() }
450452
}
451453

454+
/**
455+
* A `record` like type.
456+
* This can be either a `class` or a `struct`.
457+
*/
458+
class RecordType extends ValueOrRefType {
459+
RecordType() { this.isRecord() }
460+
}
461+
452462
/**
453463
* The Boolean type, `bool`.
454464
*/
@@ -711,6 +721,18 @@ class Struct extends ValueType, @struct_type {
711721
override string getAPrimaryQlClass() { result = "Struct" }
712722
}
713723

724+
/**
725+
* A `record struct`, for example
726+
* ```csharp
727+
* record struct RS {
728+
* ...
729+
* }
730+
* ```
731+
*/
732+
class RecordStruct extends RecordType, Struct {
733+
override string getAPrimaryQlClass() { result = "RecordStruct" }
734+
}
735+
714736
/**
715737
* A reference type.
716738
*
@@ -765,6 +787,16 @@ class Class extends RefType, @class_type {
765787
override string getAPrimaryQlClass() { result = "Class" }
766788
}
767789

790+
/**
791+
* DEPRECATED: Use `RecordClass` instead.
792+
*/
793+
deprecated class Record extends Class {
794+
Record() { this.isRecord() }
795+
796+
/** Gets the clone method of this record. */
797+
RecordCloneMethod getCloneMethod() { result = this.getAMember() }
798+
}
799+
768800
/**
769801
* A `record`, for example
770802
*
@@ -774,13 +806,11 @@ class Class extends RefType, @class_type {
774806
* }
775807
* ```
776808
*/
777-
class Record extends Class {
778-
Record() { this.isRecord() }
779-
809+
class RecordClass extends RecordType, Class {
780810
/** Gets the clone method of this record. */
781811
RecordCloneMethod getCloneMethod() { result = this.getAMember() }
782812

783-
override string getAPrimaryQlClass() { result = "Record" }
813+
override string getAPrimaryQlClass() { result = "RecordClass" }
784814
}
785815

786816
/**

csharp/ql/lib/semmle/code/csharp/dataflow/FlowSummary.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ module SummaryComponentStack {
111111
class SummarizedCallable = Impl::Public::SummarizedCallable;
112112

113113
private predicate recordConstructorFlow(Constructor c, int i, Property p) {
114-
c = any(Record r).getAMember() and
114+
c = any(RecordType r).getAMember() and
115115
exists(string name |
116116
c.getParameter(i).getName() = name and
117117
c.getDeclaringType().getAMember(name) = p

csharp/ql/lib/semmle/code/csharp/exprs/Expr.qll

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1175,7 +1175,9 @@ class WithExpr extends Expr, @with_expr {
11751175
Expr getExpr() { result = this.getChild(0) }
11761176

11771177
/** Gets the clone method of the `record` that is targetted by this `with` expression. */
1178-
RecordCloneMethod getCloneMethod() { result = this.getExpr().getType().(Record).getCloneMethod() }
1178+
RecordCloneMethod getCloneMethod() {
1179+
result = this.getExpr().getType().(RecordClass).getCloneMethod()
1180+
}
11791181

11801182
override string toString() { result = "... with { ... }" }
11811183

csharp/ql/test/library-tests/csharp10/RecordTypeSealedToString.cs

Lines changed: 0 additions & 6 deletions
This file was deleted.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
3+
public record MyEntry(string Name, string Address)
4+
{
5+
sealed public override string ToString() => $"{Name} lives at {Address}";
6+
};
7+
8+
public record class MyClassRecord(DateTime stuff) { }
9+
10+
public readonly record struct MyReadonlyRecordStruct(string Stuff) { }
11+
12+
public record struct MyRecordStruct(int Stuff) { }
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
| RecordTypeSealedToString.cs:5:35:5:42 | ToString |
1+
| RecordTypes.cs:5:35:5:42 | ToString |
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
recordTypes
2+
| RecordTypes.cs:3:1:6:2 | MyEntry |
3+
| RecordTypes.cs:8:1:8:53 | MyClassRecord |
4+
| RecordTypes.cs:10:1:10:70 | MyReadonlyRecordStruct |
5+
| RecordTypes.cs:12:1:12:50 | MyRecordStruct |
6+
recordStructs
7+
| RecordTypes.cs:10:1:10:70 | MyReadonlyRecordStruct |
8+
| RecordTypes.cs:12:1:12:50 | MyRecordStruct |
9+
recordClass
10+
| RecordTypes.cs:3:1:6:2 | MyEntry |
11+
| RecordTypes.cs:8:1:8:53 | MyClassRecord |
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import csharp
2+
3+
query predicate recordTypes(RecordType rt) { any() }
4+
5+
query predicate recordStructs(RecordStruct rs) { any() }
6+
7+
query predicate recordClass(RecordClass r) { any() }

0 commit comments

Comments
 (0)