Skip to content

Commit ef1ddd0

Browse files
authored
Merge pull request #19520 from michaelnebel/csharp/missedreadonly
C#: Improve `cs/missed-readonly-modifier` and to code-quality suite.
2 parents 8595bd8 + bae16f0 commit ef1ddd0

File tree

7 files changed

+74
-5
lines changed

7 files changed

+74
-5
lines changed

csharp/ql/integration-tests/posix/query-suite/csharp-code-quality.qls.expected

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ ql/csharp/ql/src/API Abuse/FormatInvalid.ql
33
ql/csharp/ql/src/API Abuse/NoDisposeCallOnLocalIDisposable.ql
44
ql/csharp/ql/src/Bad Practices/Control-Flow/ConstantCondition.ql
55
ql/csharp/ql/src/Dead Code/DeadStoreOfLocal.ql
6+
ql/csharp/ql/src/Language Abuse/MissedReadonlyOpportunity.ql
67
ql/csharp/ql/src/Likely Bugs/Collections/ContainerLengthCmpOffByOne.ql
78
ql/csharp/ql/src/Likely Bugs/Collections/ContainerSizeCmpZero.ql
89
ql/csharp/ql/src/Likely Bugs/DangerousNonShortCircuitLogic.ql

csharp/ql/src/Language Abuse/MissedReadonlyOpportunity.ql

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
* @id cs/missed-readonly-modifier
99
* @tags maintainability
1010
* language-features
11+
* quality
1112
*/
1213

1314
import csharp
@@ -19,13 +20,17 @@ predicate defTargetsField(AssignableDefinition def, Field f) {
1920
predicate isReadonlyCompatibleDefinition(AssignableDefinition def, Field f) {
2021
defTargetsField(def, f) and
2122
(
22-
def.getEnclosingCallable().(Constructor).getDeclaringType() = f.getDeclaringType()
23+
def.getEnclosingCallable().(StaticConstructor).getDeclaringType() = f.getDeclaringType()
24+
or
25+
def.getEnclosingCallable().(InstanceConstructor).getDeclaringType() = f.getDeclaringType() and
26+
def.getTargetAccess().(QualifiableExpr).getQualifier() instanceof ThisAccess
2327
or
2428
def instanceof AssignableDefinitions::InitializerDefinition
2529
)
2630
}
2731

2832
predicate canBeReadonly(Field f) {
33+
exists(Type t | t = f.getType() | not t instanceof Struct or t.(Struct).isReadonly()) and
2934
forex(AssignableDefinition def | defTargetsField(def, f) | isReadonlyCompatibleDefinition(def, f))
3035
}
3136

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* The precision of the query `cs/missed-readonly-modifier` has been improved. Some false positives related to static fields and struct type fields have been removed.
Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,27 @@
11
class MissedReadonlyOpportunity<T>
22
{
3-
public int Bad1;
4-
public T Bad2;
3+
public int Bad1; // $ Alert
4+
public T Bad2; // $ Alert
5+
public Immutable Bad3; // $ Alert
56
public readonly int Good1;
67
public readonly int Good2 = 0;
78
public const int Good3 = 0;
89
public int Good4;
910
public readonly T Good5;
1011
public T Good6;
12+
public Mutable Good7;
1113

1214
public MissedReadonlyOpportunity(int i, T t)
1315
{
1416
Bad1 = i;
1517
Bad2 = t;
18+
Bad3 = new Immutable();
1619
Good1 = i;
1720
Good2 = i;
1821
Good4 = i;
1922
Good5 = t;
2023
Good6 = t;
24+
Good7 = new Mutable();
2125
}
2226

2327
public void M(int i)
@@ -27,3 +31,54 @@ public void M(int i)
2731
x.Good6 = false;
2832
}
2933
}
34+
35+
struct Mutable
36+
{
37+
private int x;
38+
public int Mutate()
39+
{
40+
x = x + 1;
41+
return x;
42+
}
43+
}
44+
45+
readonly struct Immutable { }
46+
47+
class Tree
48+
{
49+
private Tree? Parent;
50+
private Tree? Left; // $ Alert
51+
private readonly Tree? Right;
52+
53+
public Tree(Tree left, Tree right)
54+
{
55+
this.Left = left;
56+
this.Right = right;
57+
left.Parent = this;
58+
right.Parent = this;
59+
}
60+
61+
public Tree()
62+
{
63+
Left = null;
64+
Right = null;
65+
}
66+
}
67+
68+
class StaticFields
69+
{
70+
static int X; // $ Alert
71+
static int Y;
72+
73+
// Static constructor
74+
static StaticFields()
75+
{
76+
X = 0;
77+
}
78+
79+
// Instance constructor
80+
public StaticFields(int y)
81+
{
82+
Y = y;
83+
}
84+
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
| MissedReadonlyOpportunity.cs:3:16:3:19 | Bad1 | Field 'Bad1' can be 'readonly'. |
22
| MissedReadonlyOpportunity.cs:4:14:4:17 | Bad2 | Field 'Bad2' can be 'readonly'. |
3+
| MissedReadonlyOpportunity.cs:5:22:5:25 | Bad3 | Field 'Bad3' can be 'readonly'. |
4+
| MissedReadonlyOpportunity.cs:50:19:50:22 | Left | Field 'Left' can be 'readonly'. |
5+
| MissedReadonlyOpportunity.cs:70:16:70:16 | X | Field 'X' can be 'readonly'. |
36
| MissedReadonlyOpportunityBad.cs:3:9:3:13 | Field | Field 'Field' can be 'readonly'. |
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
Language Abuse/MissedReadonlyOpportunity.ql
1+
query: Language Abuse/MissedReadonlyOpportunity.ql
2+
postprocess: utils/test/InlineExpectationsTestQuery.ql

csharp/ql/test/query-tests/Language Abuse/MissedReadonlyOpportunity/MissedReadonlyOpportunityBad.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
class Bad
22
{
3-
int Field;
3+
int Field; // $ Alert
44

55
public Bad(int i)
66
{

0 commit comments

Comments
 (0)