Skip to content

Commit 072713f

Browse files
committed
C#: Exclude more property access expressions from DB quality metric
1 parent 5727fda commit 072713f

File tree

6 files changed

+111
-15
lines changed

6 files changed

+111
-15
lines changed

csharp/ql/src/Telemetry/DatabaseQuality.qll

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,27 +32,64 @@ module ReportStats<StatsSig Stats> {
3232
}
3333
}
3434

35-
private predicate isNoSetterPropertyCallInConstructor(PropertyCall c) {
36-
exists(Property p |
37-
p = c.getProperty() and
38-
not exists(Setter a | a = p.getAnAccessor()) and
39-
c.getEnclosingCallable().(Constructor).getDeclaringType().getASubType*() = p.getDeclaringType()
40-
)
41-
}
42-
4335
module CallTargetStats implements StatsSig {
4436
int getNumberOfOk() { result = count(Call c | exists(c.getTarget())) }
4537

46-
int getNumberOfNotOk() {
47-
result =
48-
count(Call c |
49-
not exists(c.getTarget()) and
50-
not c instanceof DelegateCall and
51-
not c instanceof DynamicExpr and
52-
not isNoSetterPropertyCallInConstructor(c)
38+
private predicate isNoSetterPropertyCallInConstructor(PropertyCall c) {
39+
exists(Property p, Constructor ctor |
40+
p = c.getProperty() and
41+
not exists(Setter a | a = p.getAnAccessor()) and
42+
c.getEnclosingCallable() = ctor and
43+
(
44+
c.hasThisQualifier()
45+
or
46+
ctor instanceof StaticConstructor and p.getDeclaringType() = ctor.getDeclaringType()
5347
)
48+
)
49+
}
50+
51+
private predicate isNoSetterPropertyInitialization(PropertyCall c) {
52+
exists(Property p, AssignExpr assign |
53+
p = c.getProperty() and
54+
not exists(Setter a | a = p.getAnAccessor()) and
55+
assign = c.getParent() and
56+
assign.getLValue() = c and
57+
assign.getParent() instanceof Property
58+
)
59+
}
60+
61+
private predicate isAnonymousObjectMemberDeclaration(PropertyCall c) {
62+
exists(Property p, AssignExpr assign |
63+
p = c.getProperty() and
64+
assign = c.getParent() and
65+
assign.getLValue() = c and
66+
assign.getParent() instanceof ObjectInitializer and
67+
assign.getParent().getParent() instanceof AnonymousObjectCreation
68+
)
5469
}
5570

71+
private predicate isInitializedWithCollectionInitializer(PropertyCall c) {
72+
exists(Property p, AssignExpr assign |
73+
p = c.getProperty() and
74+
assign = c.getParent() and
75+
assign.getLValue() = c and
76+
assign.getRValue() instanceof CollectionInitializer
77+
)
78+
}
79+
80+
additional predicate isNotOkCall(Call c) {
81+
not exists(c.getTarget()) and
82+
not c instanceof DelegateCall and
83+
not c instanceof DynamicExpr and
84+
not isNoSetterPropertyCallInConstructor(c) and
85+
not isNoSetterPropertyInitialization(c) and
86+
not isAnonymousObjectMemberDeclaration(c) and
87+
not isInitializedWithCollectionInitializer(c) and
88+
not c.getParent+() instanceof NameOfExpr
89+
}
90+
91+
int getNumberOfNotOk() { result = count(Call c | isNotOkCall(c)) }
92+
5693
string getOkText() { result = "calls with call target" }
5794

5895
string getNotOkText() { result = "calls with missing call target" }

csharp/ql/test/query-tests/Telemetry/DatabaseQuality/IsNotOkayCall.expected

Whitespace-only changes.
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* @id test/missing-call-target
3+
* @kind problem
4+
* @problem.severity warning
5+
*/
6+
7+
import csharp
8+
import Telemetry.DatabaseQuality
9+
10+
from Call call
11+
where CallTargetStats::isNotOkCall(call)
12+
select call, "Call without target $@.", call, call.toString()
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
| Quality.cs:8:9:8:19 | access to property MyProperty5 | Call without target $@. | Quality.cs:8:9:8:19 | access to property MyProperty5 | access to property MyProperty5 |
2+
| Quality.cs:13:9:13:19 | access to property MyProperty1 | Call without target $@. | Quality.cs:13:9:13:19 | access to property MyProperty1 | access to property MyProperty1 |
3+
| Quality.cs:14:24:14:34 | access to property MyProperty3 | Call without target $@. | Quality.cs:14:24:14:34 | access to property MyProperty3 | access to property MyProperty3 |
4+
| Quality.cs:15:24:15:34 | access to property MyProperty3 | Call without target $@. | Quality.cs:15:24:15:34 | access to property MyProperty3 | access to property MyProperty3 |
5+
| Quality.cs:15:24:15:46 | access to property MyProperty2 | Call without target $@. | Quality.cs:15:24:15:46 | access to property MyProperty2 | access to property MyProperty2 |
6+
| Quality.cs:19:13:19:23 | access to property MyProperty4 | Call without target $@. | Quality.cs:19:13:19:23 | access to property MyProperty4 | access to property MyProperty4 |
7+
| Quality.cs:24:16:24:26 | access to property MyProperty2 | Call without target $@. | Quality.cs:24:16:24:26 | access to property MyProperty2 | access to property MyProperty2 |
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/**
2+
* @id test/missing-call-target
3+
* @kind problem
4+
* @problem.severity warning
5+
*/
6+
7+
import csharp
8+
import Telemetry.DatabaseQuality
9+
10+
from Call call
11+
where not exists(call.getTarget())
12+
select call, "Call without target $@.", call, call.toString()
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
public class Test
5+
{
6+
static Test()
7+
{
8+
MyProperty5 = 42;
9+
}
10+
11+
public Test()
12+
{
13+
MyProperty1 = 42;
14+
var x = nameof(MyProperty3);
15+
var y = nameof(MyProperty3.MyProperty2);
16+
17+
new Test()
18+
{
19+
MyProperty4 = { 1, 2, 3 }
20+
};
21+
}
22+
23+
public int MyProperty1 { get; }
24+
public int MyProperty2 { get; } = 42;
25+
public Test MyProperty3 { get; set; }
26+
public List<int> MyProperty4 { get; }
27+
static int MyProperty5 { get; }
28+
}

0 commit comments

Comments
 (0)