/
LockedFieldShouldBeReadonly.cs
213 lines (173 loc) · 7.94 KB
/
LockedFieldShouldBeReadonly.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
using System;
using System.Collections.Generic;
using System.Linq;
class Test
{
private static readonly object staticReadonlyField = null;
private static object staticReadWriteField = null;
private readonly object readonlyField = null;
private readonly string readonlyStringField = "a string";
private object readWriteField = null;
private static object StaticReadonlyProperty => null;
private object ReadonlyProperty => null;
private static object StaticReadWriteProperty { get; set; }
private object ReadWriteProperty { get; set; }
void OnAStaticField()
{
lock (staticReadonlyField) { }
lock (staticReadWriteField) { } // Noncompliant {{Do not lock on writable field 'staticReadWriteField', use a readonly field instead.}}
// ^^^^^^^^^^^^^^^^^^^^
lock (Test.staticReadonlyField) { }
lock (Test.staticReadWriteField) { } // Noncompliant {{Do not lock on writable field 'staticReadWriteField', use a readonly field instead.}}
// ^^^^^^^^^^^^^^^^^^^^^^^^^
lock (AnotherClass.staticReadonlyField) { }
lock (AnotherClass.staticReadWriteField) { } // Noncompliant {{Do not lock on writable field 'staticReadWriteField', use a readonly field instead.}}
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
void OnAFieldOfSameInstance()
{
lock (readonlyField) { }
lock (readonlyStringField) { } // Noncompliant {{Do not lock on strings as they can be interned, use a readonly field instead.}}
lock (readWriteField) { } // Noncompliant {{Do not lock on writable field 'readWriteField', use a readonly field instead.}}
lock (this.readonlyField) { }
lock (this.readWriteField) { } // Noncompliant {{Do not lock on writable field 'readWriteField', use a readonly field instead.}}
}
void OnParenthesizedExpressions()
{
lock ((readonlyField)) { }
lock ((readWriteField)) { } // Noncompliant {{Do not lock on writable field 'readWriteField', use a readonly field instead.}}
lock ((this.readWriteField)) { } // Noncompliant {{Do not lock on writable field 'readWriteField', use a readonly field instead.}}
}
void OnAFieldOfDifferentInstance()
{
var anotherInstance = new Test();
lock (anotherInstance.readonlyField) { }
lock (anotherInstance.readWriteField) { } // Noncompliant {{Do not lock on writable field 'readWriteField', use a readonly field instead.}}
lock (anotherInstance.readonlyField) { }
lock (anotherInstance?.readWriteField) { } // FN: ?. not supported
}
void OnALocalVariable()
{
object localVarNull = null;
lock (localVarNull) { } // Noncompliant {{Do not lock on local variable 'localVarNull', use a readonly field instead.}}
object localVarReadonlyField = readonlyField;
lock (localVarReadonlyField) { } // Noncompliant, while the local variable references a readonly field, the local variable itself can mutate
object localVarReadWriteField = readWriteField;
lock (localVarReadWriteField) { } // Noncompliant
}
void OnALocalOutVar(Dictionary<int, object> lockObjs)
{
if (lockObjs.TryGetValue(42, out var lockObj))
{
lock (lockObj) { } // Noncompliant, FP: the lock object is a local variable retrieved from a collection of locks
}
}
void OnANewInstance()
{
lock (new object()) { } // Noncompliant {{Do not lock on a new instance because is a no-op, use a readonly field instead.}}
lock (new ANamespace.AClass()) { } // Noncompliant
lock (new Test[] { }) { } // Noncompliant
lock (new[] { readonlyField }) { } // Noncompliant
lock (new Tuple<object>(readonlyField)) { } // Noncompliant
lock (new { }) { } // Noncompliant
lock (1) { } // Error [CS0185]
lock ((a: readonlyField, b: readonlyField)) { } // Error [CS0185]
lock (new ADelegate(x => x)) { } // Noncompliant
lock (new Func<int, int>(x => x)) { } // Noncompliant
lock (x => x) { } // Error [CS0185]
lock ((int?)1) { } // Error [CS0185]
lock (from x in new object[2] select x) { } // Noncompliant
}
void OnAStringInstance()
{
lock ("a string") { } // Noncompliant {{Do not lock on strings as they can be interned, use a readonly field instead.}}
lock ($"an interpolated {"string"}") { } // Noncompliant
lock ("a" + "string") { } // Noncompliant
lock (MethodReturningString()) { } // Noncompliant
string MethodReturningString() => "a string";
}
void OnAssignment()
{
object x;
lock (x = readonlyField) { }
lock (x = readWriteField) { } // FN, assignment not supported
}
void OtherCases(object oPar, bool bPar, object[] arrayPar)
{
lock (null) { }
lock (oPar) { }
lock (this) { }
lock (SomeMethod()) { }
lock (oPar.GetType()) { }
lock (typeof(Test)) { }
lock (default(Test)) { }
object SomeMethod() => null;
lock (StaticReadonlyProperty) { }
lock (ReadonlyProperty) { }
lock (StaticReadWriteProperty) { }
lock (ReadWriteProperty) { }
lock (bPar ? readWriteField : readonlyField) { }
lock (oPar ?? readonlyField) { }
lock (oPar = readonlyField) { }
lock (arrayPar[0]) { }
}
void ReadWriteReferences()
{
lock (RefReturnReadWriteField(this)) { } // FN, the method returns a readwrite ref to a member
lock (RefReturnStaticReadonlyField(this)) { } // FN, the method returns a readwrite ref to a member
ref object RefReturnReadWriteField(Test instance) => ref instance.readWriteField;
ref object RefReturnStaticReadonlyField(Test instance) => ref Test.staticReadWriteField;
}
void NoIdentifier()
{
lock () { } // Error
lock (()) { } // Error
}
delegate object ADelegate(object oPar);
}
class TestExplicitCast
{
private readonly object readonlyField = null;
void Test()
{
lock ((AnotherClass)readonlyField) { } // Compliant, the cast operator can run arbitrary code
}
}
class AnotherClass
{
public static readonly object staticReadonlyField = null;
public static object staticReadWriteField = null;
public readonly object readonlyField = null;
public object readWriteField = null;
public static explicit operator AnotherClass(Test o) => new AnotherClass();
}
class FieldAccessibily
{
private readonly object privateField = null;
protected readonly object protectedField = null;
protected internal readonly object protectedInternalField = null;
internal readonly object internalField = null;
public readonly object publicField = null;
private object PrivateProperty => null;
protected object ProtectedProperty => null;
protected internal object ProtectedInternalProperty => null;
internal object InternalProperty => null;
public object PublicProperty => null;
void Test()
{
lock (privateField) { }
lock (protectedField) { }
lock (protectedInternalField) { }
lock (internalField) { }
lock (publicField) { }
lock (PrivateProperty) { }
lock (ProtectedProperty) { }
lock (ProtectedInternalProperty) { }
lock (InternalProperty) { }
lock (PublicProperty) { }
}
}
namespace ANamespace
{
class AClass { }
}