Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit daccd04

Browse files
committed
Basic extraction of record patterns
1 parent 293cc67 commit daccd04

10 files changed

Lines changed: 93 additions & 42 deletions

File tree

java/ql/lib/config/semmlecode.dbscheme

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -774,6 +774,7 @@ case @expr.kind of
774774
| 86 = @valueeqexpr
775775
| 87 = @valueneexpr
776776
| 88 = @propertyref
777+
| 89 = @recordpatternexpr
777778
;
778779

779780
/** Holds if this `when` expression was written as an `if` expression. */

java/ql/lib/semmle/code/java/ControlFlowGraph.qll

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -529,7 +529,7 @@ private module ControlFlowGraphImpl {
529529
or
530530
this instanceof LocalVariableDeclExpr and
531531
not this = any(InstanceOfExpr ioe).getLocalVariableDeclExpr() and
532-
not this = any(PatternCase pc).getDecl()
532+
not this = any(PatternCase pc).getPattern()
533533
or
534534
this instanceof StringTemplateExpr
535535
or
@@ -971,7 +971,7 @@ private module ControlFlowGraphImpl {
971971
(
972972
if exists(pc.getGuard())
973973
then last(pc.getGuard(), last, BooleanCompletion(true, _))
974-
else last = pc.getDecl()
974+
else last = pc.getPattern()
975975
) and
976976
not pc.isRule() and
977977
completion = NormalCompletion()
@@ -1332,7 +1332,7 @@ private module ControlFlowGraphImpl {
13321332
last(case.(PatternCase).getGuard(), preBodyNode, completion) and
13331333
completion = basicBooleanCompletion(true)
13341334
) else (
1335-
preBodyNode = case.(PatternCase).getDecl() and completion = NormalCompletion()
1335+
preBodyNode = case.(PatternCase).getPattern() and completion = NormalCompletion()
13361336
)
13371337
) else (
13381338
preBodyNode = case and completion = NormalCompletion()
@@ -1351,7 +1351,7 @@ private module ControlFlowGraphImpl {
13511351
result = getASuccessorSwitchCase(case)
13521352
or
13531353
completion = basicBooleanCompletion(true) and
1354-
result = case.getDecl()
1354+
result = case.getPattern()
13551355
)
13561356
)
13571357
or
@@ -1362,7 +1362,7 @@ private module ControlFlowGraphImpl {
13621362
exists(PatternCase case, Expr guard |
13631363
guard = case.getGuard() and
13641364
(
1365-
n = case.getDecl() and
1365+
n = case.getPattern() and
13661366
result = first(guard) and
13671367
completion = NormalCompletion()
13681368
or

java/ql/lib/semmle/code/java/Dependency.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ predicate depends(RefType t, RefType dep) {
8282
or
8383
// the type accessed in a pattern-switch case statement in `t`.
8484
exists(PatternCase pc | t = pc.getEnclosingCallable().getDeclaringType() |
85-
usesType(pc.getDecl().getType(), dep)
85+
usesType(pc.getPattern().getType(), dep)
8686
)
8787
)
8888
}

java/ql/lib/semmle/code/java/Expr.qll

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2541,3 +2541,10 @@ class NotNullExpr extends UnaryExpr, @notnullexpr {
25412541

25422542
override string getAPrimaryQlClass() { result = "NotNullExpr" }
25432543
}
2544+
2545+
/** A record pattern expr, as in `if (x instanceof SomeRecord(int field))`. */
2546+
class RecordPatternExpr extends Expr, @recordpatternexpr {
2547+
override string toString() { result = this.getType().toString() + "(...)" }
2548+
2549+
override string getAPrimaryQlClass() { result = "RecordPatternExpr" }
2550+
}

java/ql/lib/semmle/code/java/Statement.qll

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -507,7 +507,7 @@ class SwitchCase extends Stmt, @case {
507507
class ConstCase extends SwitchCase {
508508
ConstCase() {
509509
exists(Expr e |
510-
e.getParent() = this and e.getIndex() >= 0 and not e instanceof LocalVariableDeclExpr
510+
e.getParent() = this and e.getIndex() >= 0 and not e instanceof Pattern
511511
)
512512
// For backward compatibility, we don't include `case null, default:` here, on the assumption
513513
// this will come as a surprise to CodeQL that predates that statement's validity.
@@ -531,14 +531,35 @@ class ConstCase extends SwitchCase {
531531
override string getAPrimaryQlClass() { result = "ConstCase" }
532532
}
533533

534+
/**
535+
* A binding or record pattern.
536+
*
537+
* Note binding patterns are represented as `LocalVariableDeclExpr`s.
538+
*/
539+
class Pattern extends Expr {
540+
Pattern() {
541+
(this.getParent() instanceof SwitchCase or this.getParent() instanceof InstanceOfExpr)
542+
and
543+
(this instanceof LocalVariableDeclExpr or this instanceof RecordPatternExpr)
544+
}
545+
546+
LocalVariableDeclExpr asBindingPattern() {
547+
result = this
548+
}
549+
550+
RecordPatternExpr asRecordPattern() {
551+
result = this
552+
}
553+
}
554+
534555
/** A pattern case of a `switch` statement */
535556
class PatternCase extends SwitchCase {
536-
LocalVariableDeclExpr patternVar;
557+
Pattern pattern;
537558

538-
PatternCase() { patternVar.isNthChildOf(this, 0) }
559+
PatternCase() { pattern.isNthChildOf(this, 0) }
539560

540-
/** Gets the variable declared by this pattern case. */
541-
LocalVariableDeclExpr getDecl() { result.isNthChildOf(this, 0) }
561+
/** Gets this case's pattern. */
562+
Pattern getPattern() { result.isNthChildOf(this, 0) }
542563

543564
/** Gets the guard applicable to this pattern case, if any. */
544565
Expr getGuard() { result.isNthChildOf(this, -3) }

java/ql/lib/semmle/code/java/controlflow/Guards.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ private predicate switchCaseControls(SwitchCase sc, BasicBlock bb) {
180180
selector = sc.getSelectorExpr() and
181181
(
182182
if sc instanceof PatternCase
183-
then caseblock.getFirstNode() = sc.(PatternCase).getDecl().getControlFlowNode()
183+
then caseblock.getFirstNode() = sc.(PatternCase).getPattern().getControlFlowNode()
184184
else (
185185
caseblock.getFirstNode() = sc.getControlFlowNode() and
186186
// Check there is no fall-through edge from a previous case:

java/ql/lib/semmle/code/java/dataflow/TypeFlow.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,7 @@ private predicate patternCaseGuarded(Expr e, RefType t) {
447447
exists(PatternCase pc |
448448
e = getAProbableAlias([pc.getSwitch().getExpr(), pc.getSwitchExpr().getExpr()]) and
449449
guardControls_v1(pc, e.getBasicBlock(), true) and
450-
t = pc.getDecl().getType()
450+
t = pc.getPattern().getType()
451451
)
452452
}
453453

java/ql/lib/semmle/code/java/dispatch/ObjFlow.qll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ private predicate step(Node n1, Node n2) {
8282
)
8383
or
8484
exists(PatternCase pc |
85-
pc.getDecl() = def.(BaseSsaUpdate).getDefiningExpr() and
85+
pc.getPattern() = def.(BaseSsaUpdate).getDefiningExpr() and
8686
(
8787
pc.getSwitch().getExpr() = n1.asExpr()
8888
or

java/ql/test/library-tests/printAst/A.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,10 @@ void varDecls(Object[] things) {
101101
case E.B -> "It's E.B";
102102
default -> "It's something else";
103103
};
104+
var recordPatterntest = switch(thing) {
105+
case Middle(Inner(String field)) -> field;
106+
default -> "Doesn't match pattern Middle(Inner(...))";
107+
};
104108
}
105109
}
106110
catch (RuntimeException rte) {
@@ -122,3 +126,6 @@ enum E {
122126
*/
123127
int i, j, k;
124128
}
129+
130+
record Inner(String field) { }
131+
record Middle(Inner inner) { }

java/ql/test/library-tests/printAst/PrintAst.expected

Lines changed: 43 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -283,36 +283,51 @@ A.java:
283283
# 101| -1: [TypeAccess] E
284284
# 102| 2: [DefaultCase] default
285285
# 102| -1: [StringLiteral] "It's something else"
286-
# 106| 0: [CatchClause] catch (...)
286+
# 104| 11: [LocalVariableDeclStmt] var ...;
287+
# 104| 1: [LocalVariableDeclExpr] recordPatterntest
288+
# 104| 0: [SwitchExpr] switch (...)
289+
# 104| -1: [VarAccess] thing
290+
# 105| 0: [PatternCase] case T t ...
291+
# 105| -1: [VarAccess] field
292+
# 105| 0: [RecordPatternExpr] Middle(...)
293+
# 105| 0: [RecordPatternExpr] Inner(...)
294+
# 105| 0: [LocalVariableDeclExpr] field
295+
# 106| 1: [DefaultCase] default
296+
# 106| -1: [StringLiteral] "Doesn't match pattern Middle(Inner(...))"
297+
# 110| 0: [CatchClause] catch (...)
287298
#-----| 0: (Single Local Variable Declaration)
288-
# 106| 0: [TypeAccess] RuntimeException
289-
# 106| 1: [LocalVariableDeclExpr] rte
290-
# 106| 1: [BlockStmt] { ... }
291-
# 107| 0: [ReturnStmt] return ...
292-
# 111| 10: [Class] E
293-
# 115| 3: [FieldDeclaration] E A;
299+
# 110| 0: [TypeAccess] RuntimeException
300+
# 110| 1: [LocalVariableDeclExpr] rte
301+
# 110| 1: [BlockStmt] { ... }
302+
# 111| 0: [ReturnStmt] return ...
303+
# 115| 10: [Class] E
304+
# 119| 3: [FieldDeclaration] E A;
294305
#-----| -3: (Javadoc)
295-
# 112| 1: [Javadoc] /** Javadoc for enum constant */
296-
# 113| 0: [JavadocText] Javadoc for enum constant
297-
# 115| -1: [TypeAccess] E
298-
# 115| 0: [ClassInstanceExpr] new E(...)
299-
# 115| -3: [TypeAccess] E
300-
# 116| 4: [FieldDeclaration] E B;
306+
# 116| 1: [Javadoc] /** Javadoc for enum constant */
307+
# 117| 0: [JavadocText] Javadoc for enum constant
308+
# 119| -1: [TypeAccess] E
309+
# 119| 0: [ClassInstanceExpr] new E(...)
310+
# 119| -3: [TypeAccess] E
311+
# 120| 4: [FieldDeclaration] E B;
301312
#-----| -3: (Javadoc)
302-
# 112| 1: [Javadoc] /** Javadoc for enum constant */
303-
# 113| 0: [JavadocText] Javadoc for enum constant
304-
# 116| -1: [TypeAccess] E
305-
# 116| 0: [ClassInstanceExpr] new E(...)
306-
# 116| -3: [TypeAccess] E
307-
# 117| 5: [FieldDeclaration] E C;
313+
# 116| 1: [Javadoc] /** Javadoc for enum constant */
314+
# 117| 0: [JavadocText] Javadoc for enum constant
315+
# 120| -1: [TypeAccess] E
316+
# 120| 0: [ClassInstanceExpr] new E(...)
317+
# 120| -3: [TypeAccess] E
318+
# 121| 5: [FieldDeclaration] E C;
308319
#-----| -3: (Javadoc)
309-
# 112| 1: [Javadoc] /** Javadoc for enum constant */
310-
# 113| 0: [JavadocText] Javadoc for enum constant
311-
# 117| -1: [TypeAccess] E
312-
# 117| 0: [ClassInstanceExpr] new E(...)
313-
# 117| -3: [TypeAccess] E
314-
# 123| 11: [FieldDeclaration] int i, ...;
320+
# 116| 1: [Javadoc] /** Javadoc for enum constant */
321+
# 117| 0: [JavadocText] Javadoc for enum constant
322+
# 121| -1: [TypeAccess] E
323+
# 121| 0: [ClassInstanceExpr] new E(...)
324+
# 121| -3: [TypeAccess] E
325+
# 127| 11: [FieldDeclaration] int i, ...;
315326
#-----| -3: (Javadoc)
316-
# 120| 1: [Javadoc] /** Javadoc for fields */
317-
# 121| 0: [JavadocText] Javadoc for fields
318-
# 123| -1: [TypeAccess] int
327+
# 124| 1: [Javadoc] /** Javadoc for fields */
328+
# 125| 0: [JavadocText] Javadoc for fields
329+
# 127| -1: [TypeAccess] int
330+
# 130| 2: [Class] Inner
331+
# 130| 2: [FieldDeclaration] String field;
332+
# 131| 3: [Class] Middle
333+
# 131| 2: [FieldDeclaration] Inner inner;

0 commit comments

Comments
 (0)