C#: Compute enclosing callable as a transitive closure

This commit is contained in:
Tom Hvitved
2021-04-07 14:27:47 +02:00
parent fd4e8f8282
commit 1cf30d2a9e
4 changed files with 51 additions and 86 deletions

View File

@@ -1,83 +0,0 @@
/**
* INTERNAL: Do not use.
*
* Provides efficient cached predicates for finding enclosing statements and callables.
*
* There are a number of difficulties. There can be expressions without
* enclosing statements (for example initialisers for fields and constructors)
* or enclosing callables (even if we consider constructor initialisers
* to be enclosed by constructors, field initialisers don't have callables).
*
* The only cases where a `Stmt` has an `Expr` parent are delegate and lambda
* expressions, which are both callable.
*/
import Stmt
private import semmle.code.csharp.ExprOrStmtParent
/**
* INTERNAL: Do not use.
*/
cached
module Internal {
/**
* INTERNAL: Do not use.
*
* Holds if `c` is the enclosing callable of statement `s`.
*/
cached
predicate enclosingCallable(Stmt s, Callable c) {
// Compute the enclosing callable for a statement. This walks up through
// enclosing statements until it hits a callable. It's unambiguous, since
// if a statement has no parent statement, it's either the method body
// or the body of an anonymous function declaration, in each of which cases the
// non-statement parent is in fact the enclosing callable.
c.getAChildStmt+() = s
}
private Expr getAChildExpr(ExprOrStmtParent p) {
result = p.getAChildExpr() or
result = p.(AssignOperation).getExpandedAssignment()
}
/**
* INTERNAL: Do not use.
*
* Holds if `s` is the enclosing statement of expression `e`.
*/
cached
predicate enclosingStmt(Expr e, Stmt s) {
// Compute the enclosing statement for an expression. Note that this need
// not exist, since expressions can occur in contexts where they have no
// enclosing statement (examples include field initialisers, both inline
// and explicit on constructor definitions, and annotation arguments).
getAChildExpr+(s) = e
}
private predicate childExprOfCallable(Callable parent, Expr child) {
child = getAChildExpr(parent)
or
exists(Expr mid | childExprOfCallable(parent, mid) |
not mid instanceof Callable and
child = getAChildExpr(mid)
)
}
/**
* INTERNAL: Do not use.
*
* Holds if `c` is the enclosing callable of expression `e`.
*/
cached
predicate exprEnclosingCallable(Expr e, Callable c) {
// Compute the enclosing callable of an expression. Note that expressions in
// lambda functions should have the lambdas as enclosing callables, and their
// enclosing statement may be the same as the enclosing statement of the
// lambda; thus, it is *not* safe to go up to the enclosing statement and
// take its own enclosing callable.
childExprOfCallable(c, e)
or
not childExprOfCallable(_, e) and
exists(Stmt s | enclosingStmt(e, s) | enclosingCallable(s, c))
}
}

View File

@@ -138,6 +138,54 @@ private module Cached {
)
else expr_parent(child, i, parent)
}
private Expr getAChildExpr(ExprOrStmtParent parent) {
result = parent.getAChildExpr() or
result = parent.(AssignOperation).getExpandedAssignment()
}
private ControlFlowElement getAChild(ExprOrStmtParent parent) {
result = getAChildExpr(parent)
or
result = parent.getAChildStmt()
}
pragma[inline]
private ControlFlowElement enclosingStart(ControlFlowElement cfe) {
result = cfe
or
getAChild(result).(AnonymousFunctionExpr) = cfe
}
private predicate parent(ControlFlowElement child, ExprOrStmtParent parent) {
child = getAChild(parent) and
not child instanceof Callable
}
/** Holds if the enclosing body of `cfe` is `body`. */
cached
predicate enclosingBody(ControlFlowElement cfe, ControlFlowElement body) {
body = any(Callable c).getBody() and
parent*(enclosingStart(cfe), body)
}
/** Holds if the enclosing callable of `cfe` is `c`. */
cached
predicate enclosingCallable(ControlFlowElement cfe, Callable c) {
enclosingBody(cfe, c.getBody())
or
parent*(enclosingStart(cfe), c.(Constructor).getInitializer())
}
/** Holds if the enclosing statement of expression `e` is `s`. */
cached
predicate enclosingStmt(Expr e, Stmt s) {
// Compute the enclosing statement for an expression. Note that this need
// not exist, since expressions can occur in contexts where they have no
// enclosing statement (examples include field initialisers, both inline
// and explicit on constructor definitions, and annotation arguments).
getAChildExpr+(s) = e
}
}
import Cached

View File

@@ -8,7 +8,7 @@ import Element
import Location
import Member
import exprs.Expr
private import semmle.code.csharp.Enclosing::Internal
private import semmle.code.csharp.ExprOrStmtParent
private import semmle.code.csharp.frameworks.System
private import TypeRef

View File

@@ -20,7 +20,7 @@ import semmle.code.csharp.Location
import semmle.code.csharp.Stmt
import semmle.code.csharp.Type
private import dotnet
private import semmle.code.csharp.Enclosing::Internal
private import semmle.code.csharp.ExprOrStmtParent
private import semmle.code.csharp.frameworks.System
private import semmle.code.csharp.TypeRef
@@ -55,7 +55,7 @@ class Expr extends DotNet::Expr, ControlFlowElement, @expr {
final Stmt getEnclosingStmt() { enclosingStmt(this, result) }
/** Gets the enclosing callable of this expression, if any. */
override Callable getEnclosingCallable() { exprEnclosingCallable(this, result) }
override Callable getEnclosingCallable() { enclosingCallable(this, result) }
/**
* Holds if this expression is generated by the compiler and does not appear