mirror of
https://github.com/github/codeql.git
synced 2026-04-23 15:55:18 +02:00
Basic extraction of record patterns
This commit is contained in:
@@ -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. */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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" }
|
||||
}
|
||||
|
||||
@@ -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) }
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -101,6 +101,10 @@ class A {
|
||||
case E.B -> "It's E.B";
|
||||
default -> "It's something else";
|
||||
};
|
||||
var recordPatterntest = switch(thing) {
|
||||
case Middle(Inner(String field)) -> field;
|
||||
default -> "Doesn't match pattern Middle(Inner(...))";
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (RuntimeException rte) {
|
||||
@@ -122,3 +126,6 @@ class A {
|
||||
*/
|
||||
int i, j, k;
|
||||
}
|
||||
|
||||
record Inner(String field) { }
|
||||
record Middle(Inner inner) { }
|
||||
|
||||
@@ -283,36 +283,51 @@ A.java:
|
||||
# 101| -1: [TypeAccess] E
|
||||
# 102| 2: [DefaultCase] default
|
||||
# 102| -1: [StringLiteral] "It's something else"
|
||||
# 106| 0: [CatchClause] catch (...)
|
||||
# 104| 11: [LocalVariableDeclStmt] var ...;
|
||||
# 104| 1: [LocalVariableDeclExpr] recordPatterntest
|
||||
# 104| 0: [SwitchExpr] switch (...)
|
||||
# 104| -1: [VarAccess] thing
|
||||
# 105| 0: [PatternCase] case T t ...
|
||||
# 105| -1: [VarAccess] field
|
||||
# 105| 0: [RecordPatternExpr] Middle(...)
|
||||
# 105| 0: [RecordPatternExpr] Inner(...)
|
||||
# 105| 0: [LocalVariableDeclExpr] field
|
||||
# 106| 1: [DefaultCase] default
|
||||
# 106| -1: [StringLiteral] "Doesn't match pattern Middle(Inner(...))"
|
||||
# 110| 0: [CatchClause] catch (...)
|
||||
#-----| 0: (Single Local Variable Declaration)
|
||||
# 106| 0: [TypeAccess] RuntimeException
|
||||
# 106| 1: [LocalVariableDeclExpr] rte
|
||||
# 106| 1: [BlockStmt] { ... }
|
||||
# 107| 0: [ReturnStmt] return ...
|
||||
# 111| 10: [Class] E
|
||||
# 115| 3: [FieldDeclaration] E A;
|
||||
# 110| 0: [TypeAccess] RuntimeException
|
||||
# 110| 1: [LocalVariableDeclExpr] rte
|
||||
# 110| 1: [BlockStmt] { ... }
|
||||
# 111| 0: [ReturnStmt] return ...
|
||||
# 115| 10: [Class] E
|
||||
# 119| 3: [FieldDeclaration] E A;
|
||||
#-----| -3: (Javadoc)
|
||||
# 112| 1: [Javadoc] /** Javadoc for enum constant */
|
||||
# 113| 0: [JavadocText] Javadoc for enum constant
|
||||
# 115| -1: [TypeAccess] E
|
||||
# 115| 0: [ClassInstanceExpr] new E(...)
|
||||
# 115| -3: [TypeAccess] E
|
||||
# 116| 4: [FieldDeclaration] E B;
|
||||
# 116| 1: [Javadoc] /** Javadoc for enum constant */
|
||||
# 117| 0: [JavadocText] Javadoc for enum constant
|
||||
# 119| -1: [TypeAccess] E
|
||||
# 119| 0: [ClassInstanceExpr] new E(...)
|
||||
# 119| -3: [TypeAccess] E
|
||||
# 120| 4: [FieldDeclaration] E B;
|
||||
#-----| -3: (Javadoc)
|
||||
# 112| 1: [Javadoc] /** Javadoc for enum constant */
|
||||
# 113| 0: [JavadocText] Javadoc for enum constant
|
||||
# 116| -1: [TypeAccess] E
|
||||
# 116| 0: [ClassInstanceExpr] new E(...)
|
||||
# 116| -3: [TypeAccess] E
|
||||
# 117| 5: [FieldDeclaration] E C;
|
||||
# 116| 1: [Javadoc] /** Javadoc for enum constant */
|
||||
# 117| 0: [JavadocText] Javadoc for enum constant
|
||||
# 120| -1: [TypeAccess] E
|
||||
# 120| 0: [ClassInstanceExpr] new E(...)
|
||||
# 120| -3: [TypeAccess] E
|
||||
# 121| 5: [FieldDeclaration] E C;
|
||||
#-----| -3: (Javadoc)
|
||||
# 112| 1: [Javadoc] /** Javadoc for enum constant */
|
||||
# 113| 0: [JavadocText] Javadoc for enum constant
|
||||
# 117| -1: [TypeAccess] E
|
||||
# 117| 0: [ClassInstanceExpr] new E(...)
|
||||
# 117| -3: [TypeAccess] E
|
||||
# 123| 11: [FieldDeclaration] int i, ...;
|
||||
# 116| 1: [Javadoc] /** Javadoc for enum constant */
|
||||
# 117| 0: [JavadocText] Javadoc for enum constant
|
||||
# 121| -1: [TypeAccess] E
|
||||
# 121| 0: [ClassInstanceExpr] new E(...)
|
||||
# 121| -3: [TypeAccess] E
|
||||
# 127| 11: [FieldDeclaration] int i, ...;
|
||||
#-----| -3: (Javadoc)
|
||||
# 120| 1: [Javadoc] /** Javadoc for fields */
|
||||
# 121| 0: [JavadocText] Javadoc for fields
|
||||
# 123| -1: [TypeAccess] int
|
||||
# 124| 1: [Javadoc] /** Javadoc for fields */
|
||||
# 125| 0: [JavadocText] Javadoc for fields
|
||||
# 127| -1: [TypeAccess] int
|
||||
# 130| 2: [Class] Inner
|
||||
# 130| 2: [FieldDeclaration] String field;
|
||||
# 131| 3: [Class] Middle
|
||||
# 131| 2: [FieldDeclaration] Inner inner;
|
||||
|
||||
Reference in New Issue
Block a user