Skip to content

Commit f0b0845

Browse files
committed
Add 'record' QL class
1 parent 9ffc38f commit f0b0845

9 files changed

Lines changed: 86 additions & 42 deletions

File tree

csharp/ql/src/semmle/code/cil/Declaration.qll

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -51,30 +51,23 @@ private predicate toCSharpTypeParameterJoin(TypeParameter tp, int i, CS::Unbound
5151
* A member of a type. Either a type (`Type`), a method (`Method`), a property (`Property`), or an event (`Event`).
5252
*/
5353
class Member extends DotNet::Member, Declaration, @cil_member {
54-
/** Holds if this member is declared `public`. */
55-
predicate isPublic() { cil_public(this) }
54+
override predicate isPublic() { cil_public(this) }
5655

57-
/** Holds if this member is declared `protected.` */
58-
predicate isProtected() { cil_protected(this) }
56+
override predicate isProtected() { cil_protected(this) }
5957

60-
/** Holds if this member is `private`. */
61-
predicate isPrivate() { cil_private(this) }
58+
override predicate isPrivate() { cil_private(this) }
6259

63-
/** Holds if this member is `internal`. */
64-
predicate isInternal() { cil_internal(this) }
60+
override predicate isInternal() { cil_internal(this) }
6561

66-
/** Holds if this member is `sealed`. */
67-
predicate isSealed() { cil_sealed(this) }
62+
override predicate isSealed() { cil_sealed(this) }
6863

69-
/** Holds if this member is `abstract`. */
70-
predicate isAbstract() { cil_abstract(this) }
64+
override predicate isAbstract() { cil_abstract(this) }
65+
66+
override predicate isStatic() { cil_static(this) }
7167

7268
/** Holds if this member has a security attribute. */
7369
predicate hasSecurity() { cil_security(this) }
7470

75-
/** Holds if this member is `static`. */
76-
predicate isStatic() { cil_static(this) }
77-
7871
override Location getLocation() { result = getDeclaringType().getLocation() }
7972
}
8073

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,11 @@ class Class extends ValueOrRefType {
6060
Class() { this.isClass() }
6161
}
6262

63+
/** A `record`. */
64+
class Record extends Class {
65+
Record() { this.isRecord() }
66+
}
67+
6368
/** An `interface`. */
6469
class Interface extends ValueOrRefType {
6570
Interface() { this.isInterface() }

csharp/ql/src/semmle/code/csharp/Member.qll

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,20 @@ class Modifiable extends Declaration, @modifiable {
120120
class Member extends DotNet::Member, Modifiable, @member {
121121
/** Gets an access to this member. */
122122
MemberAccess getAnAccess() { result.getTarget() = this }
123+
124+
override predicate isPublic() { Modifiable.super.isPublic() }
125+
126+
override predicate isProtected() { Modifiable.super.isProtected() }
127+
128+
override predicate isPrivate() { Modifiable.super.isPrivate() }
129+
130+
override predicate isInternal() { Modifiable.super.isInternal() }
131+
132+
override predicate isSealed() { Modifiable.super.isSealed() }
133+
134+
override predicate isAbstract() { Modifiable.super.isAbstract() }
135+
136+
override predicate isStatic() { Modifiable.super.isStatic() }
123137
}
124138

125139
/**

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -736,6 +736,21 @@ class Class extends RefType, @class_type {
736736
override string getAPrimaryQlClass() { result = "Class" }
737737
}
738738

739+
/**
740+
* A `record`, for example
741+
*
742+
* ```csharp
743+
* record R(object Prop) {
744+
* ...
745+
* }
746+
* ```
747+
*/
748+
class Record extends Class {
749+
Record() { this.isRecord() }
750+
751+
override string getAPrimaryQlClass() { result = "Record" }
752+
}
753+
739754
/**
740755
* A class generated by the compiler from an anonymous object creation.
741756
*

csharp/ql/src/semmle/code/dotnet/Declaration.qll

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,28 @@ class Declaration extends NamedElement, @dotnet_declaration {
5858
}
5959

6060
/** A member of a type. */
61-
class Member extends Declaration, @dotnet_member { }
61+
class Member extends Declaration, @dotnet_member {
62+
/** Holds if this member is declared `public`. */
63+
predicate isPublic() { none() }
64+
65+
/** Holds if this member is declared `protected.` */
66+
predicate isProtected() { none() }
67+
68+
/** Holds if this member is `private`. */
69+
predicate isPrivate() { none() }
70+
71+
/** Holds if this member is `internal`. */
72+
predicate isInternal() { none() }
73+
74+
/** Holds if this member is `sealed`. */
75+
predicate isSealed() { none() }
76+
77+
/** Holds if this member is `abstract`. */
78+
predicate isAbstract() { none() }
79+
80+
/** Holds if this member is `static`. */
81+
predicate isStatic() { none() }
82+
}
6283

6384
/** A property. */
6485
class Property extends Member, @dotnet_property {

csharp/ql/src/semmle/code/dotnet/Type.qll

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,18 @@ class ValueOrRefType extends Type, @dotnet_valueorreftype {
4949

5050
/** Gets a base type of this type, if any. */
5151
ValueOrRefType getABaseType() { none() }
52+
53+
/** Holds if this type is a `record`. */
54+
predicate isRecord() {
55+
exists(Callable c |
56+
c.getDeclaringType() = this and
57+
c.hasName("<Clone>$") and
58+
c.getNumberOfParameters() = 0 and
59+
c.getReturnType() = this.getABaseType*() and
60+
c.(Member).isPublic() and
61+
not c.(Member).isStatic()
62+
)
63+
}
5264
}
5365

5466
/**

csharp/ql/test/library-tests/csharp9/PrintAst.expected

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -658,7 +658,7 @@ ParenthesizedPattern.cs:
658658
# 27| 0: [TypeMention] string
659659
# 27| 2: [IntLiteral] 5
660660
Record.cs:
661-
# 4| [Class] Person
661+
# 4| [Record] Person
662662
# 4| 11: [NEOperator] !=
663663
#-----| 2: (Parameters)
664664
# 4| 0: [Parameter] r1
@@ -688,7 +688,7 @@ Record.cs:
688688
# 9| 1: [TupleExpr] (..., ...)
689689
# 9| 0: [ParameterAccess] access to parameter first
690690
# 9| 1: [ParameterAccess] access to parameter last
691-
# 12| [Class] Teacher
691+
# 12| [Record] Teacher
692692
# 12| 12: [NEOperator] !=
693693
#-----| 2: (Parameters)
694694
# 12| 0: [Parameter] r1
@@ -716,7 +716,7 @@ Record.cs:
716716
# 17| 4: [AssignExpr] ... = ...
717717
# 17| 0: [PropertyCall] access to property Subject
718718
# 17| 1: [ParameterAccess] access to parameter sub
719-
# 20| [Class] Student
719+
# 20| [Record] Student
720720
# 20| 12: [NEOperator] !=
721721
#-----| 2: (Parameters)
722722
# 20| 0: [Parameter] r1
@@ -744,7 +744,7 @@ Record.cs:
744744
# 24| 4: [AssignExpr] ... = ...
745745
# 24| 0: [PropertyCall] access to property Level
746746
# 24| 1: [ParameterAccess] access to parameter level
747-
# 27| [Class] Person1
747+
# 27| [Record] Person1
748748
# 27| 12: [NEOperator] !=
749749
#-----| 2: (Parameters)
750750
# 27| 0: [Parameter] r1
@@ -771,7 +771,7 @@ Record.cs:
771771
# 27| 4: [Setter] set_LastName
772772
#-----| 2: (Parameters)
773773
# 27| 0: [Parameter] value
774-
# 29| [Class] Teacher1
774+
# 29| [Record] Teacher1
775775
# 29| 13: [NEOperator] !=
776776
#-----| 2: (Parameters)
777777
# 29| 0: [Parameter] r1
@@ -795,7 +795,7 @@ Record.cs:
795795
# 29| 4: [Setter] set_Subject
796796
#-----| 2: (Parameters)
797797
# 29| 0: [Parameter] value
798-
# 32| [Class] Student1
798+
# 32| [Record] Student1
799799
# 32| 13: [NEOperator] !=
800800
#-----| 2: (Parameters)
801801
# 32| 0: [Parameter] r1
@@ -819,7 +819,7 @@ Record.cs:
819819
# 32| 4: [Setter] set_Level
820820
#-----| 2: (Parameters)
821821
# 32| 0: [Parameter] value
822-
# 35| [Class] Pet
822+
# 35| [Record] Pet
823823
# 35| 12: [NEOperator] !=
824824
#-----| 2: (Parameters)
825825
# 35| 0: [Parameter] r1
@@ -845,7 +845,7 @@ Record.cs:
845845
# 38| -1: [TypeAccess] access to type Console
846846
# 38| 0: [TypeMention] Console
847847
# 38| 0: [StringLiteral] "Shredding furniture"
848-
# 41| [Class] Dog
848+
# 41| [Record] Dog
849849
# 41| 12: [NEOperator] !=
850850
#-----| 2: (Parameters)
851851
# 41| 0: [Parameter] r1

csharp/ql/test/library-tests/csharp9/record.expected

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -198,15 +198,3 @@ members
198198
| Record.cs:41:1:52:1 | Dog | System.Object.Object() | no location |
199199
| Record.cs:41:1:52:1 | Dog | System.Object.ReferenceEquals(object, object) | no location |
200200
| Record.cs:41:1:52:1 | Dog | System.Object.~Object() | no location |
201-
| Record.cs:54:14:54:20 | Record1 | Record1.M1() | Record.cs:56:17:56:18 |
202-
| Record.cs:54:14:54:20 | Record1 | Record1.M2() | Record.cs:64:17:64:18 |
203-
| Record.cs:54:14:54:20 | Record1 | Record1.Record1() | no location |
204-
| Record.cs:54:14:54:20 | Record1 | System.Object.Equals(object) | no location |
205-
| Record.cs:54:14:54:20 | Record1 | System.Object.Equals(object, object) | no location |
206-
| Record.cs:54:14:54:20 | Record1 | System.Object.GetHashCode() | no location |
207-
| Record.cs:54:14:54:20 | Record1 | System.Object.GetType() | no location |
208-
| Record.cs:54:14:54:20 | Record1 | System.Object.MemberwiseClone() | no location |
209-
| Record.cs:54:14:54:20 | Record1 | System.Object.Object() | no location |
210-
| Record.cs:54:14:54:20 | Record1 | System.Object.ReferenceEquals(object, object) | no location |
211-
| Record.cs:54:14:54:20 | Record1 | System.Object.ToString() | no location |
212-
| Record.cs:54:14:54:20 | Record1 | System.Object.~Object() | no location |

csharp/ql/test/library-tests/csharp9/record.ql

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
import csharp
22

3-
query predicate types(Class t, string i) {
4-
t.getFile().getStem() = "Record" and
5-
t.getABaseInterface().toStringWithTypes() = i
6-
}
3+
query predicate types(Record t, string i) { t.getABaseInterface().toStringWithTypes() = i }
74

85
private string getMemberName(Member m) {
96
result = m.getDeclaringType().getQualifiedName() + "." + m.toStringWithTypes()
107
}
118

12-
query predicate members(Class t, string ms, string l) {
13-
t.getFile().getStem() = "Record" and
9+
query predicate members(Record t, string ms, string l) {
1410
exists(Member m | t.hasMember(m) |
1511
ms = getMemberName(m) and
1612
if m.fromSource() then l = m.getLocation().toString() else l = "no location"

0 commit comments

Comments
 (0)