Skip to content

Commit 1f92390

Browse files
authored
Merge pull request github#5695 from hvitved/csharp/dispose-not-called-on-exc-perf
C#: Improve performance of `DisposeNotCalledOnException.ql`
2 parents b2a7a3e + 40b7416 commit 1f92390

File tree

1 file changed

+71
-36
lines changed

1 file changed

+71
-36
lines changed

csharp/ql/src/API Abuse/DisposeNotCalledOnException.ql

Lines changed: 71 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -18,36 +18,6 @@ import csharp
1818
import Dispose
1919
import semmle.code.csharp.frameworks.System
2020

21-
/**
22-
* Gets an exception type that may be thrown during the execution of method `m`.
23-
* Assumes any exception may be thrown by library types.
24-
*/
25-
Class getAThrownException(Method m) {
26-
m.fromLibrary() and
27-
result = any(SystemExceptionClass sc)
28-
or
29-
exists(ControlFlowElement cfe |
30-
cfe = any(ThrowElement te | result = te.getExpr().getType()) or
31-
cfe = any(MethodCall mc | result = getAThrownException(mc.getARuntimeTarget()))
32-
|
33-
cfe.getEnclosingCallable() = m and
34-
not isTriedAgainstException(cfe, result)
35-
)
36-
}
37-
38-
/**
39-
* Holds if control flow element is tried against throwing an exception of type
40-
* `ec`.
41-
*/
42-
pragma[noinline]
43-
predicate isTriedAgainstException(ControlFlowElement cfe, ExceptionClass ec) {
44-
(cfe instanceof ThrowElement or cfe instanceof MethodCall) and
45-
exists(TryStmt ts |
46-
ts.getATriedElement() = cfe and
47-
exists(ts.getAnExceptionHandler(ec))
48-
)
49-
}
50-
5121
private class DisposeCall extends MethodCall {
5222
DisposeCall() { this.getTarget() instanceof DisposeMethod }
5323
}
@@ -78,8 +48,17 @@ predicate disposeReachableFromDisposableCreation(DisposeCall disposeCall, Expr d
7848
reachesDisposeCall(disposeCall, DataFlow::exprNode(disposableCreation))
7949
}
8050

81-
class MethodCallThatMayThrow extends MethodCall {
82-
MethodCallThatMayThrow() { exists(getAThrownException(this.getARuntimeTarget())) }
51+
/**
52+
* Holds if control flow element is tried against throwing an exception of type
53+
* `ec`.
54+
*/
55+
pragma[noinline]
56+
predicate isTriedAgainstException(ControlFlowElement cfe, ExceptionClass ec) {
57+
(cfe instanceof ThrowElement or cfe instanceof MethodCall) and
58+
exists(TryStmt ts |
59+
ts.getATriedElement() = cfe and
60+
exists(ts.getAnExceptionHandler(ec))
61+
)
8362
}
8463

8564
ControlFlowElement getACatchOrFinallyClauseChild() {
@@ -88,15 +67,71 @@ ControlFlowElement getACatchOrFinallyClauseChild() {
8867
result = getACatchOrFinallyClauseChild().getAChild()
8968
}
9069

91-
from DisposeCall disposeCall, Expr disposableCreation, MethodCallThatMayThrow callThatThrows
92-
where
70+
private predicate candidate(DisposeCall disposeCall, Call call, Expr disposableCreation) {
9371
disposeReachableFromDisposableCreation(disposeCall, disposableCreation) and
9472
// The dispose call is not, itself, within a dispose method.
9573
not disposeCall.getEnclosingCallable() instanceof DisposeMethod and
9674
// Dispose call not within a finally or catch block
9775
not getACatchOrFinallyClauseChild() = disposeCall and
9876
// At least one method call exists between the allocation and disposal that could throw
99-
disposableCreation.getAReachableElement() = callThatThrows and
100-
callThatThrows.getAReachableElement() = disposeCall
77+
disposableCreation.getAReachableElement() = call and
78+
call.getAReachableElement() = disposeCall
79+
}
80+
81+
private class RelevantMethod extends Method {
82+
RelevantMethod() {
83+
exists(Call call |
84+
candidate(_, call, _) and
85+
this = call.getARuntimeTarget()
86+
)
87+
or
88+
exists(RelevantMethod other | other.calls(this))
89+
}
90+
91+
pragma[noinline]
92+
private RelevantMethod callsNoTry() {
93+
exists(MethodCall mc |
94+
result = mc.getARuntimeTarget() and
95+
not isTriedAgainstException(mc, _) and
96+
mc.getEnclosingCallable() = this
97+
)
98+
}
99+
100+
pragma[noinline]
101+
private RelevantMethod callsInTry(MethodCall mc) {
102+
result = mc.getARuntimeTarget() and
103+
isTriedAgainstException(mc, _) and
104+
mc.getEnclosingCallable() = this
105+
}
106+
107+
/**
108+
* Gets an exception type that may be thrown during the execution of this method.
109+
* Assumes any exception may be thrown by library types.
110+
*/
111+
Class getAThrownException() {
112+
this.fromLibrary() and
113+
result instanceof SystemExceptionClass
114+
or
115+
exists(ControlFlowElement cfe |
116+
result = cfe.(ThrowElement).getExpr().getType() and
117+
cfe.getEnclosingCallable() = this
118+
or
119+
result = this.callsInTry(cfe).getAThrownException()
120+
|
121+
not isTriedAgainstException(cfe, result)
122+
)
123+
or
124+
result = this.callsNoTry().getAThrownException()
125+
}
126+
}
127+
128+
class MethodCallThatMayThrow extends MethodCall {
129+
MethodCallThatMayThrow() {
130+
exists(this.getARuntimeTarget().(RelevantMethod).getAThrownException())
131+
}
132+
}
133+
134+
from DisposeCall disposeCall, Expr disposableCreation, MethodCallThatMayThrow callThatThrows
135+
where candidate(disposeCall, callThatThrows, disposableCreation)
101136
select disposeCall, "Dispose missed if exception is thrown by $@.", callThatThrows,
102137
callThatThrows.toString()

0 commit comments

Comments
 (0)