Swift: Create a custom IPA type for 'ControlFlowElement's and fixup various type annotations.

This commit is contained in:
Mathias Vorreiter Pedersen 2022-05-24 13:45:53 +01:00
Родитель 67572bb770
Коммит ab268514a1
9 изменённых файлов: 258 добавлений и 66 удалений

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

@ -3,6 +3,7 @@
private import swift
private import ControlFlowGraph
private import internal.ControlFlowGraphImpl
private import internal.ControlFlowElements
private import CfgNodes
private import SuccessorTypes
@ -198,8 +199,18 @@ private module JoinBlockPredecessors {
private predicate idOf(AstNode x, int y) = equivalenceRelation(id/2)(x, y)
private AstNode projctToAst(ControlFlowElement n) {
result = n.asAstNode()
or
isPropertyGetterElement(n, _, result)
or
isPropertySetterElement(n, _, result)
or
isPropertyObserverElement(n, _, result)
}
int getId(JoinBlockPredecessor jbp) {
idOf(jbp.getFirstNode().(AstCfgNode).getNode(), result)
idOf(projctToAst(jbp.getFirstNode().(AstCfgNode).getNode()), result)
or
idOf(jbp.(EntryBasicBlock).getScope(), result)
}

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

@ -4,6 +4,7 @@ private import swift
private import BasicBlocks
private import ControlFlowGraph
private import internal.ControlFlowGraphImpl
private import internal.ControlFlowElements
private import internal.Splitting
/** An entry node for a given scope. */
@ -66,11 +67,11 @@ class ExitNode extends ControlFlowNode, TExitNode {
*/
class AstCfgNode extends ControlFlowNode, TElementNode {
private Splits splits;
private AstNode n;
private ControlFlowElement n;
AstCfgNode() { this = TElementNode(_, n, splits) }
final override AstNode getNode() { result = n }
final override ControlFlowElement getNode() { result = n }
override Location getLocation() { result = n.getLocation() }
@ -96,7 +97,7 @@ class AstCfgNode extends ControlFlowNode, TElementNode {
class ExprCfgNode extends AstCfgNode {
Expr e;
ExprCfgNode() { e = this.getNode() }
ExprCfgNode() { e = this.getNode().asAstNode() }
/** Gets the underlying expression. */
Expr getExpr() { result = e }

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

@ -6,13 +6,14 @@ private import SuccessorTypes
private import internal.ControlFlowGraphImpl
private import internal.Completion
private import internal.Scope
private import internal.ControlFlowElements
/** An AST node with an associated control-flow graph. */
class CfgScope extends Scope instanceof CfgScope::Range_ {
/** Gets the CFG scope that this scope is nested under, if any. */
final CfgScope getOuterCfgScope() {
exists(AstNode parent |
parent = getParent(this) and
exists(ControlFlowElement parent |
parent.asAstNode() = getParentOfAst(this) and
result = getCfgScope(parent)
)
}
@ -31,7 +32,7 @@ class ControlFlowNode extends TCfgNode {
string toString() { none() }
/** Gets the AST node that this node corresponds to, if any. */
AstNode getNode() { none() }
ControlFlowElement getNode() { none() }
/** Gets the location of this control flow node. */
Location getLocation() { none() }

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

@ -6,6 +6,7 @@
private import swift
private import codeql.swift.controlflow.ControlFlowGraph
private import ControlFlowElements
private import ControlFlowGraphImpl
private import SuccessorTypes
@ -41,8 +42,8 @@ private predicate completionIsValidForStmt(Stmt stmt, Completion c) {
/** A completion of a statement or an expression. */
abstract class Completion extends TCompletion {
private predicate isValidForSpecific(AstNode n) {
completionIsValidForStmt(n, this)
private predicate isValidForSpecific(ControlFlowElement n) {
completionIsValidForStmt(n.asAstNode(), this)
or
mustHaveBooleanCompletion(n) and
(
@ -52,22 +53,24 @@ abstract class Completion extends TCompletion {
this = TBooleanCompletion(_)
)
or
mustHaveMatchingCompletion(n) and
mustHaveMatchingCompletion(n.asAstNode()) and
(
exists(boolean value | isMatchingConstant(n, value) | this = TMatchingCompletion(value))
exists(boolean value | isMatchingConstant(n.asAstNode(), value) |
this = TMatchingCompletion(value)
)
or
not isMatchingConstant(n, _) and
not isMatchingConstant(n.asAstNode(), _) and
this = TMatchingCompletion(_)
)
or
mustHaveThrowCompletion(n, this)
mustHaveThrowCompletion(n.asAstNode(), this)
}
/** Holds if this completion is valid for node `n`. */
predicate isValidFor(AstNode n) {
predicate isValidFor(ControlFlowElement n) {
this.isValidForSpecific(n)
or
mayHaveThrowCompletion(n, this)
mayHaveThrowCompletion(n.asAstNode(), this)
or
not any(Completion c).isValidForSpecific(n) and
this = TSimpleCompletion()
@ -87,25 +90,35 @@ abstract class Completion extends TCompletion {
}
/** Holds if node `n` has the Boolean constant value `value`. */
private predicate isBooleanConstant(AstNode n, boolean value) {
private predicate isBooleanConstant(ControlFlowElement n, boolean value) {
mustHaveBooleanCompletion(n) and
value = n.(BooleanLiteralExpr).getValue()
value = n.asAstNode().(BooleanLiteralExpr).getValue()
or
// Boolean consants hidden inside conversions are also
// constants that resolve to the same value.
isBooleanConstant(n.getResolveStep(), value)
exists(ControlFlowElement parent |
parent.asAstNode() = n.asAstNode().getResolveStep() and
isBooleanConstant(parent, value)
)
}
/**
* Holds if a normal completion of `n` must be a Boolean completion.
*/
private predicate mustHaveBooleanCompletion(AstNode n) { inBooleanContext(n) }
private predicate mustHaveBooleanCompletion(ControlFlowElement n) { inBooleanContext(n) }
/**
* Holds if `n` is used in a Boolean context. That is, the value
* that `n` evaluates to determines a true/false branch successor.
*/
private predicate inBooleanContext(AstNode n) {
private predicate inBooleanContext(ControlFlowElement n) {
astInBooleanContext(n.asAstNode()) or
astInBooleanContext(n.(PropertyGetterElement).getRef()) or
astInBooleanContext(n.(PropertySetterElement).getAssignExpr()) or
astInBooleanContext(n.(PropertyObserverElement).getAssignExpr())
}
private predicate astInBooleanContext(AstNode n) {
n = any(ConditionElement condElem).getFullyUnresolved()
or
n = any(StmtCondition stmtCond).getFullyUnresolved()
@ -115,30 +128,30 @@ private predicate inBooleanContext(AstNode n) {
exists(LogicalAndExpr parent |
n = parent.getLeftOperand().getFullyConverted()
or
inBooleanContext(parent) and
astInBooleanContext(parent) and
n = parent.getRightOperand().getFullyConverted()
)
or
exists(LogicalOrExpr parent |
n = parent.getLeftOperand().getFullyConverted()
or
inBooleanContext(parent) and
astInBooleanContext(parent) and
n = parent.getRightOperand().getFullyConverted()
)
or
n = any(NotExpr parent | inBooleanContext(parent)).getOperand().getFullyConverted()
n = any(NotExpr parent | astInBooleanContext(parent)).getOperand().getFullyConverted()
or
exists(IfExpr ifExpr |
ifExpr.getCondition().getFullyConverted() = n
or
inBooleanContext(ifExpr) and
astInBooleanContext(ifExpr) and
n = ifExpr.getBranch(_).getFullyConverted()
)
or
exists(ForEachStmt foreach | n = foreach.getWhere().getFullyConverted())
or
exists(Exprs::Conversions::ConversionOrIdentityTree parent |
inBooleanContext(parent) and
astInBooleanContext(parent.getAst()) and
parent.convertsFrom(n)
)
}
@ -477,3 +490,18 @@ class ThrowCompletion extends TThrowCompletion, Completion {
override string toString() { result = "throw" }
}
/**
* Hold if `c` represents normal evaluation of a statement or an
* expression.
*/
predicate completionIsNormal(Completion c) { c instanceof NormalCompletion }
/**
* Hold if `c` represents simple (normal) evaluation of a statement or an
* expression.
*/
predicate completionIsSimple(Completion c) { c instanceof SimpleCompletion }
/** Holds if `c` is a valid completion for `e`. */
predicate completionIsValidFor(Completion c, ControlFlowElement e) { c.isValidFor(e) }

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

@ -0,0 +1,163 @@
private import swift
cached
newtype TControlFlowElement =
TAstElement(AstNode n) or
TPropertyGetterElement(Decl accessor, Expr ref) { isPropertyGetterElement(accessor, ref) } or
TPropertySetterElement(AccessorDecl accessor, AssignExpr assign) {
isPropertySetterElement(accessor, assign)
} or
TPropertyObserverElement(AccessorDecl observer, AssignExpr assign) {
isPropertyObserverElement(observer, assign)
}
predicate isLValue(Expr e) { any(AssignExpr assign).getDest() = e }
predicate isRValue(Expr e) { not isLValue(e) }
predicate ignoreAstElement(AstNode n) {
isPropertyGetterElement(_, n)
or
isPropertySetterElement(_, n)
}
private AccessorDecl getAnAccessorDecl(Decl d) {
result = d.(VarDecl).getAnAccessorDecl() or
result = d.(SubscriptDecl).getAnAccessorDecl()
}
predicate isPropertyGetterElement(AccessorDecl accessor, Expr ref) {
hasDirectToImplementationOrOrdinarySemantics(ref) and
isRValue(ref) and
accessor.isGetter() and
accessor = getAnAccessorDecl([ref.(LookupExpr).getMember(), ref.(DeclRefExpr).getDecl()])
}
predicate isPropertyGetterElement(PropertyGetterElement pge, AccessorDecl accessor, Expr ref) {
pge = TPropertyGetterElement(accessor, ref)
}
private predicate hasDirectToImplementationSemantics(Expr e) {
e.(MemberRefExpr).hasDirectToImplementationSemantics()
or
e.(SubscriptExpr).hasDirectToImplementationSemantics()
or
e.(DeclRefExpr).hasDirectToImplementationSemantics()
}
private predicate hasOrdinarySemantics(Expr e) {
e.(MemberRefExpr).hasOrdinarySemantics()
or
e.(SubscriptExpr).hasOrdinarySemantics()
or
e.(DeclRefExpr).hasOrdinarySemantics()
}
private predicate hasDirectToImplementationOrOrdinarySemantics(Expr e) {
hasDirectToImplementationSemantics(e) or hasOrdinarySemantics(e)
}
predicate isPropertySetterElement(AccessorDecl accessor, AssignExpr assign) {
exists(Expr lhs | lhs = assign.getDest() |
hasDirectToImplementationOrOrdinarySemantics(lhs) and
accessor.isSetter() and
isLValue(lhs) and
accessor = getAnAccessorDecl([lhs.(LookupExpr).getMember(), lhs.(DeclRefExpr).getDecl()])
)
}
predicate isPropertySetterElement(
PropertySetterElement pse, AccessorDecl accessor, AssignExpr assign
) {
pse = TPropertySetterElement(accessor, assign)
}
predicate isPropertyObserverElement(AccessorDecl observer, AssignExpr assign) {
exists(Expr lhs | lhs = assign.getDest() |
hasDirectToImplementationOrOrdinarySemantics(lhs) and
observer.isPropertyObserver() and
isLValue(lhs) and
observer = getAnAccessorDecl([lhs.(LookupExpr).getMember(), lhs.(DeclRefExpr).getDecl()])
)
}
predicate isPropertyObserverElement(
PropertyObserverElement poe, AccessorDecl accessor, AssignExpr assign
) {
poe = TPropertyObserverElement(accessor, assign)
}
class ControlFlowElement extends TControlFlowElement {
string toString() { none() } // overriden in subclasses
AstNode asAstNode() { none() }
Location getLocation() { none() } // overriden in subclasses
}
class AstElement extends ControlFlowElement, TAstElement {
AstNode n;
AstElement() { this = TAstElement(n) }
override string toString() { result = n.toString() }
override AstNode asAstNode() { result = n }
override Location getLocation() { result = n.getLocation() }
}
class PropertyGetterElement extends ControlFlowElement, TPropertyGetterElement {
AccessorDecl accessor;
Expr ref;
PropertyGetterElement() { this = TPropertyGetterElement(accessor, ref) }
override string toString() { result = "getter for " + ref.toString() }
override Location getLocation() { result = ref.getLocation() }
Expr getRef() { result = ref }
AccessorDecl getAccessorDecl() { result = accessor }
}
class PropertySetterElement extends ControlFlowElement, TPropertySetterElement {
AccessorDecl accessor;
AssignExpr assign;
PropertySetterElement() { this = TPropertySetterElement(accessor, assign) }
override string toString() { result = "setter for " + assign }
override Location getLocation() { result = assign.getLocation() }
AccessorDecl getAccessorDecl() { result = accessor }
AssignExpr getAssignExpr() { result = assign }
}
class PropertyObserverElement extends ControlFlowElement, TPropertyObserverElement {
AccessorDecl observer;
AssignExpr assign;
PropertyObserverElement() { this = TPropertyObserverElement(observer, assign) }
override string toString() {
this.isWillSet() and
result = "willSet observer for " + assign.toString()
or
this.isDidSet() and
result = "didSet observer for " + assign.toString()
}
override Location getLocation() { result = assign.getLocation() }
AccessorDecl getObserver() { result = observer }
predicate isWillSet() { observer.isWillSet() }
predicate isDidSet() { observer.isDidSet() }
AssignExpr getAssignExpr() { result = assign }
}

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

@ -1,30 +1,14 @@
private import swift as S
private import ControlFlowGraphImpl as Impl
private import Completion as Comp
import Completion
private import codeql.swift.controlflow.ControlFlowGraph as CFG
private import Splitting as Splitting
private import Scope
import ControlFlowElements
import AstControlFlowTrees
/** The base class for `ControlFlowTree`. */
class ControlFlowTreeBase extends S::AstNode { }
class ControlFlowElement = S::AstNode;
class Completion = Comp::Completion;
/**
* Hold if `c` represents normal evaluation of a statement or an
* expression.
*/
predicate completionIsNormal(Completion c) { c instanceof Comp::NormalCompletion }
/**
* Hold if `c` represents simple (normal) evaluation of a statement or an
* expression.
*/
predicate completionIsSimple(Completion c) { c instanceof Comp::SimpleCompletion }
/** Holds if `c` is a valid completion for `e`. */
predicate completionIsValidFor(Completion c, ControlFlowElement e) { c.isValidFor(e) }
class ControlFlowTreeBase = ControlFlowElement;
class CfgScope = CFG::CfgScope;

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

@ -16,7 +16,7 @@ private module Scope {
class TypeRange = Callable::TypeRange;
class Range extends AstNode, TypeRange {
Range getOuterScope() { result = scopeOf(this) }
Range getOuterScope() { result = scopeOfAst(this) }
}
}
@ -235,14 +235,14 @@ private module Cached {
}
cached
AstNode getParent(AstNode ast) { getChild(result, _) = ast }
AstNode getParentOfAst(AstNode ast) { getChild(result, _) = ast }
}
/** Gets the enclosing scope of a node */
cached
AstNode scopeOf(AstNode n) {
exists(AstNode p | p = getParent(n) |
if p instanceof Scope then p = result else result = scopeOf(p)
AstNode scopeOfAst(AstNode n) {
exists(AstNode p | p = getParentOfAst(n) |
if p instanceof Scope then p = result else result = scopeOfAst(p)
)
}

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

@ -6,6 +6,8 @@ private import swift
private import Completion
private import ControlFlowGraphImpl
private import codeql.swift.controlflow.ControlFlowGraph
private import AstControlFlowTrees
private import ControlFlowElements
cached
private module Cached {
@ -37,7 +39,7 @@ private module ConditionalCompletionSplitting {
private class ConditionalCompletionSplitKind extends SplitKind, TConditionalCompletionSplitKind {
override int getListOrder() { result = 0 }
override predicate isEnabled(AstNode n) { this.appliesTo(n) }
override predicate isEnabled(ControlFlowElement n) { this.appliesTo(n) }
override string toString() { result = "ConditionalCompletion" }
}
@ -45,47 +47,50 @@ private module ConditionalCompletionSplitting {
private class ConditionalCompletionSplitImpl extends SplitImpl, ConditionalCompletionSplit {
override ConditionalCompletionSplitKind getKind() { any() }
override predicate hasEntry(AstNode pred, AstNode succ, Completion c) {
override predicate hasEntry(ControlFlowElement pred, ControlFlowElement succ, Completion c) {
succ(pred, succ, c) and
last(succ, _, completion) and
(
last(succ.(NotExpr).getOperand().getFullyConverted(), pred, c) and
astLast(succ.asAstNode().(NotExpr).getOperand().getFullyConverted(), pred, c) and
completion.(BooleanCompletion).getDual() = c
or
last(succ.(LogicalAndExpr).getAnOperand().getFullyConverted(), pred, c) and
astLast(succ.asAstNode().(LogicalAndExpr).getAnOperand().getFullyConverted(), pred, c) and
completion = c
or
last(succ.(LogicalOrExpr).getAnOperand().getFullyConverted(), pred, c) and
astLast(succ.asAstNode().(LogicalOrExpr).getAnOperand().getFullyConverted(), pred, c) and
completion = c
or
succ =
succ.asAstNode() =
any(IfExpr ce |
last(ce.getBranch(_).getFullyConverted(), pred, c) and
astLast(ce.getBranch(_).getFullyConverted(), pred, c) and
completion = c
)
or
exists(Expr e |
succ.(Exprs::Conversions::ConversionOrIdentityTree).convertsFrom(e) and
last(e, pred, c) and
exists(Expr e, Exprs::Conversions::ConversionOrIdentityTree conv |
succ.asAstNode() = conv.getAst() and
conv.convertsFrom(e) and
astLast(e, pred, c) and
completion = c
)
)
}
override predicate hasEntryScope(CfgScope scope, AstNode succ) { none() }
override predicate hasEntryScope(CfgScope scope, ControlFlowElement succ) { none() }
override predicate hasExit(AstNode pred, AstNode succ, Completion c) {
override predicate hasExit(ControlFlowElement pred, ControlFlowElement succ, Completion c) {
this.appliesTo(pred) and
succ(pred, succ, c) and
if c instanceof ConditionalCompletion then completion = c else any()
}
override predicate hasExitScope(CfgScope scope, AstNode last, Completion c) {
override predicate hasExitScope(CfgScope scope, ControlFlowElement last, Completion c) {
this.appliesTo(last) and
succExit(scope, last, c) and
if c instanceof ConditionalCompletion then completion = c else any()
}
override predicate hasSuccessor(AstNode pred, AstNode succ, Completion c) { none() }
override predicate hasSuccessor(ControlFlowElement pred, ControlFlowElement succ, Completion c) {
none()
}
}
}

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

@ -1,4 +1,3 @@
// generated by codegen/codegen.py, remove this comment if you wish to edit this file
private import codeql.swift.generated.expr.SubscriptExpr
class SubscriptExpr extends SubscriptExprBase { }