mirror of
https://github.com/github/codeql.git
synced 2026-04-26 01:05:15 +02:00
Implement dataflow for record patterns
Strategy: there is now a regular flow step from an instance-of LHS / switch expr to the pattern, 0 or more read steps corresponding to record pattern destructors, and then finally a normal SSA def/use step connecting the binding patterns to their first uses.
This commit is contained in:
@@ -576,6 +576,10 @@ lambdaKind(
|
||||
int bodyKind: int ref
|
||||
);
|
||||
|
||||
isCanonicalConstr(
|
||||
int constructorid: @constructor ref
|
||||
);
|
||||
|
||||
arrays(
|
||||
unique int id: @array,
|
||||
string nodeName: string ref,
|
||||
|
||||
@@ -1639,7 +1639,11 @@ class LocalVariableDeclExpr extends Expr, @localvariabledeclexpr {
|
||||
/** Gets the name of the variable declared by this local variable declaration expression. */
|
||||
string getName() { result = this.getVariable().getName() }
|
||||
|
||||
/** Gets the switch statement or expression whose pattern declares this identifier, if any. */
|
||||
/**
|
||||
* Gets the switch statement or expression whose pattern declares this identifier, if any.
|
||||
*
|
||||
* Note this only applies to a direct binding pattern, such as `case T t`, not a record pattern.
|
||||
*/
|
||||
StmtParent getAssociatedSwitch() { result = this.getParent().(PatternCase).getParent() }
|
||||
|
||||
/** Holds if this is a declaration stemming from a pattern switch case. */
|
||||
|
||||
@@ -746,6 +746,13 @@ class DataClass extends Class {
|
||||
*/
|
||||
class Record extends Class {
|
||||
Record() { isRecord(this) }
|
||||
|
||||
/**
|
||||
* Gets the canonical constructor of this record.
|
||||
*/
|
||||
Constructor getCanonicalConstructor() {
|
||||
result = this.getAConstructor() and isCanonicalConstr(result)
|
||||
}
|
||||
}
|
||||
|
||||
/** An intersection type. */
|
||||
|
||||
@@ -232,6 +232,17 @@ predicate storeStep(Node node1, ContentSet f, Node node2) {
|
||||
pragma[only_bind_out](node2.getEnclosingCallable())
|
||||
}
|
||||
|
||||
private Field getLexicallyOrderedRecordField(Record r, int idx) {
|
||||
result =
|
||||
rank[idx + 1](Field f, int i, Parameter p |
|
||||
f = r.getAField() and
|
||||
p = r.getCanonicalConstructor().getParameter(i) and
|
||||
f.getName() = p.getName()
|
||||
|
|
||||
f order by i
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if data can flow from `node1` to `node2` via a read of `f`.
|
||||
* Thus, `node1` references an object with a field `f` whose value ends up in
|
||||
@@ -256,6 +267,13 @@ predicate readStep(Node node1, ContentSet f, Node node2) {
|
||||
node2.asExpr() = get
|
||||
)
|
||||
or
|
||||
exists(RecordPatternExpr rpe, Pattern subPattern, int i |
|
||||
node1.asExpr() = rpe and
|
||||
subPattern = rpe.getSubPattern(i) and
|
||||
node2.asExpr() = subPattern and
|
||||
f.(FieldContent).getField() = getLexicallyOrderedRecordField(rpe.getType(), i)
|
||||
)
|
||||
or
|
||||
f instanceof ArrayContent and arrayReadStep(node1, node2, _)
|
||||
or
|
||||
f instanceof CollectionContent and collectionReadStep(node1, node2)
|
||||
|
||||
@@ -190,6 +190,18 @@ predicate simpleAstFlowStep(Expr e1, Expr e2) {
|
||||
e2 = any(NotNullExpr nne | e1 = nne.getExpr())
|
||||
or
|
||||
e2.(WhenExpr).getBranch(_).getAResult() = e1
|
||||
or
|
||||
exists(SwitchExpr se |
|
||||
e1 = se.getExpr() and e2 = se.getACase().(PatternCase).getPattern()
|
||||
)
|
||||
or
|
||||
exists(SwitchStmt ss |
|
||||
e1 = ss.getExpr() and e2 = ss.getACase().(PatternCase).getPattern()
|
||||
)
|
||||
or
|
||||
exists(InstanceOfExpr ioe |
|
||||
e1 = ioe.getExpr() and e2 = ioe.getLocalVariableDeclExpr()
|
||||
)
|
||||
}
|
||||
|
||||
private predicate simpleLocalFlowStep0(Node node1, Node node2) {
|
||||
@@ -198,8 +210,7 @@ private predicate simpleLocalFlowStep0(Node node1, Node node2) {
|
||||
exists(SsaExplicitUpdate upd |
|
||||
upd.getDefiningExpr().(VariableAssign).getSource() = node1.asExpr() or
|
||||
upd.getDefiningExpr().(AssignOp) = node1.asExpr() or
|
||||
upd.getDefiningExpr().(LocalVariableDeclExpr).getAssociatedSwitch().(SwitchStmt).getExpr() = node1.asExpr() or
|
||||
upd.getDefiningExpr().(LocalVariableDeclExpr).getAssociatedSwitch().(SwitchExpr).getExpr() = node1.asExpr()
|
||||
upd.getDefiningExpr().(Pattern).asBindingPattern() = node1.asExpr()
|
||||
|
|
||||
node2.asExpr() = upd.getAFirstUse() and
|
||||
not capturedVariableRead(node2)
|
||||
|
||||
Reference in New Issue
Block a user