C#: Avoid using `ExceptionClass` in deliberate Cartesian products

Using the class `ExceptionClass` in combination with a deliberate Cartesian
product can lead to bad join orders, for example

```
EVALUATE NONRECURSIVE RELATION:
  Completion::TriedControlFlowElement::getAThrownException_dispred#ff(int this, int result) :-
    {1} r1 = JOIN Expr::Expr::getType_dispred#ff_10#join_rhs WITH @integral_type#f ON Expr::Expr::getType_dispred#ff_10#join_rhs.<0>=@integral_type#f.<0> OUTPUT FIELDS {Expr::Expr::getType_dispred#ff_10#join_rhs.<1>}
    {1} r2 = JOIN r1 WITH @un_op#f ON r1.<0>=@un_op#f.<0> OUTPUT FIELDS {r1.<0>}
    {1} r3 = JOIN r2 WITH Stmt::TryStmt::getATriedElement#ff_1#join_rhs ON r2.<0>=Stmt::TryStmt::getATriedElement#ff_1#join_rhs.<0> OUTPUT FIELDS {r2.<0>}
    {2} r4 = JOIN r3 WITH Stmt::ExceptionClass#f CARTESIAN PRODUCT OUTPUT FIELDS {Stmt::ExceptionClass#f.<0>,r3.<0>}
    {2} r5 = JOIN r4 WITH System::SystemOverflowExceptionClass#class#f ON r4.<0>=System::SystemOverflowExceptionClass#class#f.<0> OUTPUT FIELDS {r4.<1>,r4.<0>}
```

where the CP is made with `ExceptionClass` rather than `SystemOverflowExceptionClass`
directly.
This commit is contained in:
Tom Hvitved 2019-02-06 14:08:30 +01:00
Родитель 87c5872bc5
Коммит e663abd5da
5 изменённых файлов: 21 добавлений и 19 удалений

Просмотреть файл

@ -23,7 +23,7 @@ import semmle.code.csharp.frameworks.System
* Gets an exception type that may be thrown during the execution of method `m`. * Gets an exception type that may be thrown during the execution of method `m`.
* Assumes any exception may be thrown by library types. * Assumes any exception may be thrown by library types.
*/ */
ExceptionClass getAThrownException(Method m) { Class getAThrownException(Method m) {
m.fromLibrary() and m.fromLibrary() and
result = any(SystemExceptionClass sc) result = any(SystemExceptionClass sc)
or or

Просмотреть файл

@ -15,7 +15,7 @@ abstract class AssertMethod extends Method {
final Parameter getAssertedParameter() { result = this.getParameter(this.getAssertionIndex()) } final Parameter getAssertedParameter() { result = this.getParameter(this.getAssertionIndex()) }
/** Gets the exception being thrown if the assertion fails, if any. */ /** Gets the exception being thrown if the assertion fails, if any. */
abstract ExceptionClass getExceptionClass(); abstract Class getExceptionClass();
} }
/** A positive assertion method. */ /** A positive assertion method. */
@ -122,7 +122,7 @@ class SystemDiagnosticsDebugAssertTrueMethod extends AssertTrueMethod {
override int getAssertionIndex() { result = 0 } override int getAssertionIndex() { result = 0 }
override ExceptionClass getExceptionClass() { override Class getExceptionClass() {
// A failing assertion generates a message box, see // A failing assertion generates a message box, see
// https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.debug.assert // https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.debug.assert
none() none()
@ -182,7 +182,7 @@ class ForwarderAssertMethod extends AssertMethod {
override int getAssertionIndex() { result = p.getPosition() } override int getAssertionIndex() { result = p.getPosition() }
override ExceptionClass getExceptionClass() { override Class getExceptionClass() {
result = this.getUnderlyingAssertMethod().getExceptionClass() result = this.getUnderlyingAssertMethod().getExceptionClass()
} }

Просмотреть файл

@ -178,6 +178,13 @@ private predicate isMatchingConstant(Expr e, boolean value) {
) )
} }
private class Overflowable extends UnaryOperation {
Overflowable() {
not this instanceof UnaryBitwiseOperation and
this.getType() instanceof IntegralType
}
}
/** A control flow element that is inside a `try` block. */ /** A control flow element that is inside a `try` block. */
private class TriedControlFlowElement extends ControlFlowElement { private class TriedControlFlowElement extends ControlFlowElement {
TriedControlFlowElement() { this = any(TryStmt try).getATriedElement() } TriedControlFlowElement() { this = any(TryStmt try).getATriedElement() }
@ -185,20 +192,15 @@ private class TriedControlFlowElement extends ControlFlowElement {
/** /**
* Gets an exception class that is potentially thrown by this element, if any. * Gets an exception class that is potentially thrown by this element, if any.
*/ */
ExceptionClass getAThrownException() { Class getAThrownException() {
this = any(UnaryOperation uo | this instanceof Overflowable and
not uo instanceof UnaryBitwiseOperation and result instanceof SystemOverflowExceptionClass
uo.getType() instanceof IntegralType and
result instanceof SystemOverflowExceptionClass
)
or or
this = any(CastExpr ce | this.(CastExpr).getType() instanceof IntegralType and
ce.getType() instanceof IntegralType and result instanceof SystemOverflowExceptionClass
result instanceof SystemOverflowExceptionClass or
or invalidCastCandidate(this) and
invalidCastCandidate(ce) and result instanceof SystemInvalidCastExceptionClass
result instanceof SystemInvalidCastExceptionClass
)
or or
this instanceof Call and this instanceof Call and
result instanceof SystemExceptionClass result instanceof SystemExceptionClass

Просмотреть файл

@ -558,7 +558,7 @@ class ThrowElement extends ControlFlowElement, DotNet::Throw, @throw_element {
override Expr getExpr() { result = this.getChild(0) } override Expr getExpr() { result = this.getChild(0) }
/** Gets the type of exception being thrown. */ /** Gets the type of exception being thrown. */
ExceptionClass getThrownExceptionType() { Class getThrownExceptionType() {
result = getExpr().getType() result = getExpr().getType()
or or
// Corner case: `throw null` // Corner case: `throw null`

Просмотреть файл

@ -89,7 +89,7 @@ class VSTestAssertClass extends Class {
} }
/** The `Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException` class. */ /** The `Microsoft.VisualStudio.TestTools.UnitTesting.AssertFailedException` class. */
class AssertFailedExceptionClass extends ExceptionClass { class AssertFailedExceptionClass extends Class {
AssertFailedExceptionClass() { AssertFailedExceptionClass() {
this.getNamespace() instanceof VSTestNamespace and this.getNamespace() instanceof VSTestNamespace and
this.hasName("AssertFailedException") this.hasName("AssertFailedException")