Skip to content

Commit 2f6ffdd

Browse files
authored
Merge pull request github#12207 from michaelnebel/csharp/requiredmembers
C# 11: Required fields and properties.
2 parents ff9e738 + b87de91 commit 2f6ffdd

File tree

9 files changed

+173
-2
lines changed

9 files changed

+173
-2
lines changed

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

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,15 @@ public static void HasModifier(Context cx, TextWriter trapFile, IEntity target,
6565
trapFile.has_modifiers(target, Modifier.Create(cx, modifier));
6666
}
6767

68+
private static void ExtractFieldModifiers(Context cx, TextWriter trapFile, IEntity key, IFieldSymbol symbol)
69+
{
70+
if (symbol.IsReadOnly)
71+
HasModifier(cx, trapFile, key, Modifiers.Readonly);
72+
73+
if (symbol.IsRequired)
74+
HasModifier(cx, trapFile, key, Modifiers.Required);
75+
}
76+
6877
private static void ExtractNamedTypeModifiers(Context cx, TextWriter trapFile, IEntity key, ISymbol symbol)
6978
{
7079
if (symbol.Kind != SymbolKind.NamedType)
@@ -106,8 +115,11 @@ public static void ExtractModifiers(Context cx, TextWriter trapFile, IEntity key
106115
if (symbol.IsVirtual)
107116
HasModifier(cx, trapFile, key, Modifiers.Virtual);
108117

109-
if (symbol.Kind == SymbolKind.Field && ((IFieldSymbol)symbol).IsReadOnly)
110-
HasModifier(cx, trapFile, key, Modifiers.Readonly);
118+
if (symbol is IFieldSymbol field)
119+
ExtractFieldModifiers(cx, trapFile, key, field);
120+
121+
if (symbol.Kind == SymbolKind.Property && ((IPropertySymbol)symbol).IsRequired)
122+
HasModifier(cx, trapFile, key, Modifiers.Required);
111123

112124
if (symbol.IsOverride)
113125
HasModifier(cx, trapFile, key, Modifiers.Override);

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ internal static class Modifiers
1313
public const string Public = "public";
1414
public const string Readonly = "readonly";
1515
public const string Record = "record";
16+
public const string Required = "required";
1617
public const string Ref = "ref";
1718
public const string Sealed = "sealed";
1819
public const string Static = "static";
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# 11: Added extractor support for `required` fields and properties.

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@ class Modifiable extends Declaration, @modifiable {
9090
/** Holds if this declaration is `const`. */
9191
predicate isConst() { this.hasModifier("const") }
9292

93+
/** Holds if this declaration has the modifier `required`. */
94+
predicate isRequired() { this.hasModifier("required") }
95+
9396
/** Holds if this declaration is `unsafe`. */
9497
predicate isUnsafe() {
9598
this.hasModifier("unsafe") or
@@ -178,6 +181,8 @@ class Member extends DotNet::Member, Modifiable, @member {
178181
override predicate isAbstract() { Modifiable.super.isAbstract() }
179182

180183
override predicate isStatic() { Modifiable.super.isStatic() }
184+
185+
override predicate isRequired() { Modifiable.super.isRequired() }
181186
}
182187

183188
private class TOverridable = @virtualizable or @callable_accessor;

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ class Member extends Declaration, @dotnet_member {
8080
/** Holds if this member is `static`. */
8181
predicate isStatic() { none() }
8282

83+
/** Holds if this member is declared `required`. */
84+
predicate isRequired() { none() }
85+
8386
/**
8487
* Holds if this member has name `name` and is defined in type `type`
8588
* with namespace `namespace`.

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

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -657,6 +657,97 @@ RelaxedShift.cs:
657657
# 30| 1: [OperatorCall] call to operator >>>
658658
# 30| 0: [LocalVariableAccess] access to local variable n31
659659
# 30| 1: [StringLiteralUtf16] "3"
660+
RequiredMembers.cs:
661+
# 4| [Class] ClassRequiredMembers
662+
# 6| 4: [Field] RequiredField
663+
# 6| -1: [TypeMention] object
664+
# 7| 5: [Property] RequiredProperty
665+
# 7| -1: [TypeMention] string
666+
# 7| 3: [Getter] get_RequiredProperty
667+
# 7| 4: [Setter] set_RequiredProperty
668+
#-----| 2: (Parameters)
669+
# 7| 0: [Parameter] value
670+
# 8| 6: [Property] VirtualProperty
671+
# 8| -1: [TypeMention] object
672+
# 8| 3: [Getter] get_VirtualProperty
673+
# 8| 4: [Setter] set_VirtualProperty
674+
#-----| 2: (Parameters)
675+
# 8| 0: [Parameter] value
676+
# 10| 7: [InstanceConstructor] ClassRequiredMembers
677+
# 10| 4: [BlockStmt] {...}
678+
# 13| 8: [InstanceConstructor] ClassRequiredMembers
679+
#-----| 0: (Attributes)
680+
# 12| 1: [DefaultAttribute] [SetsRequiredMembers(...)]
681+
# 12| 0: [TypeMention] SetsRequiredMembersAttribute
682+
#-----| 2: (Parameters)
683+
# 13| 0: [Parameter] requiredField
684+
# 13| -1: [TypeMention] object
685+
# 13| 1: [Parameter] requiredProperty
686+
# 13| -1: [TypeMention] string
687+
# 14| 4: [BlockStmt] {...}
688+
# 15| 0: [ExprStmt] ...;
689+
# 15| 0: [AssignExpr] ... = ...
690+
# 15| 0: [FieldAccess] access to field RequiredField
691+
# 15| 1: [ParameterAccess] access to parameter requiredField
692+
# 16| 1: [ExprStmt] ...;
693+
# 16| 0: [AssignExpr] ... = ...
694+
# 16| 0: [PropertyCall] access to property RequiredProperty
695+
# 16| 1: [ParameterAccess] access to parameter requiredProperty
696+
# 20| [Class] ClassRequiredMembersSub
697+
#-----| 3: (Base types)
698+
# 20| 0: [TypeMention] ClassRequiredMembers
699+
# 22| 4: [Property] VirtualProperty
700+
# 22| -1: [TypeMention] object
701+
# 22| 3: [Getter] get_VirtualProperty
702+
# 22| 4: [Setter] set_VirtualProperty
703+
#-----| 2: (Parameters)
704+
# 22| 0: [Parameter] value
705+
# 24| 5: [InstanceConstructor] ClassRequiredMembersSub
706+
# 24| 3: [ConstructorInitializer] call to constructor ClassRequiredMembers
707+
# 24| 4: [BlockStmt] {...}
708+
# 27| 6: [InstanceConstructor] ClassRequiredMembersSub
709+
#-----| 0: (Attributes)
710+
# 26| 1: [DefaultAttribute] [SetsRequiredMembers(...)]
711+
# 26| 0: [TypeMention] SetsRequiredMembersAttribute
712+
#-----| 2: (Parameters)
713+
# 27| 0: [Parameter] requiredField
714+
# 27| -1: [TypeMention] object
715+
# 27| 1: [Parameter] requiredProperty
716+
# 27| -1: [TypeMention] string
717+
# 27| 2: [Parameter] virtualProperty
718+
# 27| -1: [TypeMention] object
719+
# 27| 3: [ConstructorInitializer] call to constructor ClassRequiredMembers
720+
# 27| 0: [ParameterAccess] access to parameter requiredField
721+
# 27| 1: [ParameterAccess] access to parameter requiredProperty
722+
# 28| 4: [BlockStmt] {...}
723+
# 29| 0: [ExprStmt] ...;
724+
# 29| 0: [AssignExpr] ... = ...
725+
# 29| 0: [PropertyCall] access to property VirtualProperty
726+
# 29| 1: [ParameterAccess] access to parameter virtualProperty
727+
# 33| [RecordClass] RecordRequiredMembers
728+
# 33| 12: [NEOperator] !=
729+
#-----| 2: (Parameters)
730+
# 33| 0: [Parameter] left
731+
# 33| 1: [Parameter] right
732+
# 33| 13: [EQOperator] ==
733+
#-----| 2: (Parameters)
734+
# 33| 0: [Parameter] left
735+
# 33| 1: [Parameter] right
736+
# 33| 14: [Property] EqualityContract
737+
# 33| 3: [Getter] get_EqualityContract
738+
# 35| 15: [Property] X
739+
# 35| -1: [TypeMention] object
740+
# 35| 3: [Getter] get_X
741+
# 35| 4: [Setter] set_X
742+
#-----| 2: (Parameters)
743+
# 35| 0: [Parameter] value
744+
# 38| [Struct] StructRequiredMembers
745+
# 40| 5: [Property] Y
746+
# 40| -1: [TypeMention] string
747+
# 40| 3: [Getter] get_Y
748+
# 40| 4: [Setter] set_Y
749+
#-----| 2: (Parameters)
750+
# 40| 0: [Parameter] value
660751
Scoped.cs:
661752
# 1| [Struct] S1
662753
# 2| [Struct] S2
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
using System;
2+
using System.Diagnostics.CodeAnalysis;
3+
4+
public class ClassRequiredMembers
5+
{
6+
public required object? RequiredField;
7+
public required string? RequiredProperty { get; init; }
8+
public virtual object? VirtualProperty { get; init; }
9+
10+
public ClassRequiredMembers() { }
11+
12+
[SetsRequiredMembers]
13+
public ClassRequiredMembers(object requiredField, string requiredProperty)
14+
{
15+
RequiredField = requiredField;
16+
RequiredProperty = requiredProperty;
17+
}
18+
}
19+
20+
public class ClassRequiredMembersSub : ClassRequiredMembers
21+
{
22+
public override required object? VirtualProperty { get; init; }
23+
24+
public ClassRequiredMembersSub() : base() { }
25+
26+
[SetsRequiredMembers]
27+
public ClassRequiredMembersSub(object requiredField, string requiredProperty, object virtualProperty) : base(requiredField, requiredProperty)
28+
{
29+
VirtualProperty = virtualProperty;
30+
}
31+
}
32+
33+
public record RecordRequiredMembers
34+
{
35+
public required object? X { get; init; }
36+
}
37+
38+
public struct StructRequiredMembers
39+
{
40+
public required string? Y { get; init; }
41+
}
42+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
| RequiredMembers.cs:6:29:6:41 | RequiredField | ClassRequiredMembers | Field |
2+
| RequiredMembers.cs:7:29:7:44 | RequiredProperty | ClassRequiredMembers | Property |
3+
| RequiredMembers.cs:22:38:22:52 | VirtualProperty | ClassRequiredMembersSub | Property |
4+
| RequiredMembers.cs:35:29:35:29 | X | RecordRequiredMembers | Property |
5+
| RequiredMembers.cs:40:29:40:29 | Y | StructRequiredMembers | Property |
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import csharp
2+
3+
query predicate requiredmembers(Member m, string type, string qlclass) {
4+
m.getFile().getStem() = "RequiredMembers" and
5+
m.isRequired() and
6+
type = m.getDeclaringType().getName() and
7+
qlclass = m.getAPrimaryQlClass()
8+
}

0 commit comments

Comments
 (0)