-
Notifications
You must be signed in to change notification settings - Fork 2k
Expand file tree
/
Copy pathUselessUpcast.ql
More file actions
220 lines (198 loc) · 6.78 KB
/
UselessUpcast.ql
File metadata and controls
220 lines (198 loc) · 6.78 KB
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
214
215
216
217
218
219
220
/**
* @name Useless upcast
* @description Casting an expression is normally not needed when there exists an implicit conversion.
* @kind problem
* @problem.severity warning
* @precision medium
* @id cs/useless-upcast
* @tags quality
* maintainability
* useless-code
* external/cwe/cwe-561
*/
import csharp
/** A static callable. */
class StaticCallable extends Callable {
StaticCallable() { this.(Modifiable).isStatic() }
}
/** An instance callable, that is, a non-static callable. */
class InstanceCallable extends Callable {
InstanceCallable() { not this instanceof StaticCallable }
}
/** A call to a static callable. */
class StaticCall extends Call {
StaticCall() {
this.getTarget() instanceof StaticCallable and
not this = any(ExtensionMethodCall emc | not emc.isOrdinaryStaticCall())
}
}
/** Holds `t` has instance callable `c` as a member, with name `name`. */
pragma[nomagic]
predicate hasInstanceCallable(ValueOrRefType t, InstanceCallable c, string name) {
t.hasMember(c) and
name = c.getUndecoratedName()
}
/** Holds if extension method `m` is a method on `t` with name `name`. */
pragma[nomagic]
predicate hasExtensionMethod(ValueOrRefType t, ExtensionMethod m, string name) {
t.isImplicitlyConvertibleTo(m.getExtendedType()) and
name = m.getUndecoratedName()
}
/** Holds `t` has static callable `c` as a member, with name `name`. */
pragma[noinline]
predicate hasStaticCallable(ValueOrRefType t, StaticCallable c, string name) {
t.hasMember(c) and
name = c.getUndecoratedName()
}
/** Gets the minimum number of arguments required to call `c`. */
int getMinimumArguments(Callable c) {
result =
count(Parameter p |
p = c.getAParameter() and
not p.hasDefaultValue()
)
}
/** Gets the maximum number of arguments allowed to call `c`, if any. */
int getMaximumArguments(Callable c) {
not c.getAParameter().isParams() and
result = c.getNumberOfParameters()
}
private class ConstructorCall extends Call {
ConstructorCall() {
this instanceof ObjectCreation or
this instanceof ConstructorInitializer
}
}
/** An explicit upcast. */
class ExplicitUpcast extends ExplicitCast {
ValueOrRefType dest;
ExplicitUpcast() {
exists(ValueOrRefType src |
src = this.getSourceType() and
dest = this.getTargetType() and
(src instanceof RefType or src instanceof Struct) and
src.isImplicitlyConvertibleTo(dest) and
src != dest // Handled by `cs/useless-cast-to-self`
)
}
pragma[nomagic]
private predicate isArgument(Type t) {
exists(Parameter p |
this = p.getAnAssignedArgument() and
t = p.getType()
)
}
/** Holds if this upcast is the argument of a call to `target`. */
private predicate isArgument(Call c, Callable target) {
this.isArgument(this.getType()) and
c.getAnArgument() = this and
target = c.getTarget()
}
/** Holds if this upcast may be used to disambiguate the target of an instance call. */
pragma[nomagic]
private predicate isDisambiguatingInstanceCall(InstanceCallable other, int args) {
exists(Call c, InstanceCallable target, ValueOrRefType t | this.isArgument(c, target) |
t = c.(QualifiableExpr).getQualifier().getType() and
hasInstanceCallable(t, other, target.getUndecoratedName()) and
args = c.getNumberOfArguments() and
other != target
)
}
/** Holds if this upcast may be used to disambiguate the target of an extension method call. */
pragma[nomagic]
private predicate isDisambiguatingExtensionCall(ExtensionMethod other, int args) {
exists(ExtensionMethodCall c, ExtensionMethod target, ValueOrRefType t |
this.isArgument(c, target)
|
not c.isOrdinaryStaticCall() and
t = target.getParameter(0).getType() and
hasExtensionMethod(t, other, target.getUndecoratedName()) and
args = c.getNumberOfArguments() and
other != target
)
}
pragma[nomagic]
private predicate isDisambiguatingStaticCall0(
StaticCall c, StaticCallable target, string name, ValueOrRefType t
) {
this.isArgument(c, target) and
name = target.getUndecoratedName() and
(
t = c.(QualifiableExpr).getQualifier().getType()
or
not c.(QualifiableExpr).hasQualifier() and
t = target.getDeclaringType()
)
}
/** Holds if this upcast may be used to disambiguate the target of a static call. */
pragma[nomagic]
private predicate isDisambiguatingStaticCall(StaticCallable other, int args) {
exists(StaticCall c, StaticCallable target, ValueOrRefType t, string name |
this.isDisambiguatingStaticCall0(c, target, name, t)
|
hasStaticCallable(t, other, name) and
args = c.getNumberOfArguments() and
other != target
)
}
/** Holds if this upcast may be used to disambiguate the target of a constructor call. */
pragma[nomagic]
private predicate isDisambiguatingConstructorCall(Constructor other, int args) {
exists(ConstructorCall cc, Constructor target, ValueOrRefType t | this.isArgument(cc, target) |
t = target.getDeclaringType() and
t.hasMember(other) and
args = cc.getNumberOfArguments() and
other != target
)
}
/** Holds if this upcast may be used to disambiguate the target of a call. */
private predicate isDisambiguatingCall() {
exists(Callable other, int args |
this.isDisambiguatingInstanceCall(other, args)
or
this.isDisambiguatingExtensionCall(other, args)
or
this.isDisambiguatingStaticCall(other, args)
or
this.isDisambiguatingConstructorCall(other, args)
|
args >= getMinimumArguments(other) and
not args > getMaximumArguments(other)
)
}
/** Holds if this is a useful upcast. */
predicate isUseful() {
this.isDisambiguatingCall()
or
this = any(Call c).(QualifiableExpr).getQualifier() and
dest instanceof Interface
or
this = any(OperatorCall oc).getAnArgument()
or
this =
any(Operation o |
not o instanceof Assignment and
not o instanceof UnaryBitwiseOperation and
not o instanceof SizeofExpr and
not o instanceof PointerIndirectionExpr and
not o instanceof AddressOfExpr and
not o instanceof UnaryLogicalOperation and
not o instanceof BinaryBitwiseOperation and
not o instanceof LogicalAndExpr and
not o instanceof LogicalOrExpr
).getAnOperand()
or
this = any(LocalVariableDeclAndInitExpr decl | decl.isImplicitlyTyped()).getInitializer()
or
exists(LambdaExpr c | c.canReturn(this))
or
dest instanceof DynamicType
}
}
from ExplicitUpcast u, ValueOrRefType src, ValueOrRefType dest
where
src = u.getSourceType() and
dest = u.getTargetType() and
not u.isUseful()
select u, "There is no need to upcast from $@ to $@ - the conversion can be done implicitly.", src,
src.getName(), dest, dest.getName()