Basic extraction of record patterns

This commit is contained in:
Chris Smowton
2023-11-01 16:05:53 +00:00
parent 293cc67494
commit daccd04087
10 changed files with 93 additions and 42 deletions

View File

@@ -774,6 +774,7 @@ case @expr.kind of
| 86 = @valueeqexpr
| 87 = @valueneexpr
| 88 = @propertyref
| 89 = @recordpatternexpr
;
/** Holds if this `when` expression was written as an `if` expression. */

View File

@@ -529,7 +529,7 @@ private module ControlFlowGraphImpl {
or
this instanceof LocalVariableDeclExpr and
not this = any(InstanceOfExpr ioe).getLocalVariableDeclExpr() and
not this = any(PatternCase pc).getDecl()
not this = any(PatternCase pc).getPattern()
or
this instanceof StringTemplateExpr
or
@@ -971,7 +971,7 @@ private module ControlFlowGraphImpl {
(
if exists(pc.getGuard())
then last(pc.getGuard(), last, BooleanCompletion(true, _))
else last = pc.getDecl()
else last = pc.getPattern()
) and
not pc.isRule() and
completion = NormalCompletion()
@@ -1332,7 +1332,7 @@ private module ControlFlowGraphImpl {
last(case.(PatternCase).getGuard(), preBodyNode, completion) and
completion = basicBooleanCompletion(true)
) else (
preBodyNode = case.(PatternCase).getDecl() and completion = NormalCompletion()
preBodyNode = case.(PatternCase).getPattern() and completion = NormalCompletion()
)
) else (
preBodyNode = case and completion = NormalCompletion()
@@ -1351,7 +1351,7 @@ private module ControlFlowGraphImpl {
result = getASuccessorSwitchCase(case)
or
completion = basicBooleanCompletion(true) and
result = case.getDecl()
result = case.getPattern()
)
)
or
@@ -1362,7 +1362,7 @@ private module ControlFlowGraphImpl {
exists(PatternCase case, Expr guard |
guard = case.getGuard() and
(
n = case.getDecl() and
n = case.getPattern() and
result = first(guard) and
completion = NormalCompletion()
or

View File

@@ -82,7 +82,7 @@ predicate depends(RefType t, RefType dep) {
or
// the type accessed in a pattern-switch case statement in `t`.
exists(PatternCase pc | t = pc.getEnclosingCallable().getDeclaringType() |
usesType(pc.getDecl().getType(), dep)
usesType(pc.getPattern().getType(), dep)
)
)
}

View File

@@ -2541,3 +2541,10 @@ class NotNullExpr extends UnaryExpr, @notnullexpr {
override string getAPrimaryQlClass() { result = "NotNullExpr" }
}
/** A record pattern expr, as in `if (x instanceof SomeRecord(int field))`. */
class RecordPatternExpr extends Expr, @recordpatternexpr {
override string toString() { result = this.getType().toString() + "(...)" }
override string getAPrimaryQlClass() { result = "RecordPatternExpr" }
}

View File

@@ -507,7 +507,7 @@ class SwitchCase extends Stmt, @case {
class ConstCase extends SwitchCase {
ConstCase() {
exists(Expr e |
e.getParent() = this and e.getIndex() >= 0 and not e instanceof LocalVariableDeclExpr
e.getParent() = this and e.getIndex() >= 0 and not e instanceof Pattern
)
// For backward compatibility, we don't include `case null, default:` here, on the assumption
// this will come as a surprise to CodeQL that predates that statement's validity.
@@ -531,14 +531,35 @@ class ConstCase extends SwitchCase {
override string getAPrimaryQlClass() { result = "ConstCase" }
}
/**
* A binding or record pattern.
*
* Note binding patterns are represented as `LocalVariableDeclExpr`s.
*/
class Pattern extends Expr {
Pattern() {
(this.getParent() instanceof SwitchCase or this.getParent() instanceof InstanceOfExpr)
and
(this instanceof LocalVariableDeclExpr or this instanceof RecordPatternExpr)
}
LocalVariableDeclExpr asBindingPattern() {
result = this
}
RecordPatternExpr asRecordPattern() {
result = this
}
}
/** A pattern case of a `switch` statement */
class PatternCase extends SwitchCase {
LocalVariableDeclExpr patternVar;
Pattern pattern;
PatternCase() { patternVar.isNthChildOf(this, 0) }
PatternCase() { pattern.isNthChildOf(this, 0) }
/** Gets the variable declared by this pattern case. */
LocalVariableDeclExpr getDecl() { result.isNthChildOf(this, 0) }
/** Gets this case's pattern. */
Pattern getPattern() { result.isNthChildOf(this, 0) }
/** Gets the guard applicable to this pattern case, if any. */
Expr getGuard() { result.isNthChildOf(this, -3) }

View File

@@ -180,7 +180,7 @@ private predicate switchCaseControls(SwitchCase sc, BasicBlock bb) {
selector = sc.getSelectorExpr() and
(
if sc instanceof PatternCase
then caseblock.getFirstNode() = sc.(PatternCase).getDecl().getControlFlowNode()
then caseblock.getFirstNode() = sc.(PatternCase).getPattern().getControlFlowNode()
else (
caseblock.getFirstNode() = sc.getControlFlowNode() and
// Check there is no fall-through edge from a previous case:

View File

@@ -447,7 +447,7 @@ private predicate patternCaseGuarded(Expr e, RefType t) {
exists(PatternCase pc |
e = getAProbableAlias([pc.getSwitch().getExpr(), pc.getSwitchExpr().getExpr()]) and
guardControls_v1(pc, e.getBasicBlock(), true) and
t = pc.getDecl().getType()
t = pc.getPattern().getType()
)
}

View File

@@ -82,7 +82,7 @@ private predicate step(Node n1, Node n2) {
)
or
exists(PatternCase pc |
pc.getDecl() = def.(BaseSsaUpdate).getDefiningExpr() and
pc.getPattern() = def.(BaseSsaUpdate).getDefiningExpr() and
(
pc.getSwitch().getExpr() = n1.asExpr()
or