mirror of
https://github.com/github/codeql.git
synced 2026-01-21 18:34:46 +01:00
Merge pull request #14671 from smowton/smowton/feature/jdk21-switch-pattern-matching
Java: Add support for Java 21 language features
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
class Expr extends @expr {
|
||||
string toString() { result = "expr" }
|
||||
}
|
||||
|
||||
class LocalVariableDeclExpr extends @localvariabledeclexpr, Expr { }
|
||||
|
||||
class InstanceOfExpr extends @instanceofexpr, Expr { }
|
||||
|
||||
class SimplePatternInstanceOfExpr extends InstanceOfExpr {
|
||||
SimplePatternInstanceOfExpr() { getNthChild(this, 2) instanceof LocalVariableDeclExpr }
|
||||
}
|
||||
|
||||
class Type extends @type {
|
||||
string toString() { result = "type" }
|
||||
}
|
||||
|
||||
class ExprParent extends @exprparent {
|
||||
string toString() { result = "exprparent" }
|
||||
}
|
||||
|
||||
Expr getNthChild(ExprParent parent, int i) { exprs(result, _, _, parent, i) }
|
||||
|
||||
// Where an InstanceOfExpr has a 2nd child that is a LocalVariableDeclExpr, that expression should becomes its 0th child and the existing 0th child should become its initialiser.
|
||||
// Any RecordPatternExpr should be replaced with an error expression, as it can't be represented in the downgraded dbscheme.
|
||||
// This reverts a reorganisation of the representation of "o instanceof String s", which used to be InstanceOfExpr -0-> LocalVariableDeclExpr --init-> o
|
||||
// \-name-> s
|
||||
// It is now InstanceOfExpr --0-> o
|
||||
// \-2-> LocalVariableDeclExpr -name-> s
|
||||
//
|
||||
// Other children are unaffected.
|
||||
predicate hasNewParent(Expr e, ExprParent newParent, int newIndex) {
|
||||
exists(SimplePatternInstanceOfExpr oldParent, int oldIndex |
|
||||
e = getNthChild(oldParent, oldIndex)
|
||||
|
|
||||
oldIndex = 0 and newParent = getNthChild(oldParent, 2) and newIndex = 0
|
||||
or
|
||||
oldIndex = 1 and newParent = oldParent and newIndex = oldIndex
|
||||
or
|
||||
oldIndex = 2 and newParent = oldParent and newIndex = 0
|
||||
)
|
||||
or
|
||||
not exists(SimplePatternInstanceOfExpr oldParent | e = getNthChild(oldParent, _)) and
|
||||
exprs(e, _, _, newParent, newIndex)
|
||||
}
|
||||
|
||||
from Expr e, int oldKind, int newKind, Type typeid, ExprParent parent, int index
|
||||
where
|
||||
exprs(e, oldKind, typeid, _, _) and
|
||||
hasNewParent(e, parent, index) and
|
||||
(
|
||||
if oldKind = /* record pattern */ 89
|
||||
then newKind = /* error expression */ 74
|
||||
else oldKind = newKind
|
||||
)
|
||||
select e, newKind, typeid, parent, index
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,5 @@
|
||||
description: Remove tables for canonical constructors and `case null, default`, and the expression kind for record patterns. Also revert the representation for instanceof with a binding pattern.
|
||||
compatibility: backwards
|
||||
exprs.rel: run exprs.qlo
|
||||
isCanonicalConstr.rel: delete
|
||||
isNullDefaultCase.rel: delete
|
||||
@@ -46,7 +46,13 @@ predicate gapInChildren(Element e, int i) {
|
||||
// value should be, because kotlinc doesn't load annotation defaults and we
|
||||
// want to leave a space for another extractor to fill in the default if it
|
||||
// is able.
|
||||
not e instanceof Annotation
|
||||
not e instanceof Annotation and
|
||||
// Pattern case statements legitimately have a TypeAccess (-2) and a pattern (0) but not a rule (-1)
|
||||
not (i = -1 and e instanceof PatternCase and not e.(PatternCase).isRule()) and
|
||||
// Instanceof with a record pattern is not expected to have a type access in position 1
|
||||
not (i = 1 and e.(InstanceOfExpr).getPattern() instanceof RecordPatternExpr) and
|
||||
// RecordPatternExpr extracts type-accesses only for its LocalVariableDeclExpr children
|
||||
not (i < 0 and e instanceof RecordPatternExpr)
|
||||
}
|
||||
|
||||
predicate lateFirstChild(Element e, int i) {
|
||||
|
||||
6
java/ql/lib/change-notes/2023-11-03-jdk21-support.md
Normal file
6
java/ql/lib/change-notes/2023-11-03-jdk21-support.md
Normal file
@@ -0,0 +1,6 @@
|
||||
---
|
||||
category: minorAnalysis
|
||||
---
|
||||
* Switch cases using binding patterns and `case null[, default]` are now supported. Classes `PatternCase` and `NullDefaultCase` are introduced to represent new kinds of case statement.
|
||||
* Both switch cases and instanceof expressions using record patterns are now supported. The new class `RecordPatternExpr` is introduced to represent record patterns, and `InstanceOfExpr` gains `getPattern` to replace `getLocalVariableDeclExpr`.
|
||||
* The control-flow graph and therefore dominance information regarding switch blocks in statement context but with an expression rule (e.g. `switch(...) { case 1 -> System.out.println("Hello world!") }`) has been fixed. This reduces false positives and negatives from various queries relating to functions featuring such statements.
|
||||
@@ -576,6 +576,10 @@ lambdaKind(
|
||||
int bodyKind: int ref
|
||||
);
|
||||
|
||||
isCanonicalConstr(
|
||||
int constructorid: @constructor ref
|
||||
);
|
||||
|
||||
arrays(
|
||||
unique int id: @array,
|
||||
string nodeName: string ref,
|
||||
@@ -774,6 +778,7 @@ case @expr.kind of
|
||||
| 86 = @valueeqexpr
|
||||
| 87 = @valueneexpr
|
||||
| 88 = @propertyref
|
||||
| 89 = @recordpatternexpr
|
||||
;
|
||||
|
||||
/** Holds if this `when` expression was written as an `if` expression. */
|
||||
@@ -992,6 +997,10 @@ providesWith(
|
||||
string serviceImpl: string ref
|
||||
);
|
||||
|
||||
isNullDefaultCase(
|
||||
int id: @case ref
|
||||
);
|
||||
|
||||
/*
|
||||
* Javadoc
|
||||
*/
|
||||
|
||||
@@ -568,6 +568,10 @@
|
||||
<k>@propertyref</k>
|
||||
<v>8439</v>
|
||||
</e>
|
||||
<e>
|
||||
<k>@recordpatternexpr</k>
|
||||
<v>50</v>
|
||||
</e>
|
||||
<e>
|
||||
<k>@localvar</k>
|
||||
<v>385272</v>
|
||||
@@ -9478,6 +9482,17 @@
|
||||
</columnsizes>
|
||||
<dependencies/>
|
||||
</relation>
|
||||
<relation>
|
||||
<name>isCanonicalConstr</name>
|
||||
<cardinality>417</cardinality>
|
||||
<columnsizes>
|
||||
<e>
|
||||
<k>constructorid</k>
|
||||
<v>417</v>
|
||||
</e>
|
||||
</columnsizes>
|
||||
<dependencies/>
|
||||
</relation>
|
||||
<relation>
|
||||
<name>fielddecls</name>
|
||||
<cardinality>210035</cardinality>
|
||||
@@ -26950,5 +26965,16 @@
|
||||
</columnsizes>
|
||||
<dependencies/>
|
||||
</relation>
|
||||
<relation>
|
||||
<name>isNullDefaultCase</name>
|
||||
<cardinality>50</cardinality>
|
||||
<columnsizes>
|
||||
<e>
|
||||
<k>id</k>
|
||||
<v>50</v>
|
||||
</e>
|
||||
</columnsizes>
|
||||
<dependencies/>
|
||||
</relation>
|
||||
</stats>
|
||||
</dbstats>
|
||||
|
||||
@@ -82,6 +82,7 @@
|
||||
import java
|
||||
private import Completion
|
||||
private import controlflow.internal.Preconditions
|
||||
private import controlflow.internal.SwitchCases
|
||||
|
||||
/** A node in the expression-level control-flow graph. */
|
||||
class ControlFlowNode extends Top, @exprparent {
|
||||
@@ -317,6 +318,8 @@ private module ControlFlowGraphImpl {
|
||||
whenexpr.getBranch(_).getAResult() = b
|
||||
)
|
||||
or
|
||||
b = any(PatternCase pc).getGuard()
|
||||
or
|
||||
inBooleanContext(b.(ExprStmt).getExpr())
|
||||
or
|
||||
inBooleanContext(b.(StmtExpr).getStmt())
|
||||
@@ -434,6 +437,73 @@ private module ControlFlowGraphImpl {
|
||||
)
|
||||
}
|
||||
|
||||
// Join order engineering -- first determine the switch block and the case indices required, then retrieve them.
|
||||
bindingset[switch, i]
|
||||
pragma[inline_late]
|
||||
private predicate isNthCaseOf(SwitchBlock switch, SwitchCase c, int i) {
|
||||
c.isNthCaseOf(switch, i)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `SwitchCase` that may be `pred`'s direct successor, where `pred` is declared in block `switch`.
|
||||
*
|
||||
* This means any switch case that comes after `pred` up to the next pattern case, if any, except for `case null`.
|
||||
*
|
||||
* Because we know the switch block contains at least one pattern, we know by https://docs.oracle.com/javase/specs/jls/se21/html/jls-14.html#jls-14.11
|
||||
* that any default case comes after the last pattern case.
|
||||
*/
|
||||
private SwitchCase getASuccessorSwitchCase(PatternCase pred, SwitchBlock switch) {
|
||||
// Note we do include `case null, default` (as well as plain old `default`) here.
|
||||
not result.(ConstCase).getValue(_) instanceof NullLiteral and
|
||||
exists(int maxCaseIndex |
|
||||
switch = pred.getParent() and
|
||||
if exists(getNextPatternCase(pred))
|
||||
then maxCaseIndex = getNextPatternCase(pred).getCaseIndex()
|
||||
else maxCaseIndex = lastCaseIndex(switch)
|
||||
|
|
||||
isNthCaseOf(switch, result, [pred.getCaseIndex() + 1 .. maxCaseIndex])
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a `SwitchCase` that may occur first in `switch`.
|
||||
*
|
||||
* If the block contains at least one PatternCase, this is any case up to and including that case, or
|
||||
* the case handling the null literal if any.
|
||||
*
|
||||
* Otherwise it is any case in the switch block.
|
||||
*/
|
||||
private SwitchCase getAFirstSwitchCase(SwitchBlock switch) {
|
||||
result.getParent() = switch and
|
||||
(
|
||||
result.(ConstCase).getValue(_) instanceof NullLiteral
|
||||
or
|
||||
result instanceof NullDefaultCase
|
||||
or
|
||||
not exists(getFirstPatternCase(switch))
|
||||
or
|
||||
result.getIndex() <= getFirstPatternCase(switch).getIndex()
|
||||
)
|
||||
}
|
||||
|
||||
private Stmt getSwitchStatement(SwitchBlock switch, int i) { result.isNthChildOf(switch, i) }
|
||||
|
||||
/**
|
||||
* Holds if `last` is the last node in a pattern case `pc`'s succeeding bind-and-test operation,
|
||||
* immediately before either falling through to execute successor statements or execute a rule body
|
||||
* if present. `completion` is the completion kind of the last operation.
|
||||
*/
|
||||
private predicate lastPatternCaseMatchingOp(
|
||||
PatternCase pc, ControlFlowNode last, Completion completion
|
||||
) {
|
||||
last(pc.getPattern(), last, completion) and
|
||||
completion = NormalCompletion() and
|
||||
not exists(pc.getGuard())
|
||||
or
|
||||
last(pc.getGuard(), last, completion) and
|
||||
completion = BooleanCompletion(true, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Expressions and statements with CFG edges in post-order AST traversal.
|
||||
*
|
||||
@@ -466,8 +536,7 @@ private module ControlFlowGraphImpl {
|
||||
or
|
||||
this instanceof NotInstanceOfExpr
|
||||
or
|
||||
this instanceof LocalVariableDeclExpr and
|
||||
not this = any(InstanceOfExpr ioe).getLocalVariableDeclExpr()
|
||||
this instanceof LocalVariableDeclExpr
|
||||
or
|
||||
this instanceof StringTemplateExpr
|
||||
or
|
||||
@@ -493,7 +562,11 @@ private module ControlFlowGraphImpl {
|
||||
or
|
||||
this.(BlockStmt).getNumStmt() = 0
|
||||
or
|
||||
this instanceof SwitchCase and not this.(SwitchCase).isRule()
|
||||
this instanceof SwitchCase and
|
||||
not this.(SwitchCase).isRule() and
|
||||
not this instanceof PatternCase
|
||||
or
|
||||
this instanceof RecordPatternExpr
|
||||
or
|
||||
this instanceof EmptyStmt
|
||||
or
|
||||
@@ -571,6 +644,8 @@ private module ControlFlowGraphImpl {
|
||||
index = 0 and result = this.(ThrowStmt).getExpr()
|
||||
or
|
||||
index = 0 and result = this.(AssertStmt).getExpr()
|
||||
or
|
||||
result = this.(RecordPatternExpr).getSubPattern(index)
|
||||
}
|
||||
|
||||
/** Gets the first child node, if any. */
|
||||
@@ -711,7 +786,7 @@ private module ControlFlowGraphImpl {
|
||||
*/
|
||||
private predicate last(ControlFlowNode n, ControlFlowNode last, Completion completion) {
|
||||
// Exceptions are propagated from any sub-expression.
|
||||
// As are any break, continue, or return completions.
|
||||
// As are any break, yield, continue, or return completions.
|
||||
exists(Expr e | e.getParent() = n |
|
||||
last(e, last, completion) and not completion instanceof NormalOrBooleanCompletion
|
||||
)
|
||||
@@ -726,7 +801,10 @@ private module ControlFlowGraphImpl {
|
||||
or
|
||||
last(n, last, BooleanCompletion(_, _)) and
|
||||
not inBooleanContext(n) and
|
||||
completion = NormalCompletion()
|
||||
completion = NormalCompletion() and
|
||||
// PatternCase has both a boolean-true completion (guard success) and a normal one
|
||||
// (variable declaration completion, when no guard is present).
|
||||
not n instanceof PatternCase
|
||||
or
|
||||
// Logic expressions and conditional expressions are executed in AST pre-order to facilitate
|
||||
// proper short-circuit representation. All other expressions are executed in post-order.
|
||||
@@ -765,7 +843,7 @@ private module ControlFlowGraphImpl {
|
||||
exists(InstanceOfExpr ioe | ioe.isPattern() and ioe = n |
|
||||
last = n and completion = basicBooleanCompletion(false)
|
||||
or
|
||||
last = ioe.getLocalVariableDeclExpr() and completion = basicBooleanCompletion(true)
|
||||
last(ioe.getPattern(), last, NormalCompletion()) and completion = basicBooleanCompletion(true)
|
||||
)
|
||||
or
|
||||
// The last node of a node executed in post-order is the node itself.
|
||||
@@ -847,14 +925,19 @@ private module ControlFlowGraphImpl {
|
||||
// any other abnormal completion is propagated
|
||||
last(switch.getAStmt(), last, completion) and
|
||||
completion != anonymousBreakCompletion() and
|
||||
completion != NormalCompletion()
|
||||
not completion instanceof NormalOrBooleanCompletion
|
||||
or
|
||||
// if the last case completes normally, then so does the switch
|
||||
last(switch.getStmt(strictcount(switch.getAStmt()) - 1), last, NormalCompletion()) and
|
||||
completion = NormalCompletion()
|
||||
or
|
||||
// if no default case exists, then normal completion of the expression may terminate the switch
|
||||
// Note this can't happen if there are pattern cases or a null literal, as
|
||||
// https://docs.oracle.com/javase/specs/jls/se21/html/jls-14.html#jls-14.11.2 requires that such
|
||||
// an enhanced switch block is exhaustive.
|
||||
not exists(switch.getDefaultCase()) and
|
||||
not exists(switch.getAPatternCase()) and
|
||||
not switch.hasNullCase() and
|
||||
last(switch.getExpr(), last, completion) and
|
||||
completion = NormalCompletion()
|
||||
)
|
||||
@@ -867,24 +950,42 @@ private module ControlFlowGraphImpl {
|
||||
// any other abnormal completion is propagated
|
||||
last(switch.getAStmt(), last, completion) and
|
||||
not completion instanceof YieldCompletion and
|
||||
completion != NormalCompletion()
|
||||
not completion instanceof NormalOrBooleanCompletion
|
||||
)
|
||||
or
|
||||
// the last node in a case rule is the last node in the right-hand side
|
||||
// if the rhs is a statement we wrap the completion as a break
|
||||
exists(Completion caseCompletion |
|
||||
last(n.(SwitchCase).getRuleStatement(), last, caseCompletion) and
|
||||
// If a case rule right-hand-side completes then the switch breaks or yields, depending
|
||||
// on whether this is a switch expression or statement. If it completes abruptly then the
|
||||
// switch completes the same way.
|
||||
exists(Completion caseCompletion, SwitchCase case |
|
||||
case = n and
|
||||
(
|
||||
last(case.getRuleStatement(), last, caseCompletion)
|
||||
or
|
||||
last(case.getRuleExpression(), last, caseCompletion)
|
||||
)
|
||||
|
|
||||
if caseCompletion instanceof NormalOrBooleanCompletion
|
||||
then completion = anonymousBreakCompletion()
|
||||
then
|
||||
case.getParent() instanceof SwitchStmt and completion = anonymousBreakCompletion()
|
||||
or
|
||||
case.getParent() instanceof SwitchExpr and completion = YieldCompletion(caseCompletion)
|
||||
else completion = caseCompletion
|
||||
)
|
||||
or
|
||||
// ...and if the rhs is an expression we wrap the completion as a yield
|
||||
exists(Completion caseCompletion |
|
||||
last(n.(SwitchCase).getRuleExpression(), last, caseCompletion) and
|
||||
if caseCompletion instanceof NormalOrBooleanCompletion
|
||||
then completion = YieldCompletion(caseCompletion)
|
||||
else completion = caseCompletion
|
||||
// A pattern case statement can complete:
|
||||
// * On failure of its type test (boolean false)
|
||||
// * On failure of its guard test if any (boolean false)
|
||||
// * On completion of its variable declarations, if it is not a rule and has no guard (normal completion)
|
||||
// * On success of its guard test, if it is not a rule (boolean true)
|
||||
// (the latter two cases are accounted for by lastPatternCaseMatchingOp)
|
||||
exists(PatternCase pc | n = pc |
|
||||
last = pc and completion = basicBooleanCompletion(false)
|
||||
or
|
||||
last(pc.getGuard(), last, completion) and
|
||||
completion = BooleanCompletion(false, _)
|
||||
or
|
||||
not pc.isRule() and
|
||||
lastPatternCaseMatchingOp(pc, last, completion)
|
||||
)
|
||||
or
|
||||
// the last statement of a synchronized statement is the last statement of its body
|
||||
@@ -1035,7 +1136,7 @@ private module ControlFlowGraphImpl {
|
||||
last(ioe.getExpr(), n, completion) and completion = NormalCompletion() and result = ioe
|
||||
or
|
||||
n = ioe and
|
||||
result = ioe.getLocalVariableDeclExpr() and
|
||||
result = first(ioe.getPattern()) and
|
||||
completion = basicBooleanCompletion(true)
|
||||
)
|
||||
or
|
||||
@@ -1196,39 +1297,67 @@ private module ControlFlowGraphImpl {
|
||||
last(cc.getVariable(), n, completion) and result = first(cc.getBlock())
|
||||
)
|
||||
or
|
||||
// Switch statements
|
||||
exists(SwitchStmt switch | completion = NormalCompletion() |
|
||||
// From the entry point control is transferred first to the expression...
|
||||
n = switch and result = first(switch.getExpr())
|
||||
or
|
||||
// ...and then to one of the cases.
|
||||
last(switch.getExpr(), n, completion) and result = first(switch.getACase())
|
||||
// Switch statements and expressions
|
||||
exists(SwitchBlock switch |
|
||||
exists(Expr switchExpr |
|
||||
switchExpr = switch.(SwitchStmt).getExpr() or switchExpr = switch.(SwitchExpr).getExpr()
|
||||
|
|
||||
// From the entry point control is transferred first to the expression...
|
||||
n = switch and result = first(switchExpr) and completion = NormalCompletion()
|
||||
or
|
||||
// ...and then to any case up to and including the first pattern case, if any.
|
||||
last(switchExpr, n, completion) and
|
||||
result = first(getAFirstSwitchCase(switch)) and
|
||||
completion = NormalCompletion()
|
||||
)
|
||||
or
|
||||
// Statements within a switch body execute sequentially.
|
||||
// Note this includes non-rule case statements and the successful pattern match successor
|
||||
// of a non-rule pattern case statement. Rule case statements do not complete normally
|
||||
// (they always break or yield).
|
||||
exists(int i |
|
||||
last(switch.getStmt(i), n, completion) and result = first(switch.getStmt(i + 1))
|
||||
last(getSwitchStatement(switch, i), n, completion) and
|
||||
result = first(getSwitchStatement(switch, i + 1)) and
|
||||
(completion = NormalCompletion() or completion = BooleanCompletion(true, _))
|
||||
)
|
||||
or
|
||||
// A pattern case that completes boolean false (type test or guard failure) continues to consider other cases:
|
||||
exists(PatternCase case | completion = BooleanCompletion(false, _) |
|
||||
last(case, n, completion) and result = getASuccessorSwitchCase(case, switch)
|
||||
)
|
||||
)
|
||||
or
|
||||
// Switch expressions
|
||||
exists(SwitchExpr switch | completion = NormalCompletion() |
|
||||
// From the entry point control is transferred first to the expression...
|
||||
n = switch and result = first(switch.getExpr())
|
||||
// Pattern cases have internal edges:
|
||||
// * Type test success -true-> variable declarations
|
||||
// * Variable declarations -normal-> guard evaluation
|
||||
// * Variable declarations -normal-> rule execution (when there is no guard)
|
||||
// * Guard success -true-> rule execution
|
||||
exists(PatternCase pc |
|
||||
n = pc and
|
||||
completion = basicBooleanCompletion(true) and
|
||||
result = first(pc.getPattern())
|
||||
or
|
||||
// ...and then to one of the cases.
|
||||
last(switch.getExpr(), n, completion) and result = first(switch.getACase())
|
||||
last(pc.getPattern(), n, completion) and
|
||||
completion = NormalCompletion() and
|
||||
result = first(pc.getGuard())
|
||||
or
|
||||
// Statements within a switch body execute sequentially.
|
||||
exists(int i |
|
||||
last(switch.getStmt(i), n, completion) and result = first(switch.getStmt(i + 1))
|
||||
lastPatternCaseMatchingOp(pc, n, completion) and
|
||||
(
|
||||
result = first(pc.getRuleExpression())
|
||||
or
|
||||
result = first(pc.getRuleStatement())
|
||||
)
|
||||
)
|
||||
or
|
||||
// No edges in a non-rule SwitchCase - the constant expression in a ConstCase isn't included in the CFG.
|
||||
exists(SwitchCase case | completion = NormalCompletion() |
|
||||
n = case and result = first(case.getRuleExpression())
|
||||
or
|
||||
n = case and result = first(case.getRuleStatement())
|
||||
// Non-pattern cases have an internal edge leading to their rule body if any when the case matches.
|
||||
exists(SwitchCase case | n = case |
|
||||
not case instanceof PatternCase and
|
||||
completion = NormalCompletion() and
|
||||
(
|
||||
result = first(case.getRuleExpression())
|
||||
or
|
||||
result = first(case.getRuleStatement())
|
||||
)
|
||||
)
|
||||
or
|
||||
// Yield
|
||||
@@ -1365,5 +1494,5 @@ class ConditionNode extends ControlFlowNode {
|
||||
ControlFlowNode getAFalseSuccessor() { result = this.getABranchSuccessor(false) }
|
||||
|
||||
/** Gets the condition of this `ConditionNode`. This is equal to the node itself. */
|
||||
Expr getCondition() { result = this }
|
||||
ExprParent getCondition() { result = this }
|
||||
}
|
||||
|
||||
@@ -78,6 +78,13 @@ predicate depends(RefType t, RefType dep) {
|
||||
// the type accessed in an `instanceof` expression in `t`.
|
||||
exists(InstanceOfExpr ioe | t = ioe.getEnclosingCallable().getDeclaringType() |
|
||||
usesType(ioe.getCheckedType(), dep)
|
||||
or
|
||||
usesType(ioe.getPattern().getAChildExpr*().getType(), dep)
|
||||
)
|
||||
or
|
||||
// A type accessed in a pattern-switch case statement in `t`.
|
||||
exists(PatternCase pc | t = pc.getEnclosingCallable().getDeclaringType() |
|
||||
usesType(pc.getPattern().getAChildExpr*().getType(), dep)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -101,6 +101,13 @@ predicate numDepends(RefType t, RefType dep, int value) {
|
||||
t = ioe.getEnclosingCallable().getDeclaringType()
|
||||
|
|
||||
usesType(ioe.getCheckedType(), dep)
|
||||
or
|
||||
usesType(ioe.getPattern().getAChildExpr*().getType(), dep)
|
||||
)
|
||||
or
|
||||
// the type accessed in a pattern-switch case statement in `t`.
|
||||
exists(PatternCase pc | elem = pc and t = pc.getEnclosingCallable().getDeclaringType() |
|
||||
usesType(pc.getPattern().getAChildExpr*().getType(), dep)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1509,17 +1509,33 @@ class SwitchExpr extends Expr, StmtParent, @switchexpr {
|
||||
*/
|
||||
Stmt getStmt(int index) { result = this.getAStmt() and result.getIndex() = index }
|
||||
|
||||
/**
|
||||
* Gets the `i`th case of this `switch` expression,
|
||||
* which may be either a normal `case` or a `default`.
|
||||
*/
|
||||
SwitchCase getCase(int i) {
|
||||
result =
|
||||
rank[i + 1](SwitchCase case, int idx | case.isNthChildOf(this, idx) | case order by idx)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a case of this `switch` expression,
|
||||
* which may be either a normal `case` or a `default`.
|
||||
*/
|
||||
SwitchCase getACase() { result = this.getAConstCase() or result = this.getDefaultCase() }
|
||||
SwitchCase getACase() { result.getParent() = this }
|
||||
|
||||
/** Gets a (non-default) `case` of this `switch` expression. */
|
||||
ConstCase getAConstCase() { result.getParent() = this }
|
||||
ConstCase getAConstCase() { result = this.getACase() }
|
||||
|
||||
/** Gets the `default` case of this switch expression, if any. */
|
||||
DefaultCase getDefaultCase() { result.getParent() = this }
|
||||
/** Gets a (non-default) pattern `case` of this `switch` expression. */
|
||||
PatternCase getAPatternCase() { result = this.getACase() }
|
||||
|
||||
/**
|
||||
* Gets the `default` case of this switch statement, if any.
|
||||
*
|
||||
* Note this may be `default` or `case null, default`.
|
||||
*/
|
||||
DefaultCase getDefaultCase() { result = this.getACase() }
|
||||
|
||||
/** Gets the expression of this `switch` expression. */
|
||||
Expr getExpr() { result.getParent() = this }
|
||||
@@ -1531,6 +1547,12 @@ class SwitchExpr extends Expr, StmtParent, @switchexpr {
|
||||
exists(YieldStmt yield | yield.getTarget() = this and result = yield.getValue())
|
||||
}
|
||||
|
||||
/** Holds if this switch has a case handling a null literal. */
|
||||
predicate hasNullCase() {
|
||||
this.getAConstCase().getValue(_) instanceof NullLiteral or
|
||||
this.getACase() instanceof NullDefaultCase
|
||||
}
|
||||
|
||||
/** Gets a printable representation of this expression. */
|
||||
override string toString() { result = "switch (...)" }
|
||||
|
||||
@@ -1540,27 +1562,61 @@ class SwitchExpr extends Expr, StmtParent, @switchexpr {
|
||||
/** An `instanceof` expression. */
|
||||
class InstanceOfExpr extends Expr, @instanceofexpr {
|
||||
/** Gets the expression on the left-hand side of the `instanceof` operator. */
|
||||
Expr getExpr() {
|
||||
if this.isPattern()
|
||||
then result = this.getLocalVariableDeclExpr().getInit()
|
||||
else result.isNthChildOf(this, 0)
|
||||
}
|
||||
Expr getExpr() { result.isNthChildOf(this, 0) }
|
||||
|
||||
/**
|
||||
* Gets the pattern of an `x instanceof T pattern` expression, if any.
|
||||
*/
|
||||
PatternExpr getPattern() { result.isNthChildOf(this, 2) }
|
||||
|
||||
/**
|
||||
* Holds if this `instanceof` expression uses pattern matching.
|
||||
*/
|
||||
predicate isPattern() { exists(this.getLocalVariableDeclExpr()) }
|
||||
predicate isPattern() { exists(this.getPattern()) }
|
||||
|
||||
/**
|
||||
* Gets the local variable declaration of this `instanceof` expression if pattern matching is used.
|
||||
* Gets the local variable declaration of this `instanceof` expression if simple pattern matching is used.
|
||||
*
|
||||
* Note that this won't get anything when record pattern matching is used-- for more general patterns,
|
||||
* use `getPattern`.
|
||||
*/
|
||||
LocalVariableDeclExpr getLocalVariableDeclExpr() { result.isNthChildOf(this, 0) }
|
||||
LocalVariableDeclExpr getLocalVariableDeclExpr() { result = this.getPattern().asBindingPattern() }
|
||||
|
||||
/** Gets the access to the type on the right-hand side of the `instanceof` operator. */
|
||||
/**
|
||||
* Gets the access to the type on the right-hand side of the `instanceof` operator.
|
||||
*
|
||||
* This does not match record patterns, which have a record pattern (use `getPattern`) not a type access.
|
||||
*/
|
||||
Expr getTypeName() { result.isNthChildOf(this, 1) }
|
||||
|
||||
/** Gets the type this `instanceof` expression checks for. */
|
||||
RefType getCheckedType() { result = this.getTypeName().getType() }
|
||||
/**
|
||||
* Gets the type this `instanceof` expression checks for.
|
||||
*
|
||||
* For a match against a record pattern, this is the type of the outermost record type, and only holds if
|
||||
* the record pattern matches that type unconditionally, i.e. it does not restrict field types more tightly
|
||||
* than the fields' declared types and therefore match a subset of `rpe.getType()`.
|
||||
*/
|
||||
RefType getCheckedType() {
|
||||
result = this.getTypeName().getType()
|
||||
or
|
||||
exists(RecordPatternExpr rpe | rpe = this.getPattern().asRecordPattern() |
|
||||
result = rpe.getType() and rpe.isUnrestricted()
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type this `instanceof` expression checks for.
|
||||
*
|
||||
* For a match against a record pattern, this is the type of the outermost record type. Note that because
|
||||
* the record match might additionally constrain field or sub-record fields to have a more specific type,
|
||||
* and so while if the `instanceof` test passes we know that `this.getExpr()` has this type, if it fails
|
||||
* we do not know that it doesn't.
|
||||
*/
|
||||
RefType getSyntacticCheckedType() {
|
||||
result = this.getTypeName().getType()
|
||||
or
|
||||
result = this.getPattern().asRecordPattern().getType()
|
||||
}
|
||||
|
||||
/** Gets a printable representation of this expression. */
|
||||
override string toString() { result = "...instanceof..." }
|
||||
@@ -1592,7 +1648,9 @@ class NotInstanceOfExpr extends Expr, @notinstanceofexpr {
|
||||
* A local variable declaration expression.
|
||||
*
|
||||
* Contexts in which such expressions may occur include
|
||||
* local variable declaration statements and `for` loops.
|
||||
* local variable declaration statements, `for` loops,
|
||||
* and binding patterns such as `if (x instanceof T t)` and
|
||||
* `case String s:`.
|
||||
*/
|
||||
class LocalVariableDeclExpr extends Expr, @localvariabledeclexpr {
|
||||
/** Gets an access to the variable declared by this local variable declaration expression. */
|
||||
@@ -1612,18 +1670,86 @@ class LocalVariableDeclExpr extends Expr, @localvariabledeclexpr {
|
||||
exists(EnhancedForStmt efs | efs.getVariable() = this | result.isNthChildOf(efs, -1))
|
||||
or
|
||||
exists(InstanceOfExpr ioe | this.getParent() = ioe | result.isNthChildOf(ioe, 1))
|
||||
or
|
||||
exists(PatternCase pc | this.getParent() = pc | result.isNthChildOf(pc, -2))
|
||||
or
|
||||
exists(RecordPatternExpr rpe, int index |
|
||||
this.isNthChildOf(rpe, index) and result.isNthChildOf(rpe, -(index + 1))
|
||||
)
|
||||
}
|
||||
|
||||
/** Gets the name of the variable declared by this local variable declaration expression. */
|
||||
string getName() { result = this.getVariable().getName() }
|
||||
|
||||
/** Gets the initializer expression of this local variable declaration expression, if any. */
|
||||
/**
|
||||
* Gets the switch statement or expression whose pattern declares this identifier, if any.
|
||||
*/
|
||||
SwitchBlock getAssociatedSwitch() {
|
||||
exists(PatternCase pc |
|
||||
pc = result.(SwitchStmt).getAPatternCase()
|
||||
or
|
||||
pc = result.(SwitchExpr).getAPatternCase()
|
||||
|
|
||||
this = pc.getPattern().getAChildExpr*()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this is a declaration stemming from a pattern switch case. */
|
||||
predicate hasAssociatedSwitch() { exists(this.getAssociatedSwitch()) }
|
||||
|
||||
/**
|
||||
* Gets the instanceof expression whose pattern declares this identifier, if any.
|
||||
*/
|
||||
InstanceOfExpr getAssociatedInstanceOfExpr() { result.getPattern().getAChildExpr*() = this }
|
||||
|
||||
/** Holds if this is a declaration stemming from a pattern instanceof expression. */
|
||||
predicate hasAssociatedInstanceOfExpr() { exists(this.getAssociatedInstanceOfExpr()) }
|
||||
|
||||
/**
|
||||
* Gets the initializer expression of this local variable declaration expression, if any.
|
||||
*
|
||||
* Note this applies specifically to a syntactic initialization like `T varname = init`;
|
||||
* to include also `e instanceof T varname` and `switch(e) ... case T varname`, which both
|
||||
* have the effect of initializing `varname` to a known local expression without using
|
||||
* that syntax, use `getInitOrPatternSource`.
|
||||
*/
|
||||
Expr getInit() { result.isNthChildOf(this, 0) }
|
||||
|
||||
/**
|
||||
* Gets the local expression that initializes this variable declaration, if any.
|
||||
*
|
||||
* Note this includes explicit `T varname = init;`, as well as `e instanceof T varname`
|
||||
* and `switch(e) ... case T varname`. To get only explicit initializers, use `getInit`.
|
||||
*
|
||||
* Note that record pattern variables like `e instance of T Record(T varname)` do not have
|
||||
* either an explicit initializer or a pattern source.
|
||||
*/
|
||||
Expr getInitOrPatternSource() {
|
||||
result = this.getInit()
|
||||
or
|
||||
exists(SwitchStmt switch |
|
||||
result = switch.getExpr() and
|
||||
this = switch.getAPatternCase().getPattern().asBindingPattern()
|
||||
)
|
||||
or
|
||||
exists(SwitchExpr switch |
|
||||
result = switch.getExpr() and
|
||||
this = switch.getAPatternCase().getPattern().asBindingPattern()
|
||||
)
|
||||
or
|
||||
exists(InstanceOfExpr ioe |
|
||||
result = ioe.getExpr() and
|
||||
this = ioe.getPattern().asBindingPattern()
|
||||
)
|
||||
}
|
||||
|
||||
/** Holds if this variable declaration implicitly initializes the variable. */
|
||||
predicate hasImplicitInit() {
|
||||
exists(CatchClause cc | cc.getVariable() = this) or
|
||||
exists(CatchClause cc | cc.getVariable() = this)
|
||||
or
|
||||
exists(EnhancedForStmt efs | efs.getVariable() = this)
|
||||
or
|
||||
this.getParent() instanceof RecordPatternExpr
|
||||
}
|
||||
|
||||
/** Gets a printable representation of this expression. */
|
||||
@@ -1632,6 +1758,11 @@ class LocalVariableDeclExpr extends Expr, @localvariabledeclexpr {
|
||||
override string getAPrimaryQlClass() { result = "LocalVariableDeclExpr" }
|
||||
}
|
||||
|
||||
/** A local variable declaration that occurs within a record pattern. */
|
||||
class RecordBindingVariableExpr extends LocalVariableDeclExpr {
|
||||
RecordBindingVariableExpr() { this.getParent() instanceof RecordPatternExpr }
|
||||
}
|
||||
|
||||
/** An update of a variable or an initialization of the variable. */
|
||||
class VariableUpdate extends Expr {
|
||||
VariableUpdate() {
|
||||
@@ -1660,12 +1791,12 @@ class VariableAssign extends VariableUpdate {
|
||||
/**
|
||||
* Gets the source (right-hand side) of this assignment, if any.
|
||||
*
|
||||
* An initialization in a `CatchClause` or `EnhancedForStmt` is implicit and
|
||||
* does not have a source.
|
||||
* An initialization in a `CatchClause`, `EnhancedForStmt` or `RecordPatternExpr`
|
||||
* is implicit and does not have a source.
|
||||
*/
|
||||
Expr getSource() {
|
||||
result = this.(AssignExpr).getSource() or
|
||||
result = this.(LocalVariableDeclExpr).getInit()
|
||||
result = this.(LocalVariableDeclExpr).getInitOrPatternSource()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2512,3 +2643,59 @@ class NotNullExpr extends UnaryExpr, @notnullexpr {
|
||||
|
||||
override string getAPrimaryQlClass() { result = "NotNullExpr" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A binding or record pattern.
|
||||
*
|
||||
* Note binding patterns are represented as `LocalVariableDeclExpr`s.
|
||||
*/
|
||||
class PatternExpr extends Expr {
|
||||
PatternExpr() {
|
||||
(
|
||||
this.getParent() instanceof SwitchCase or
|
||||
this.getParent() instanceof InstanceOfExpr or
|
||||
this.getParent() instanceof RecordPatternExpr
|
||||
) and
|
||||
(this instanceof LocalVariableDeclExpr or this instanceof RecordPatternExpr)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this pattern cast to a binding pattern.
|
||||
*/
|
||||
LocalVariableDeclExpr asBindingPattern() { result = this }
|
||||
|
||||
/**
|
||||
* Gets this pattern cast to a record pattern.
|
||||
*/
|
||||
RecordPatternExpr asRecordPattern() { result = this }
|
||||
}
|
||||
|
||||
/** 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" }
|
||||
|
||||
/**
|
||||
* Gets the `i`th subpattern of this record pattern.
|
||||
*/
|
||||
PatternExpr getSubPattern(int i) { result.isNthChildOf(this, i) }
|
||||
|
||||
/**
|
||||
* Holds if this record pattern matches any record of its type.
|
||||
*
|
||||
* For example, for `record R(Object o) { }`, pattern `R(Object o)` is unrestricted, whereas
|
||||
* pattern `R(String s)` is not because it matches a subset of `R` instances, those containing `String`s.
|
||||
*/
|
||||
predicate isUnrestricted() {
|
||||
forall(PatternExpr subPattern, int idx | subPattern = this.getSubPattern(idx) |
|
||||
subPattern.getType() =
|
||||
this.getType().(Record).getCanonicalConstructor().getParameter(idx).getType() and
|
||||
(
|
||||
subPattern instanceof LocalVariableDeclExpr
|
||||
or
|
||||
subPattern.(RecordPatternExpr).isUnrestricted()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -737,10 +737,17 @@ class FieldDeclaration extends ExprParent, @fielddecl, Annotatable {
|
||||
/** Gets the number of fields declared in this declaration. */
|
||||
int getNumField() { result = max(int idx | fieldDeclaredIn(_, this, idx) | idx) + 1 }
|
||||
|
||||
private string stringifyType() {
|
||||
// Necessary because record fields are missing their type access.
|
||||
if exists(this.getTypeAccess())
|
||||
then result = this.getTypeAccess().toString()
|
||||
else result = this.getAField().getType().toString()
|
||||
}
|
||||
|
||||
override string toString() {
|
||||
if this.getNumField() = 1
|
||||
then result = this.getTypeAccess() + " " + this.getField(0) + ";"
|
||||
else result = this.getTypeAccess() + " " + this.getField(0) + ", ...;"
|
||||
then result = this.stringifyType() + " " + this.getField(0) + ";"
|
||||
else result = this.stringifyType() + " " + this.getField(0) + ", ...;"
|
||||
}
|
||||
|
||||
override string getAPrimaryQlClass() { result = "FieldDeclaration" }
|
||||
|
||||
@@ -383,15 +383,18 @@ private class PpInstanceOfExpr extends PpAst, InstanceOfExpr {
|
||||
override string getPart(int i) {
|
||||
i = 1 and result = " instanceof "
|
||||
or
|
||||
i = 3 and result = " " and this.isPattern()
|
||||
i = 3 and result = " " and this.getPattern() instanceof LocalVariableDeclExpr
|
||||
or
|
||||
i = 4 and result = this.getLocalVariableDeclExpr().getName()
|
||||
i = 4 and
|
||||
result = this.getPattern().asBindingPattern().getName()
|
||||
}
|
||||
|
||||
override PpAst getChild(int i) {
|
||||
i = 0 and result = this.getExpr()
|
||||
or
|
||||
i = 2 and result = this.getTypeName()
|
||||
or
|
||||
i = 2 and result = this.getPattern().asRecordPattern()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -742,11 +745,19 @@ private class PpSwitchStmt extends PpAst, SwitchStmt {
|
||||
}
|
||||
}
|
||||
|
||||
private predicate isNonNullDefaultCase(SwitchCase sc) {
|
||||
sc instanceof DefaultCase and not sc instanceof NullDefaultCase
|
||||
}
|
||||
|
||||
private class PpSwitchCase extends PpAst, SwitchCase {
|
||||
PpSwitchCase() { not this instanceof PatternCase }
|
||||
|
||||
override string getPart(int i) {
|
||||
i = 0 and result = "default" and this instanceof DefaultCase
|
||||
i = 0 and result = "default" and isNonNullDefaultCase(this)
|
||||
or
|
||||
i = 0 and result = "case " and this instanceof ConstCase
|
||||
i = 0 and result = "case null, default" and this instanceof NullDefaultCase
|
||||
or
|
||||
i = 0 and result = "case " and not this instanceof DefaultCase
|
||||
or
|
||||
exists(int j | i = 2 * j and j != 0 and result = ", " and exists(this.(ConstCase).getValue(j)))
|
||||
or
|
||||
@@ -758,7 +769,7 @@ private class PpSwitchCase extends PpAst, SwitchCase {
|
||||
}
|
||||
|
||||
private int lastConstCaseValueIndex() {
|
||||
result = 1 + 2 * max(int j | j = 0 or exists(this.(ConstCase).getValue(j)))
|
||||
result = 1 + 2 * (max(int j | j = 0 or exists(this.(ConstCase).getValue(j))))
|
||||
}
|
||||
|
||||
override PpAst getChild(int i) {
|
||||
@@ -770,6 +781,32 @@ private class PpSwitchCase extends PpAst, SwitchCase {
|
||||
}
|
||||
}
|
||||
|
||||
private class PpPatternCase extends PpAst, PatternCase {
|
||||
override string getPart(int i) {
|
||||
i = 0 and result = "case "
|
||||
or
|
||||
i = 2 and this.getPattern() instanceof LocalVariableDeclExpr and result = " "
|
||||
or
|
||||
i = 3 and result = this.getPattern().asBindingPattern().getName()
|
||||
or
|
||||
i = 4 and result = ":" and not this.isRule()
|
||||
or
|
||||
i = 4 and result = " -> " and this.isRule()
|
||||
or
|
||||
i = 6 and result = ";" and exists(this.getRuleExpression())
|
||||
}
|
||||
|
||||
override PpAst getChild(int i) {
|
||||
i = 1 and result = this.getPattern().asBindingPattern().getTypeAccess()
|
||||
or
|
||||
i = 1 and result = this.getPattern().asRecordPattern()
|
||||
or
|
||||
i = 5 and result = this.getRuleExpression()
|
||||
or
|
||||
i = 5 and result = this.getRuleStatement()
|
||||
}
|
||||
}
|
||||
|
||||
private class PpSynchronizedStmt extends PpAst, SynchronizedStmt {
|
||||
override string getPart(int i) {
|
||||
i = 0 and result = "synchronized ("
|
||||
@@ -885,6 +922,8 @@ private class PpAssertStmt extends PpAst, AssertStmt {
|
||||
|
||||
private class PpLocalVariableDeclStmt extends PpAst, LocalVariableDeclStmt {
|
||||
override string getPart(int i) {
|
||||
i = 0 and not exists(this.getAVariable().getTypeAccess()) and result = "var"
|
||||
or
|
||||
i = 1 and result = " "
|
||||
or
|
||||
exists(int v | v > 1 and i = 2 * v - 1 and result = ", " and v = this.getAVariableIndex())
|
||||
@@ -1025,3 +1064,37 @@ private class PpCallable extends PpAst, Callable {
|
||||
i = 5 + 4 * this.getNumberOfParameters() and result = this.getBody()
|
||||
}
|
||||
}
|
||||
|
||||
private class PpRecordPattern extends PpAst, RecordPatternExpr {
|
||||
override string getPart(int i) {
|
||||
i = 0 and result = this.getType().getName()
|
||||
or
|
||||
i = 1 and result = "("
|
||||
or
|
||||
i = 1 + ((any(int x | x >= 1 and exists(this.getSubPattern(x)))) * 4) and result = ", "
|
||||
or
|
||||
i = 1 + (count(this.getSubPattern(_)) * 4) and result = ")"
|
||||
or
|
||||
exists(int x, LocalVariableDeclExpr v, int offset | v = this.getSubPattern(x) |
|
||||
i = (offset) + (x * 4) and
|
||||
(
|
||||
offset = 2 and not exists(v.getTypeAccess()) and result = "var"
|
||||
or
|
||||
offset = 3 and result = " "
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override PpAst getChild(int i) {
|
||||
exists(int x, PatternExpr subPattern, int offset | subPattern = this.getSubPattern(x) |
|
||||
i = (offset) + (x * 4) and
|
||||
(
|
||||
result = subPattern.(RecordPatternExpr) and offset = 2
|
||||
or
|
||||
result = subPattern.(LocalVariableDeclExpr).getTypeAccess() and offset = 2
|
||||
or
|
||||
result = subPattern.(LocalVariableDeclExpr) and offset = 4
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,7 +423,8 @@ private class SingleLocalVarDeclParent extends ExprOrStmt {
|
||||
SingleLocalVarDeclParent() {
|
||||
this instanceof EnhancedForStmt or
|
||||
this instanceof CatchClause or
|
||||
this.(InstanceOfExpr).isPattern()
|
||||
this.(InstanceOfExpr).isPattern() or
|
||||
this instanceof PatternCase
|
||||
}
|
||||
|
||||
/** Gets the variable declaration that this element contains */
|
||||
|
||||
@@ -382,21 +382,43 @@ class SwitchStmt extends Stmt, @switchstmt {
|
||||
*/
|
||||
Stmt getStmt(int index) { result = this.getAStmt() and result.getIndex() = index }
|
||||
|
||||
/**
|
||||
* Gets the `i`th case of this `switch` statement,
|
||||
* which may be either a normal `case` or a `default`.
|
||||
*/
|
||||
SwitchCase getCase(int i) {
|
||||
result =
|
||||
rank[i + 1](SwitchCase case, int idx | case.isNthChildOf(this, idx) | case order by idx)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a case of this `switch` statement,
|
||||
* which may be either a normal `case` or a `default`.
|
||||
*/
|
||||
SwitchCase getACase() { result = this.getAConstCase() or result = this.getDefaultCase() }
|
||||
SwitchCase getACase() { result.getParent() = this }
|
||||
|
||||
/** Gets a (non-default) `case` of this `switch` statement. */
|
||||
ConstCase getAConstCase() { result.getParent() = this }
|
||||
/** Gets a (non-default) constant `case` of this `switch` statement. */
|
||||
ConstCase getAConstCase() { result = this.getACase() }
|
||||
|
||||
/** Gets the `default` case of this switch statement, if any. */
|
||||
DefaultCase getDefaultCase() { result.getParent() = this }
|
||||
/** Gets a (non-default) pattern `case` of this `switch` statement. */
|
||||
PatternCase getAPatternCase() { result = this.getACase() }
|
||||
|
||||
/**
|
||||
* Gets the `default` case of this switch statement, if any.
|
||||
*
|
||||
* Note this may be `default` or `case null, default`.
|
||||
*/
|
||||
DefaultCase getDefaultCase() { result = this.getACase() }
|
||||
|
||||
/** Gets the expression of this `switch` statement. */
|
||||
Expr getExpr() { result.getParent() = this }
|
||||
|
||||
/** Holds if this switch has a case handling a null literal. */
|
||||
predicate hasNullCase() {
|
||||
this.getAConstCase().getValue(_) instanceof NullLiteral or
|
||||
this.getACase() instanceof NullDefaultCase
|
||||
}
|
||||
|
||||
override string pp() { result = "switch (...)" }
|
||||
|
||||
override string toString() { result = "switch (...)" }
|
||||
@@ -406,6 +428,13 @@ class SwitchStmt extends Stmt, @switchstmt {
|
||||
override string getAPrimaryQlClass() { result = "SwitchStmt" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `switch` statement or expression.
|
||||
*/
|
||||
class SwitchBlock extends StmtParent {
|
||||
SwitchBlock() { this instanceof SwitchStmt or this instanceof SwitchExpr }
|
||||
}
|
||||
|
||||
/**
|
||||
* A case of a `switch` statement or expression.
|
||||
*
|
||||
@@ -428,6 +457,21 @@ class SwitchCase extends Stmt, @case {
|
||||
result = this.getSwitch().getExpr() or result = this.getSwitchExpr().getExpr()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets this case's ordinal in its switch block.
|
||||
*/
|
||||
int getCaseIndex() {
|
||||
this = any(SwitchStmt ss).getCase(result) or this = any(SwitchExpr se).getCase(result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this is the `n`th case of switch block `parent`.
|
||||
*/
|
||||
pragma[nomagic]
|
||||
predicate isNthCaseOf(SwitchBlock parent, int n) {
|
||||
this.getCaseIndex() = n and this.getParent() = parent
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this `case` is a switch labeled rule of the form `... -> ...`.
|
||||
*/
|
||||
@@ -462,17 +506,27 @@ class SwitchCase extends Stmt, @case {
|
||||
}
|
||||
}
|
||||
|
||||
/** A constant `case` of a switch statement. */
|
||||
/**
|
||||
* A constant `case` of a switch statement.
|
||||
*
|
||||
* Note this excludes `case null, default` even though that includes a null constant. It
|
||||
* does however include plain `case null`.
|
||||
*/
|
||||
class ConstCase extends SwitchCase {
|
||||
ConstCase() { exists(Expr e | e.getParent() = this | e.getIndex() >= 0) }
|
||||
ConstCase() {
|
||||
exists(Expr e | e.getParent() = this and e.getIndex() >= 0 and not e instanceof PatternExpr) and
|
||||
// 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.
|
||||
not isNullDefaultCase(this)
|
||||
}
|
||||
|
||||
/** Gets the `case` constant at index 0. */
|
||||
Expr getValue() { result.getParent() = this and result.getIndex() = 0 }
|
||||
Expr getValue() { result.isNthChildOf(this, 0) }
|
||||
|
||||
/**
|
||||
* Gets the `case` constant at the specified index.
|
||||
* Gets the `case` constant at index `i`.
|
||||
*/
|
||||
Expr getValue(int i) { result.getParent() = this and result.getIndex() = i and i >= 0 }
|
||||
Expr getValue(int i) { result.isNthChildOf(this, i) and i >= 0 }
|
||||
|
||||
override string pp() { result = "case ..." }
|
||||
|
||||
@@ -483,9 +537,36 @@ class ConstCase extends SwitchCase {
|
||||
override string getAPrimaryQlClass() { result = "ConstCase" }
|
||||
}
|
||||
|
||||
/** A `default` case of a `switch` statement */
|
||||
/** A pattern case of a `switch` statement */
|
||||
class PatternCase extends SwitchCase {
|
||||
PatternExpr pattern;
|
||||
|
||||
PatternCase() { pattern.isNthChildOf(this, 0) }
|
||||
|
||||
/** Gets this case's pattern. */
|
||||
PatternExpr getPattern() { result = pattern }
|
||||
|
||||
/** Gets the guard applicable to this pattern case, if any. */
|
||||
Expr getGuard() { result.isNthChildOf(this, -3) }
|
||||
|
||||
override string pp() { result = "case <Pattern>" }
|
||||
|
||||
override string toString() { result = "case <Pattern>" }
|
||||
|
||||
override string getHalsteadID() { result = "PatternCase" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "PatternCase" }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `default` or `case null, default` case of a `switch` statement or expression.
|
||||
*/
|
||||
class DefaultCase extends SwitchCase {
|
||||
DefaultCase() { not exists(Expr e | e.getParent() = this | e.getIndex() >= 0) }
|
||||
DefaultCase() {
|
||||
isNullDefaultCase(this)
|
||||
or
|
||||
not exists(Expr e | e.getParent() = this | e.getIndex() >= 0)
|
||||
}
|
||||
|
||||
override string pp() { result = "default" }
|
||||
|
||||
@@ -496,6 +577,19 @@ class DefaultCase extends SwitchCase {
|
||||
override string getAPrimaryQlClass() { result = "DefaultCase" }
|
||||
}
|
||||
|
||||
/** A `case null, default` statement of a `switch` statement or expression. */
|
||||
class NullDefaultCase extends DefaultCase {
|
||||
NullDefaultCase() { isNullDefaultCase(this) }
|
||||
|
||||
override string pp() { result = "case null, default" }
|
||||
|
||||
override string toString() { result = "case null, default" }
|
||||
|
||||
override string getHalsteadID() { result = "NullDefaultCase" }
|
||||
|
||||
override string getAPrimaryQlClass() { result = "NullDefaultCase" }
|
||||
}
|
||||
|
||||
/** A `synchronized` statement. */
|
||||
class SynchronizedStmt extends Stmt, @synchronizedstmt {
|
||||
/** Gets the expression on which this `synchronized` statement synchronizes. */
|
||||
|
||||
@@ -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. */
|
||||
|
||||
@@ -15,9 +15,12 @@ class Variable extends @variable, Annotatable, Element, Modifiable {
|
||||
/** Gets an access to this variable. */
|
||||
VarAccess getAnAccess() { variableBinding(result, this) }
|
||||
|
||||
/** Gets an expression on the right-hand side of an assignment to this variable. */
|
||||
/**
|
||||
* Gets an expression assigned to this variable, either appearing on the right-hand side of an
|
||||
* assignment or bound to it via a binding `instanceof` expression or `switch` block.
|
||||
*/
|
||||
Expr getAnAssignedValue() {
|
||||
exists(LocalVariableDeclExpr e | e.getVariable() = this and result = e.getInit())
|
||||
exists(LocalVariableDeclExpr e | e.getVariable() = this and result = e.getInitOrPatternSource())
|
||||
or
|
||||
exists(AssignExpr e | e.getDest() = this.getAnAccess() and result = e.getSource())
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import java
|
||||
private import semmle.code.java.controlflow.Dominance
|
||||
private import semmle.code.java.controlflow.internal.GuardsLogic
|
||||
private import semmle.code.java.controlflow.internal.Preconditions
|
||||
private import semmle.code.java.controlflow.internal.SwitchCases
|
||||
|
||||
/**
|
||||
* A basic block that terminates in a condition, splitting the subsequent control flow.
|
||||
@@ -18,7 +19,7 @@ class ConditionBlock extends BasicBlock {
|
||||
ConditionNode getConditionNode() { result = this.getLastNode() }
|
||||
|
||||
/** Gets the condition of the last node of this basic block. */
|
||||
Expr getCondition() { result = this.getConditionNode().getCondition() }
|
||||
ExprParent getCondition() { result = this.getConditionNode().getCondition() }
|
||||
|
||||
/** Gets a `true`- or `false`-successor of the last node of this basic block. */
|
||||
BasicBlock getTestSuccessor(boolean testIsTrue) {
|
||||
@@ -72,6 +73,54 @@ class ConditionBlock extends BasicBlock {
|
||||
}
|
||||
}
|
||||
|
||||
// Join order engineering -- first determine the switch block and the case indices required, then retrieve them.
|
||||
bindingset[switch, i]
|
||||
pragma[inline_late]
|
||||
private predicate isNthCaseOf(SwitchBlock switch, SwitchCase c, int i) { c.isNthCaseOf(switch, i) }
|
||||
|
||||
/**
|
||||
* Gets a switch case >= pred, up to but not including `pred`'s successor pattern case,
|
||||
* where `pred` is declared on `switch`.
|
||||
*/
|
||||
private SwitchCase getACaseUpToNextPattern(PatternCase pred, SwitchBlock switch) {
|
||||
// Note we do include `case null, default` (as well as plain old `default`) here.
|
||||
not result.(ConstCase).getValue(_) instanceof NullLiteral and
|
||||
exists(int maxCaseIndex |
|
||||
switch = pred.getParent() and
|
||||
if exists(getNextPatternCase(pred))
|
||||
then maxCaseIndex = getNextPatternCase(pred).getCaseIndex() - 1
|
||||
else maxCaseIndex = lastCaseIndex(switch)
|
||||
|
|
||||
isNthCaseOf(switch, result, [pred.getCaseIndex() .. maxCaseIndex])
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the closest pattern case preceding `case`, including `case` itself, if any.
|
||||
*/
|
||||
private PatternCase getClosestPrecedingPatternCase(SwitchCase case) {
|
||||
case = getACaseUpToNextPattern(result, _)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `pred` is a control-flow predecessor of switch case `sc` that is not a
|
||||
* fall-through from a previous case.
|
||||
*
|
||||
* For classic switches that means flow from the selector expression; for switches
|
||||
* involving pattern cases it can also mean flow from a previous pattern case's type
|
||||
* test or guard failing and proceeding to then consider subsequent cases.
|
||||
*/
|
||||
private predicate isNonFallThroughPredecessor(SwitchCase sc, ControlFlowNode pred) {
|
||||
pred = sc.getControlFlowNode().getAPredecessor() and
|
||||
(
|
||||
pred.(Expr).getParent*() = sc.getSelectorExpr()
|
||||
or
|
||||
pred.(Expr).getParent*() = getClosestPrecedingPatternCase(sc).getGuard()
|
||||
or
|
||||
pred = getClosestPrecedingPatternCase(sc)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* A condition that can be evaluated to either true or false. This can either
|
||||
* be an `Expr` of boolean type that isn't a boolean literal, or a case of a
|
||||
@@ -103,13 +152,21 @@ class Guard extends ExprParent {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the basic block containing this guard or the basic block containing
|
||||
* the switch expression if the guard is a switch case.
|
||||
* Gets the basic block containing this guard or the basic block that tests the
|
||||
* applicability of this switch case -- for a pattern case this is the case statement
|
||||
* itself; for a non-pattern case this is the most recent pattern case or the top of
|
||||
* the switch block if there is none.
|
||||
*/
|
||||
BasicBlock getBasicBlock() {
|
||||
result = this.(Expr).getBasicBlock() or
|
||||
result = this.(SwitchCase).getSwitch().getExpr().getBasicBlock() or
|
||||
result = this.(SwitchCase).getSwitchExpr().getExpr().getBasicBlock()
|
||||
// Not a switch case
|
||||
result = this.(Expr).getBasicBlock()
|
||||
or
|
||||
// Return the closest pattern case statement before this one, including this one.
|
||||
result = getClosestPrecedingPatternCase(this).getBasicBlock()
|
||||
or
|
||||
// Not a pattern case and no preceding pattern case -- return the top of the switch block.
|
||||
not exists(getClosestPrecedingPatternCase(this)) and
|
||||
result = this.(SwitchCase).getSelectorExpr().getBasicBlock()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -126,6 +183,39 @@ class Guard extends ExprParent {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this guard tests whether `testedExpr` has type `testedType`.
|
||||
*
|
||||
* `restricted` is true if the test applies additional restrictions on top of just `testedType`, and so
|
||||
* this guard failing does not guarantee `testedExpr` is *not* a `testedType`-- for example,
|
||||
* matching `record R(Object o)` with `case R(String s)` is a guard with an additional restriction on the
|
||||
* type of field `o`, so the guard passing guarantees `testedExpr` is an `R`, but it failing does not
|
||||
* guarantee `testedExpr` is not an `R`.
|
||||
*/
|
||||
predicate appliesTypeTest(Expr testedExpr, Type testedType, boolean restricted) {
|
||||
(
|
||||
exists(InstanceOfExpr ioe | this = ioe |
|
||||
testedExpr = ioe.getExpr() and
|
||||
testedType = ioe.getSyntacticCheckedType()
|
||||
)
|
||||
or
|
||||
exists(PatternCase pc | this = pc |
|
||||
pc.getSelectorExpr() = testedExpr and
|
||||
testedType = pc.getPattern().getType()
|
||||
)
|
||||
) and
|
||||
(
|
||||
if
|
||||
exists(RecordPatternExpr rpe |
|
||||
rpe = [this.(InstanceOfExpr).getPattern(), this.(PatternCase).getPattern()]
|
||||
|
|
||||
not rpe.isUnrestricted()
|
||||
)
|
||||
then restricted = true
|
||||
else restricted = false
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if the evaluation of this guard to `branch` corresponds to the edge
|
||||
* from `bb1` to `bb2`.
|
||||
@@ -139,10 +229,11 @@ class Guard extends ExprParent {
|
||||
or
|
||||
exists(SwitchCase sc, ControlFlowNode pred |
|
||||
sc = this and
|
||||
// Pattern cases are handled as ConditionBlocks above.
|
||||
not sc instanceof PatternCase and
|
||||
branch = true and
|
||||
bb2.getFirstNode() = sc.getControlFlowNode() and
|
||||
pred = sc.getControlFlowNode().getAPredecessor() and
|
||||
pred.(Expr).getParent*() = sc.getSelectorExpr() and
|
||||
isNonFallThroughPredecessor(sc, pred) and
|
||||
bb1 = pred.getBasicBlock()
|
||||
)
|
||||
or
|
||||
@@ -176,12 +267,14 @@ class Guard extends ExprParent {
|
||||
}
|
||||
|
||||
private predicate switchCaseControls(SwitchCase sc, BasicBlock bb) {
|
||||
exists(BasicBlock caseblock, Expr selector |
|
||||
selector = sc.getSelectorExpr() and
|
||||
exists(BasicBlock caseblock |
|
||||
// Pattern cases are handled as condition blocks
|
||||
not sc instanceof PatternCase and
|
||||
caseblock.getFirstNode() = sc.getControlFlowNode() and
|
||||
caseblock.bbDominates(bb) and
|
||||
// Check we can't fall through from a previous block:
|
||||
forall(ControlFlowNode pred | pred = sc.getControlFlowNode().getAPredecessor() |
|
||||
pred.(Expr).getParent*() = selector
|
||||
isNonFallThroughPredecessor(sc, pred)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -170,6 +170,7 @@ class ConstSwitchStmt extends SwitchStmt {
|
||||
/** Gets the matching case, if it can be deduced. */
|
||||
SwitchCase getMatchingCase() {
|
||||
// Must be a value we can deduce
|
||||
// TODO: handle other known constants (enum constants, String constants)
|
||||
exists(this.getExpr().(ConstantExpr).getIntValue()) and
|
||||
if exists(this.getMatchingConstCase())
|
||||
then result = this.getMatchingConstCase()
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
/** Provides utility predicates relating to switch cases. */
|
||||
|
||||
import java
|
||||
|
||||
/**
|
||||
* Gets the `i`th `PatternCase` defined on `switch`, if one exists.
|
||||
*/
|
||||
private PatternCase getPatternCase(SwitchBlock switch, int i) {
|
||||
result =
|
||||
rank[i + 1](PatternCase pc, int caseIdx | pc.isNthCaseOf(switch, caseIdx) | pc order by caseIdx)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the first `PatternCase` defined on `switch`, if one exists.
|
||||
*/
|
||||
PatternCase getFirstPatternCase(SwitchBlock switch) { result = getPatternCase(switch, 0) }
|
||||
|
||||
/**
|
||||
* Gets the PatternCase after pc, if one exists.
|
||||
*/
|
||||
PatternCase getNextPatternCase(PatternCase pc) {
|
||||
exists(int idx, SwitchBlock switch |
|
||||
pc = getPatternCase(switch, idx) and result = getPatternCase(switch, idx + 1)
|
||||
)
|
||||
}
|
||||
|
||||
int lastCaseIndex(SwitchBlock switch) {
|
||||
result = max(int i | any(SwitchCase c).isNthCaseOf(switch, i))
|
||||
}
|
||||
@@ -100,9 +100,9 @@ predicate dereference(Expr e) {
|
||||
or
|
||||
exists(SynchronizedStmt synch | synch.getExpr() = e)
|
||||
or
|
||||
exists(SwitchStmt switch | switch.getExpr() = e)
|
||||
exists(SwitchStmt switch | switch.getExpr() = e and not switch.hasNullCase())
|
||||
or
|
||||
exists(SwitchExpr switch | switch.getExpr() = e)
|
||||
exists(SwitchExpr switch | switch.getExpr() = e and not switch.hasNullCase())
|
||||
or
|
||||
exists(FieldAccess fa, Field f | fa.getQualifier() = e and fa.getField() = f and not f.isStatic())
|
||||
or
|
||||
@@ -460,7 +460,7 @@ private predicate interestingCond(SsaSourceVariable npecand, ConditionBlock cond
|
||||
varMaybeNullInBlock(_, npecand, cond, _) or
|
||||
varConditionallyNull(npecand.getAnSsaVariable(), cond, _)
|
||||
) and
|
||||
not cond.getCondition().getAChildExpr*() = npecand.getAnAccess()
|
||||
not cond.getCondition().(Expr).getAChildExpr*() = npecand.getAnAccess()
|
||||
}
|
||||
|
||||
/** A pair of correlated conditions for a given NPE candidate. */
|
||||
@@ -588,7 +588,7 @@ private predicate trackingVar(
|
||||
exists(ConditionBlock cond |
|
||||
interestingCond(npecand, cond) and
|
||||
varMaybeNullInBlock(_, npecand, cond, _) and
|
||||
cond.getCondition().getAChildExpr*() = trackvar.getAnAccess() and
|
||||
cond.getCondition().(Expr).getAChildExpr*() = trackvar.getAnAccess() and
|
||||
trackssa.getSourceVariable() = trackvar and
|
||||
trackssa.getDefiningExpr().(VariableAssign).getSource() = init
|
||||
|
|
||||
|
||||
@@ -414,29 +414,27 @@ private predicate downcastSuccessor(VarAccess va, RefType t) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `va` is an access to a value that is guarded by `instanceof t`.
|
||||
* Holds if `va` is an access to a value that is guarded by `instanceof t` or `case e t`.
|
||||
*/
|
||||
private predicate instanceOfGuarded(VarAccess va, RefType t) {
|
||||
exists(InstanceOfExpr ioe, BaseSsaVariable v |
|
||||
ioe.getExpr() = v.getAUse() and
|
||||
t = ioe.getCheckedType() and
|
||||
private predicate typeTestGuarded(VarAccess va, RefType t) {
|
||||
exists(Guard typeTest, BaseSsaVariable v |
|
||||
typeTest.appliesTypeTest(v.getAUse(), t, _) and
|
||||
va = v.getAUse() and
|
||||
guardControls_v1(ioe, va.getBasicBlock(), true)
|
||||
guardControls_v1(typeTest, va.getBasicBlock(), true)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `aa` is an access to a value that is guarded by `instanceof t`.
|
||||
* Holds if `aa` is an access to a value that is guarded by `instanceof t` or `case e t`.
|
||||
*/
|
||||
predicate arrayInstanceOfGuarded(ArrayAccess aa, RefType t) {
|
||||
exists(InstanceOfExpr ioe, BaseSsaVariable v1, BaseSsaVariable v2, ArrayAccess aa1 |
|
||||
ioe.getExpr() = aa1 and
|
||||
t = ioe.getCheckedType() and
|
||||
predicate arrayTypeTestGuarded(ArrayAccess aa, RefType t) {
|
||||
exists(Guard typeTest, BaseSsaVariable v1, BaseSsaVariable v2, ArrayAccess aa1 |
|
||||
typeTest.appliesTypeTest(aa1, t, _) and
|
||||
aa1.getArray() = v1.getAUse() and
|
||||
aa1.getIndexExpr() = v2.getAUse() and
|
||||
aa.getArray() = v1.getAUse() and
|
||||
aa.getIndexExpr() = v2.getAUse() and
|
||||
guardControls_v1(ioe, aa.getBasicBlock(), true)
|
||||
guardControls_v1(typeTest, aa.getBasicBlock(), true)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -462,8 +460,8 @@ private predicate typeFlowBaseCand(TypeFlowNode n, RefType t) {
|
||||
upcast(n, srctype) or
|
||||
upcastEnhancedForStmt(n.asSsa(), srctype) or
|
||||
downcastSuccessor(n.asExpr(), srctype) or
|
||||
instanceOfGuarded(n.asExpr(), srctype) or
|
||||
arrayInstanceOfGuarded(n.asExpr(), srctype) or
|
||||
typeTestGuarded(n.asExpr(), srctype) or
|
||||
arrayTypeTestGuarded(n.asExpr(), srctype) or
|
||||
n.asExpr().(FunctionalExpr).getConstructedType() = srctype or
|
||||
superAccess(n.asExpr(), srctype)
|
||||
|
|
||||
@@ -629,7 +627,7 @@ private predicate instanceofDisjunctionGuarded(TypeFlowNode n, RefType t) {
|
||||
bb.bbDominates(va.getBasicBlock()) and
|
||||
va = v.getAUse() and
|
||||
instanceofDisjunct(ioe, bb, v) and
|
||||
t = ioe.getCheckedType() and
|
||||
t = ioe.getSyntacticCheckedType() and
|
||||
n.asExpr() = va
|
||||
)
|
||||
}
|
||||
|
||||
@@ -232,6 +232,27 @@ predicate storeStep(Node node1, ContentSet f, Node node2) {
|
||||
pragma[only_bind_out](node2.getEnclosingCallable())
|
||||
}
|
||||
|
||||
// Manual join hacking, to avoid a parameters x fields product.
|
||||
pragma[noinline]
|
||||
private predicate hasNamedField(Record r, Field f, string name) {
|
||||
f = r.getAField() and name = f.getName()
|
||||
}
|
||||
|
||||
pragma[noinline]
|
||||
private predicate hasNamedCanonicalParameter(Record r, Parameter p, int idx, string name) {
|
||||
p = r.getCanonicalConstructor().getParameter(idx) and name = p.getName()
|
||||
}
|
||||
|
||||
private Field getLexicallyOrderedRecordField(Record r, int idx) {
|
||||
result =
|
||||
rank[idx + 1](Field f, int i, Parameter p, string name |
|
||||
hasNamedCanonicalParameter(r, p, i, name) and
|
||||
hasNamedField(r, f, name)
|
||||
|
|
||||
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 +277,13 @@ predicate readStep(Node node1, ContentSet f, Node node2) {
|
||||
node2.asExpr() = get
|
||||
)
|
||||
or
|
||||
exists(RecordPatternExpr rpe, PatternExpr 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)
|
||||
@@ -347,7 +375,18 @@ predicate compatibleTypes(DataFlowType t1, DataFlowType t2) { compatibleTypes0(t
|
||||
|
||||
/** A node that performs a type cast. */
|
||||
class CastNode extends ExprNode {
|
||||
CastNode() { this.getExpr() instanceof CastingExpr }
|
||||
CastNode() {
|
||||
this.getExpr() instanceof CastingExpr
|
||||
or
|
||||
exists(SsaExplicitUpdate upd |
|
||||
upd.getDefiningExpr().(VariableAssign).getSource() =
|
||||
[
|
||||
any(SwitchStmt ss).getExpr(), any(SwitchExpr se).getExpr(),
|
||||
any(InstanceOfExpr ioe).getExpr()
|
||||
] and
|
||||
this.asExpr() = upd.getAFirstUse()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private newtype TDataFlowCallable =
|
||||
|
||||
@@ -190,6 +190,19 @@ predicate simpleAstFlowStep(Expr e1, Expr e2) {
|
||||
e2 = any(NotNullExpr nne | e1 = nne.getExpr())
|
||||
or
|
||||
e2.(WhenExpr).getBranch(_).getAResult() = e1
|
||||
or
|
||||
// In the following three cases only record patterns need this flow edge, leading from the bound instanceof
|
||||
// or switch tested expression to a record pattern that will read its fields. Simple binding patterns are
|
||||
// handled via VariableAssign.getSource instead.
|
||||
exists(SwitchExpr se |
|
||||
e1 = se.getExpr() and e2 = se.getACase().(PatternCase).getPattern().asRecordPattern()
|
||||
)
|
||||
or
|
||||
exists(SwitchStmt ss |
|
||||
e1 = ss.getExpr() and e2 = ss.getACase().(PatternCase).getPattern().asRecordPattern()
|
||||
)
|
||||
or
|
||||
exists(InstanceOfExpr ioe | e1 = ioe.getExpr() and e2 = ioe.getPattern().asRecordPattern())
|
||||
}
|
||||
|
||||
private predicate simpleLocalFlowStep0(Node node1, Node node2) {
|
||||
@@ -197,7 +210,8 @@ private predicate simpleLocalFlowStep0(Node node1, Node node2) {
|
||||
// Variable flow steps through adjacent def-use and use-use pairs.
|
||||
exists(SsaExplicitUpdate upd |
|
||||
upd.getDefiningExpr().(VariableAssign).getSource() = node1.asExpr() or
|
||||
upd.getDefiningExpr().(AssignOp) = node1.asExpr()
|
||||
upd.getDefiningExpr().(AssignOp) = node1.asExpr() or
|
||||
upd.getDefiningExpr().(RecordBindingVariableExpr) = node1.asExpr()
|
||||
|
|
||||
node2.asExpr() = upd.getAFirstUse() and
|
||||
not capturedVariableRead(node2)
|
||||
|
||||
@@ -194,13 +194,12 @@ private module Dispatch {
|
||||
*/
|
||||
private predicate impossibleDispatchTarget(MethodCall source, Method tgt) {
|
||||
tgt = viableImpl_v1_cand(source) and
|
||||
exists(InstanceOfExpr ioe, BaseSsaVariable v, Expr q, RefType t |
|
||||
exists(Guard typeTest, BaseSsaVariable v, Expr q, RefType t |
|
||||
source.getQualifier() = q and
|
||||
v.getAUse() = q and
|
||||
guardControls_v1(ioe, q.getBasicBlock(), false) and
|
||||
ioe.getExpr() = v.getAUse() and
|
||||
ioe.getCheckedType().getErasure() = t and
|
||||
tgt.getDeclaringType().getSourceDeclaration().getASourceSupertype*() = t
|
||||
typeTest.appliesTypeTest(v.getAUse(), t, false) and
|
||||
guardControls_v1(typeTest, q.getBasicBlock(), false) and
|
||||
tgt.getDeclaringType().getSourceDeclaration().getASourceSupertype*() = t.getErasure()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -73,13 +73,13 @@ class MetricCallable extends Callable {
|
||||
// so there should be a branching point for each non-default switch
|
||||
// case (ignoring those that just fall through to the next case).
|
||||
private predicate branchingSwitchCase(ConstCase sc) {
|
||||
not sc.(ControlFlowNode).getASuccessor() instanceof ConstCase and
|
||||
not sc.(ControlFlowNode).getASuccessor() instanceof DefaultCase and
|
||||
not sc.(ControlFlowNode).getASuccessor() instanceof SwitchCase and
|
||||
not defaultFallThrough(sc)
|
||||
}
|
||||
|
||||
private predicate defaultFallThrough(ConstCase sc) {
|
||||
exists(DefaultCase default | default.(ControlFlowNode).getASuccessor() = sc) or
|
||||
exists(DefaultCase default | default.(ControlFlowNode).getASuccessor() = sc)
|
||||
or
|
||||
defaultFallThrough(sc.(ControlFlowNode).getAPredecessor())
|
||||
}
|
||||
|
||||
@@ -90,6 +90,7 @@ private predicate branchingStmt(Stmt stmt) {
|
||||
stmt instanceof DoStmt or
|
||||
stmt instanceof ForStmt or
|
||||
stmt instanceof EnhancedForStmt or
|
||||
stmt instanceof PatternCase or
|
||||
branchingSwitchCase(stmt) or
|
||||
stmt instanceof CatchClause
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
class Expr extends @expr {
|
||||
string toString() { result = "expr" }
|
||||
}
|
||||
|
||||
class LocalVariableDeclExpr extends @localvariabledeclexpr, Expr { }
|
||||
|
||||
class InstanceOfExpr extends @instanceofexpr, Expr { }
|
||||
|
||||
class Type extends @type {
|
||||
string toString() { result = "type" }
|
||||
}
|
||||
|
||||
class ExprParent extends @exprparent {
|
||||
string toString() { result = "exprparent" }
|
||||
}
|
||||
|
||||
// Initialisers of local variable declarations that occur as the 0th child of an instanceof expression should be reparented to be the 0th child of the instanceof itself,
|
||||
// while the LocalVariableDeclExpr, now without an initialiser, should become its 2nd child.
|
||||
// This implements a reorganisation of the representation of "o instanceof String s", which used to be InstanceOfExpr -0-> LocalVariableDeclExpr --init-> o
|
||||
// \-name-> s
|
||||
// It is now InstanceOfExpr --0-> o
|
||||
// \-2-> LocalVariableDeclExpr -name-> s
|
||||
//
|
||||
// Other children are unaffected.
|
||||
ExprParent getParent(Expr e) { exprs(e, _, _, result, _) }
|
||||
|
||||
predicate hasNewParent(Expr e, ExprParent newParent, int newIndex) {
|
||||
if
|
||||
getParent(e) instanceof LocalVariableDeclExpr and
|
||||
getParent(getParent(e)) instanceof InstanceOfExpr
|
||||
then (
|
||||
// Initialiser moves to hang directly off the instanceof expression
|
||||
newParent = getParent(getParent(e)) and newIndex = 0
|
||||
) else (
|
||||
if e instanceof LocalVariableDeclExpr and getParent(e) instanceof InstanceOfExpr
|
||||
then
|
||||
// Variable declaration moves to be the instanceof expression's 2nd child
|
||||
newParent = getParent(e) and newIndex = 2
|
||||
else exprs(e, _, _, newParent, newIndex) // Other expressions unchanged
|
||||
)
|
||||
}
|
||||
|
||||
from Expr e, int kind, Type typeid, ExprParent parent, int index
|
||||
where
|
||||
exprs(e, kind, typeid, _, _) and
|
||||
hasNewParent(e, parent, index)
|
||||
select e, kind, typeid, parent, index
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,3 @@
|
||||
description: Add tables for canonical constructors and `case null, default`, plus an expression kind for record patterns. Also alter the representation for instanceof with a binding pattern.
|
||||
compatibility: backwards
|
||||
exprs.rel: run exprs.qlo
|
||||
@@ -17,7 +17,7 @@ from InstanceOfExpr ioe, RefType t, RefType ct
|
||||
where
|
||||
ioe.getExpr() instanceof ThisAccess and
|
||||
t = ioe.getExpr().getType() and
|
||||
ct = ioe.getCheckedType() and
|
||||
ct = ioe.getSyntacticCheckedType() and
|
||||
ct.getAnAncestor() = t
|
||||
select ioe,
|
||||
"Testing whether 'this' is an instance of $@ in $@ introduces a dependency cycle between the two types.",
|
||||
|
||||
@@ -40,6 +40,6 @@ where
|
||||
) and
|
||||
// Also, any value that `v` is initialized to is a fresh container,
|
||||
forall(Expr e | e = v.getAnAssignedValue() | e instanceof FreshContainer) and
|
||||
// and `v` is not implicitly initialized by a for-each loop.
|
||||
not exists(EnhancedForStmt efs | efs.getVariable().getVariable() = v)
|
||||
// and `v` is not implicitly initialized.
|
||||
not v.(LocalVariableDecl).getDeclExpr().hasImplicitInit()
|
||||
select v, "The contents of this container are never initialized."
|
||||
|
||||
@@ -39,6 +39,6 @@ where
|
||||
) and
|
||||
// Also, any value that `v` is initialized to is a new container,
|
||||
forall(Expr e | e = v.getAnAssignedValue() | e instanceof ClassInstanceExpr) and
|
||||
// and `v` is not implicitly initialized by a for-each loop.
|
||||
not exists(EnhancedForStmt efs | efs.getVariable().getVariable() = v)
|
||||
// and `v` is not implicitly initialized
|
||||
not v.(LocalVariableDecl).getDeclExpr().hasImplicitInit()
|
||||
select v, "The contents of this container are never accessed."
|
||||
|
||||
@@ -18,7 +18,7 @@ predicate instanceofInEquals(EqualsMethod m, InstanceOfExpr e) {
|
||||
e.getEnclosingCallable() = m and
|
||||
e.getExpr().(VarAccess).getVariable() = m.getParameter() and
|
||||
exists(RefType instanceofType |
|
||||
instanceofType = e.getCheckedType() and
|
||||
instanceofType = e.getSyntacticCheckedType() and
|
||||
not instanceofType.isFinal()
|
||||
)
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ from LocalBoxedVar v
|
||||
where
|
||||
forall(Expr e | e = v.getAnAssignedValue() | e.getType() = v.getPrimitiveType()) and
|
||||
(
|
||||
not v.getDeclExpr().getParent() instanceof EnhancedForStmt or
|
||||
not v.getDeclExpr().hasImplicitInit() or
|
||||
v.getDeclExpr().getParent().(EnhancedForStmt).getExpr().getType().(Array).getComponentType() =
|
||||
v.getPrimitiveType()
|
||||
) and
|
||||
|
||||
@@ -11,22 +11,13 @@
|
||||
import java
|
||||
import DeadLocals
|
||||
|
||||
predicate exceptionVariable(LocalVariableDeclExpr ve) {
|
||||
exists(CatchClause catch | catch.getVariable() = ve)
|
||||
}
|
||||
|
||||
predicate enhancedForVariable(LocalVariableDeclExpr ve) {
|
||||
exists(EnhancedForStmt for | for.getVariable() = ve)
|
||||
}
|
||||
|
||||
from LocalVariableDeclExpr ve, LocalVariableDecl v
|
||||
where
|
||||
v = ve.getVariable() and
|
||||
not assigned(v) and
|
||||
not read(v) and
|
||||
(not exists(ve.getInit()) or exprHasNoEffect(ve.getInit())) and
|
||||
// Remove contexts where Java forces a variable declaration: enhanced-for and catch clauses.
|
||||
// Remove contexts where Java forces a variable declaration: enhanced-for, catch clauses and pattern cases.
|
||||
// Rules about catch clauses belong in an exception handling query
|
||||
not exceptionVariable(ve) and
|
||||
not enhancedForVariable(ve)
|
||||
not ve.hasImplicitInit()
|
||||
select v, "Variable " + v.getName() + " is not used."
|
||||
|
||||
@@ -17,8 +17,7 @@ import Common
|
||||
from SwitchStmt s, Stmt c
|
||||
where
|
||||
c = s.getACase() and
|
||||
not c.(ControlFlowNode).getASuccessor() instanceof ConstCase and
|
||||
not c.(ControlFlowNode).getASuccessor() instanceof DefaultCase and
|
||||
not c.(ControlFlowNode).getASuccessor() instanceof SwitchCase and
|
||||
not s.(Annotatable).suppressesWarningsAbout("fallthrough") and
|
||||
mayDropThroughWithoutComment(s, c)
|
||||
select c,
|
||||
|
||||
@@ -14,8 +14,17 @@
|
||||
| dependency/A.java:15:8:15:8 | E | java.lang.Object |
|
||||
| dependency/A.java:22:7:22:7 | F | java.lang.Object |
|
||||
| dependency/A.java:24:7:24:7 | G | java.lang.Throwable |
|
||||
| dependency/A.java:26:7:26:7 | H | dependency.H$Used1 |
|
||||
| dependency/A.java:26:7:26:7 | H | dependency.H$Used2 |
|
||||
| dependency/A.java:26:7:26:7 | H | dependency.H$Used3 |
|
||||
| dependency/A.java:26:7:26:7 | H | java.lang.Number |
|
||||
| dependency/A.java:26:7:26:7 | H | java.lang.Object |
|
||||
| dependency/A.java:26:7:26:7 | H | java.lang.String |
|
||||
| dependency/A.java:26:7:26:7 | H | java.util.Collection |
|
||||
| dependency/A.java:27:3:27:18 | T | java.lang.String |
|
||||
| dependency/A.java:41:22:41:26 | Used1 | dependency.H |
|
||||
| dependency/A.java:41:22:41:26 | Used1 | java.lang.Object |
|
||||
| dependency/A.java:42:22:42:26 | Used2 | dependency.H |
|
||||
| dependency/A.java:42:22:42:26 | Used2 | java.lang.Object |
|
||||
| dependency/A.java:43:22:43:26 | Used3 | dependency.H |
|
||||
| dependency/A.java:43:22:43:26 | Used3 | java.lang.Object |
|
||||
|
||||
@@ -49,3 +49,39 @@ dependency/A.java:
|
||||
# 28| 0: [WildcardTypeAccess] ? ...
|
||||
# 28| 0: [TypeAccess] Number
|
||||
# 28| 5: [BlockStmt] { ... }
|
||||
# 29| 4: [Method] test3
|
||||
# 29| 3: [TypeAccess] void
|
||||
#-----| 4: (Parameters)
|
||||
# 29| 0: [Parameter] o
|
||||
# 29| 0: [TypeAccess] Object
|
||||
# 29| 5: [BlockStmt] { ... }
|
||||
# 30| 0: [IfStmt] if (...)
|
||||
# 30| 0: [InstanceOfExpr] ...instanceof...
|
||||
# 30| 0: [VarAccess] o
|
||||
# 30| 1: [TypeAccess] Used1
|
||||
# 30| 1: [ReturnStmt] return ...
|
||||
# 31| 1: [SwitchStmt] switch (...)
|
||||
# 31| -1: [VarAccess] o
|
||||
# 32| 0: [PatternCase] case <Pattern>
|
||||
#-----| 0: (Single Local Variable Declaration)
|
||||
# 32| 0: [TypeAccess] Used2
|
||||
# 32| 1: [LocalVariableDeclExpr] u2
|
||||
# 32| 1: [BreakStmt] break
|
||||
# 33| 2: [DefaultCase] default
|
||||
# 33| 3: [BreakStmt] break
|
||||
# 35| 2: [LocalVariableDeclStmt] var ...;
|
||||
# 35| 1: [LocalVariableDeclExpr] x
|
||||
# 35| 0: [SwitchExpr] switch (...)
|
||||
# 35| -1: [VarAccess] o
|
||||
# 36| 0: [PatternCase] case <Pattern>
|
||||
#-----| 0: (Single Local Variable Declaration)
|
||||
# 36| 0: [TypeAccess] Used3
|
||||
# 36| 1: [LocalVariableDeclExpr] u3
|
||||
# 36| 1: [YieldStmt] yield ...
|
||||
# 36| 0: [IntegerLiteral] 1
|
||||
# 37| 2: [DefaultCase] default
|
||||
# 37| 3: [YieldStmt] yield ...
|
||||
# 37| 0: [IntegerLiteral] 2
|
||||
# 41| 5: [Class] Used1
|
||||
# 42| 6: [Class] Used2
|
||||
# 43| 7: [Class] Used3
|
||||
|
||||
@@ -10,4 +10,7 @@
|
||||
| dependency/A.java:22:7:22:7 | F | dependency/A.java:22:7:22:7 | F |
|
||||
| dependency/A.java:24:7:24:7 | G | dependency/A.java:24:7:24:7 | G |
|
||||
| dependency/A.java:26:7:26:7 | H | dependency/A.java:26:7:26:7 | H |
|
||||
| dependency/A.java:41:22:41:26 | Used1 | dependency/A.java:41:22:41:26 | Used1 |
|
||||
| dependency/A.java:42:22:42:26 | Used2 | dependency/A.java:42:22:42:26 | Used2 |
|
||||
| dependency/A.java:43:22:43:26 | Used3 | dependency/A.java:43:22:43:26 | Used3 |
|
||||
| file://:0:0:0:0 | C[] | dependency/A.java:9:7:9:7 | C |
|
||||
|
||||
@@ -26,4 +26,19 @@ class G extends Throwable { }
|
||||
class H {
|
||||
<T extends String> T test(T t) { return t; }
|
||||
void test2(java.util.Collection<? extends Number> t) {}
|
||||
void test3(Object o) {
|
||||
if (o instanceof Used1) return;
|
||||
switch (o) {
|
||||
case Used2 u2: break;
|
||||
default: break;
|
||||
}
|
||||
var x = switch (o) {
|
||||
case Used3 u3: yield 1;
|
||||
default: yield 2;
|
||||
};
|
||||
}
|
||||
|
||||
static class Used1 { }
|
||||
static class Used2 { }
|
||||
static class Used3 { }
|
||||
}
|
||||
|
||||
1
java/ql/test/library-tests/dependency/options
Normal file
1
java/ql/test/library-tests/dependency/options
Normal file
@@ -0,0 +1 @@
|
||||
//semmle-extractor-options: --javac-args --release 21
|
||||
75
java/ql/test/library-tests/flow-through-binding/Test.java
Normal file
75
java/ql/test/library-tests/flow-through-binding/Test.java
Normal file
@@ -0,0 +1,75 @@
|
||||
public class Test {
|
||||
|
||||
public static Object testFlowThroughSwitchStmt(String s, Integer i, boolean unknown) {
|
||||
Object o = unknown ? s : i;
|
||||
switch (o) {
|
||||
case Integer i2 -> { return (Object)i2; }
|
||||
default -> { return null; }
|
||||
}
|
||||
}
|
||||
|
||||
public static Object testFlowThroughSwitchExpr(String s, Integer i, boolean unknown) {
|
||||
Object o = unknown ? s : i;
|
||||
Object toRet = switch (o) {
|
||||
case Integer i2 -> (Object)i2;
|
||||
default -> null;
|
||||
};
|
||||
return toRet;
|
||||
}
|
||||
|
||||
public static Object testFlowThroughBindingInstanceOf(String s, Integer i, boolean unknown) {
|
||||
Object o = unknown ? s : i;
|
||||
if (o instanceof Integer i2)
|
||||
return (Object)i2;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Object testFlowThroughSwitchStmtWrapper(Wrapper s, Wrapper i, boolean unknown) {
|
||||
Wrapper o = unknown ? s : i;
|
||||
switch (o) {
|
||||
case Wrapper(Integer i2) -> { return (Object)i2; }
|
||||
default -> { return null; }
|
||||
}
|
||||
}
|
||||
|
||||
public static Object testFlowThroughSwitchExprWrapper(Wrapper s, Wrapper i, boolean unknown) {
|
||||
Wrapper o = unknown ? s : i;
|
||||
Object toRet = switch (o) {
|
||||
case Wrapper(Integer i2) -> (Object)i2;
|
||||
default -> null;
|
||||
};
|
||||
return toRet;
|
||||
}
|
||||
|
||||
public static Object testFlowThroughBindingInstanceOfWrapper(Wrapper s, Wrapper i, boolean unknown) {
|
||||
Wrapper o = unknown ? s : i;
|
||||
if (o instanceof Wrapper(Integer i2))
|
||||
return (Object)i2;
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T> T source() { return null; }
|
||||
|
||||
public static void sink(Object o) { }
|
||||
|
||||
public static void test(boolean unknown, boolean unknown2) {
|
||||
|
||||
String source1 = source();
|
||||
Integer source2 = source();
|
||||
sink(testFlowThroughSwitchStmt(source1, source2, unknown));
|
||||
sink(testFlowThroughSwitchExpr(source1, source2, unknown));
|
||||
sink(testFlowThroughBindingInstanceOf(source1, source2, unknown));
|
||||
|
||||
Wrapper source1Wrapper = new Wrapper((String)source());
|
||||
Wrapper source2Wrapper = new Wrapper((Integer)source());
|
||||
sink(testFlowThroughSwitchStmtWrapper(source1Wrapper, source2Wrapper, unknown));
|
||||
sink(testFlowThroughSwitchExprWrapper(source1Wrapper, source2Wrapper, unknown));
|
||||
sink(testFlowThroughBindingInstanceOfWrapper(source1Wrapper, source2Wrapper, unknown));
|
||||
|
||||
}
|
||||
|
||||
record Wrapper(Object o) { }
|
||||
|
||||
}
|
||||
1
java/ql/test/library-tests/flow-through-binding/options
Normal file
1
java/ql/test/library-tests/flow-through-binding/options
Normal file
@@ -0,0 +1 @@
|
||||
//semmle-extractor-options: --javac-args --release 21
|
||||
@@ -0,0 +1,6 @@
|
||||
| Test.java:60:23:60:30 | source(...) | Test.java:61:10:61:61 | testFlowThroughSwitchStmt(...) |
|
||||
| Test.java:60:23:60:30 | source(...) | Test.java:62:10:62:61 | testFlowThroughSwitchExpr(...) |
|
||||
| Test.java:60:23:60:30 | source(...) | Test.java:63:10:63:68 | testFlowThroughBindingInstanceOf(...) |
|
||||
| Test.java:66:51:66:58 | source(...) | Test.java:67:10:67:82 | testFlowThroughSwitchStmtWrapper(...) |
|
||||
| Test.java:66:51:66:58 | source(...) | Test.java:68:10:68:82 | testFlowThroughSwitchExprWrapper(...) |
|
||||
| Test.java:66:51:66:58 | source(...) | Test.java:69:10:69:89 | testFlowThroughBindingInstanceOfWrapper(...) |
|
||||
18
java/ql/test/library-tests/flow-through-binding/test.ql
Normal file
18
java/ql/test/library-tests/flow-through-binding/test.ql
Normal file
@@ -0,0 +1,18 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
|
||||
module TestConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr() = any(MethodCall mc | mc.getCallee().getName() = "source")
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(MethodCall mc | mc.getMethod().getName() = "sink").getAnArgument()
|
||||
}
|
||||
}
|
||||
|
||||
module Flow = DataFlow::Global<TestConfig>;
|
||||
|
||||
from DataFlow::Node source, DataFlow::Node sink
|
||||
where Flow::flow(source, sink)
|
||||
select source, sink
|
||||
@@ -6,6 +6,8 @@ Test.java:
|
||||
#-----| 4: (Parameters)
|
||||
# 2| 0: [Parameter] s
|
||||
# 2| 0: [TypeAccess] String
|
||||
# 2| 1: [Parameter] unknown
|
||||
# 2| 0: [TypeAccess] boolean
|
||||
# 2| 5: [BlockStmt] { ... }
|
||||
# 3| 0: [LocalVariableDeclStmt] var ...;
|
||||
# 3| 0: [TypeAccess] int
|
||||
@@ -38,3 +40,45 @@ Test.java:
|
||||
# 12| 0: [StringLiteral] "d"
|
||||
# 13| 3: [DefaultCase] default
|
||||
# 13| -1: [BlockStmt] { ... }
|
||||
# 15| 2: [LocalVariableDeclStmt] var ...;
|
||||
# 15| 0: [TypeAccess] int
|
||||
# 15| 1: [LocalVariableDeclExpr] len
|
||||
# 15| 0: [MethodCall] length(...)
|
||||
# 15| -1: [VarAccess] s
|
||||
# 16| 3: [SwitchStmt] switch (...)
|
||||
# 16| -1: [VarAccess] s
|
||||
# 17| 0: [PatternCase] case <Pattern>
|
||||
# 17| -3: [EQExpr] ... == ...
|
||||
# 17| 0: [VarAccess] len
|
||||
# 17| 1: [IntegerLiteral] 4
|
||||
# 17| -1: [BlockStmt] { ... }
|
||||
#-----| 0: (Single Local Variable Declaration)
|
||||
# 17| 0: [TypeAccess] String
|
||||
# 17| 1: [LocalVariableDeclExpr] s2
|
||||
# 18| 1: [ConstCase] case ...
|
||||
# 18| -1: [BlockStmt] { ... }
|
||||
# 18| 0: [StringLiteral] "e"
|
||||
# 19| 2: [DefaultCase] default
|
||||
# 19| -1: [BlockStmt] { ... }
|
||||
# 21| 4: [SwitchStmt] switch (...)
|
||||
# 21| -1: [ConditionalExpr] ...?...:...
|
||||
# 21| 0: [VarAccess] unknown
|
||||
# 21| 1: [VarAccess] s
|
||||
# 21| 2: [MethodCall] toLowerCase(...)
|
||||
# 21| -1: [VarAccess] s
|
||||
# 22| 0: [ConstCase] case ...
|
||||
# 22| -1: [BlockStmt] { ... }
|
||||
# 22| 0: [StringLiteral] "f"
|
||||
# 23| 1: [PatternCase] case <Pattern>
|
||||
# 23| -3: [EQExpr] ... == ...
|
||||
# 23| 0: [VarAccess] len
|
||||
# 23| 1: [IntegerLiteral] 4
|
||||
# 23| -1: [BlockStmt] { ... }
|
||||
#-----| 0: (Single Local Variable Declaration)
|
||||
# 23| 0: [TypeAccess] String
|
||||
# 23| 1: [LocalVariableDeclExpr] s2
|
||||
# 24| 2: [ConstCase] case ...
|
||||
# 24| -1: [BlockStmt] { ... }
|
||||
# 24| 0: [StringLiteral] "g"
|
||||
# 25| 3: [DefaultCase] default
|
||||
# 25| -1: [BlockStmt] { ... }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
class Test {
|
||||
void foo(String s) {
|
||||
void foo(String s, boolean unknown) {
|
||||
int x = switch(s) {
|
||||
case "a", "b" -> 1;
|
||||
case "c" -> 2;
|
||||
@@ -12,5 +12,17 @@ class Test {
|
||||
case "d" -> { }
|
||||
default -> { }
|
||||
}
|
||||
int len = s.length();
|
||||
switch (s) {
|
||||
case String s2 when len == 4 -> { }
|
||||
case "e" -> { }
|
||||
default -> { }
|
||||
}
|
||||
switch (unknown ? s : s.toLowerCase()) {
|
||||
case "f" -> { }
|
||||
case String s2 when len == 4 -> { }
|
||||
case "g" -> { }
|
||||
default -> { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,37 @@
|
||||
hasBranchEdge
|
||||
| Test.java:4:7:4:22 | case ... | Test.java:2:39:27:3 | { ... } | Test.java:4:7:4:22 | case ... | true |
|
||||
| Test.java:5:7:5:17 | case ... | Test.java:2:39:27:3 | { ... } | Test.java:5:7:5:17 | case ... | true |
|
||||
| Test.java:6:7:6:17 | case ... | Test.java:2:39:27:3 | { ... } | Test.java:6:7:6:17 | case ... | true |
|
||||
| Test.java:7:7:7:16 | default | Test.java:2:39:27:3 | { ... } | Test.java:7:7:7:16 | default | true |
|
||||
| Test.java:10:7:10:22 | case ... | Test.java:3:9:3:21 | x | Test.java:10:7:10:22 | case ... | true |
|
||||
| Test.java:11:7:11:17 | case ... | Test.java:3:9:3:21 | x | Test.java:11:7:11:17 | case ... | true |
|
||||
| Test.java:12:7:12:17 | case ... | Test.java:3:9:3:21 | x | Test.java:12:7:12:17 | case ... | true |
|
||||
| Test.java:13:7:13:16 | default | Test.java:3:9:3:21 | x | Test.java:13:7:13:16 | default | true |
|
||||
| Test.java:17:7:17:37 | case <Pattern> | Test.java:15:5:15:25 | var ...; | Test.java:17:19:17:20 | s2 | true |
|
||||
| Test.java:17:7:17:37 | case <Pattern> | Test.java:15:5:15:25 | var ...; | Test.java:18:7:18:17 | case ... | false |
|
||||
| Test.java:17:7:17:37 | case <Pattern> | Test.java:15:5:15:25 | var ...; | Test.java:19:7:19:16 | default | false |
|
||||
| Test.java:17:27:17:34 | ... == ... | Test.java:17:19:17:20 | s2 | Test.java:17:39:17:41 | { ... } | true |
|
||||
| Test.java:17:27:17:34 | ... == ... | Test.java:17:19:17:20 | s2 | Test.java:18:7:18:17 | case ... | false |
|
||||
| Test.java:17:27:17:34 | ... == ... | Test.java:17:19:17:20 | s2 | Test.java:19:7:19:16 | default | false |
|
||||
| Test.java:18:7:18:17 | case ... | Test.java:15:5:15:25 | var ...; | Test.java:18:7:18:17 | case ... | true |
|
||||
| Test.java:18:7:18:17 | case ... | Test.java:17:19:17:20 | s2 | Test.java:18:7:18:17 | case ... | true |
|
||||
| Test.java:19:7:19:16 | default | Test.java:15:5:15:25 | var ...; | Test.java:19:7:19:16 | default | true |
|
||||
| Test.java:19:7:19:16 | default | Test.java:17:19:17:20 | s2 | Test.java:19:7:19:16 | default | true |
|
||||
| Test.java:21:13:21:19 | unknown | Test.java:21:5:21:42 | switch (...) | Test.java:21:23:21:23 | s | true |
|
||||
| Test.java:21:13:21:19 | unknown | Test.java:21:5:21:42 | switch (...) | Test.java:21:27:21:27 | s | false |
|
||||
| Test.java:22:7:22:17 | case ... | Test.java:21:23:21:23 | s | Test.java:22:7:22:17 | case ... | true |
|
||||
| Test.java:22:7:22:17 | case ... | Test.java:21:27:21:27 | s | Test.java:22:7:22:17 | case ... | true |
|
||||
| Test.java:23:7:23:37 | case <Pattern> | Test.java:23:7:23:37 | case <Pattern> | Test.java:23:19:23:20 | s2 | true |
|
||||
| Test.java:23:7:23:37 | case <Pattern> | Test.java:23:7:23:37 | case <Pattern> | Test.java:24:7:24:17 | case ... | false |
|
||||
| Test.java:23:7:23:37 | case <Pattern> | Test.java:23:7:23:37 | case <Pattern> | Test.java:25:7:25:16 | default | false |
|
||||
| Test.java:23:27:23:34 | ... == ... | Test.java:23:19:23:20 | s2 | Test.java:23:39:23:41 | { ... } | true |
|
||||
| Test.java:23:27:23:34 | ... == ... | Test.java:23:19:23:20 | s2 | Test.java:24:7:24:17 | case ... | false |
|
||||
| Test.java:23:27:23:34 | ... == ... | Test.java:23:19:23:20 | s2 | Test.java:25:7:25:16 | default | false |
|
||||
| Test.java:24:7:24:17 | case ... | Test.java:23:7:23:37 | case <Pattern> | Test.java:24:7:24:17 | case ... | true |
|
||||
| Test.java:24:7:24:17 | case ... | Test.java:23:19:23:20 | s2 | Test.java:24:7:24:17 | case ... | true |
|
||||
| Test.java:25:7:25:16 | default | Test.java:23:7:23:37 | case <Pattern> | Test.java:25:7:25:16 | default | true |
|
||||
| Test.java:25:7:25:16 | default | Test.java:23:19:23:20 | s2 | Test.java:25:7:25:16 | default | true |
|
||||
#select
|
||||
| Test.java:5:7:5:17 | case ... | Test.java:3:20:3:20 | s | Test.java:5:12:5:14 | "c" | true | false | Test.java:7:7:7:16 | default |
|
||||
| Test.java:5:7:5:17 | case ... | Test.java:3:20:3:20 | s | Test.java:5:12:5:14 | "c" | true | true | Test.java:5:7:5:17 | case ... |
|
||||
| Test.java:6:7:6:17 | case ... | Test.java:3:20:3:20 | s | Test.java:6:12:6:14 | "d" | true | false | Test.java:7:7:7:16 | default |
|
||||
@@ -6,3 +40,11 @@
|
||||
| Test.java:11:7:11:17 | case ... | Test.java:9:13:9:13 | s | Test.java:11:12:11:14 | "c" | true | true | Test.java:11:7:11:17 | case ... |
|
||||
| Test.java:12:7:12:17 | case ... | Test.java:9:13:9:13 | s | Test.java:12:12:12:14 | "d" | true | false | Test.java:13:7:13:16 | default |
|
||||
| Test.java:12:7:12:17 | case ... | Test.java:9:13:9:13 | s | Test.java:12:12:12:14 | "d" | true | true | Test.java:12:7:12:17 | case ... |
|
||||
| Test.java:17:27:17:34 | ... == ... | Test.java:17:27:17:29 | len | Test.java:17:34:17:34 | 4 | true | true | Test.java:17:39:17:41 | { ... } |
|
||||
| Test.java:18:7:18:17 | case ... | Test.java:16:13:16:13 | s | Test.java:18:12:18:14 | "e" | true | false | Test.java:19:7:19:16 | default |
|
||||
| Test.java:18:7:18:17 | case ... | Test.java:16:13:16:13 | s | Test.java:18:12:18:14 | "e" | true | true | Test.java:18:7:18:17 | case ... |
|
||||
| Test.java:22:7:22:17 | case ... | Test.java:21:13:21:41 | ...?...:... | Test.java:22:12:22:14 | "f" | true | false | Test.java:25:7:25:16 | default |
|
||||
| Test.java:22:7:22:17 | case ... | Test.java:21:13:21:41 | ...?...:... | Test.java:22:12:22:14 | "f" | true | true | Test.java:22:7:22:17 | case ... |
|
||||
| Test.java:23:27:23:34 | ... == ... | Test.java:23:27:23:29 | len | Test.java:23:34:23:34 | 4 | true | true | Test.java:23:39:23:41 | { ... } |
|
||||
| Test.java:24:7:24:17 | case ... | Test.java:21:13:21:41 | ...?...:... | Test.java:24:12:24:14 | "g" | true | false | Test.java:25:7:25:16 | default |
|
||||
| Test.java:24:7:24:17 | case ... | Test.java:21:13:21:41 | ...?...:... | Test.java:24:12:24:14 | "g" | true | true | Test.java:24:7:24:17 | case ... |
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import java
|
||||
import semmle.code.java.controlflow.Guards
|
||||
|
||||
from Guard g, BasicBlock bb, boolean branch, VarAccess e1, Expr e2, boolean pol
|
||||
query predicate hasBranchEdge(Guard g, BasicBlock bb1, BasicBlock bb2, boolean branch) {
|
||||
g.hasBranchEdge(bb1, bb2, branch)
|
||||
}
|
||||
|
||||
from Guard g, BasicBlock bb, boolean branch, Expr e1, Expr e2, boolean pol
|
||||
where
|
||||
g.controls(bb, branch) and
|
||||
g.isEquality(e1, e2, pol)
|
||||
g.isEquality(e1, e2, pol) and
|
||||
not e1 instanceof Literal
|
||||
select g, e1, e2, pol, branch, bb
|
||||
|
||||
@@ -1 +1 @@
|
||||
//semmle-extractor-options: --javac-args -source 14 -target 14
|
||||
//semmle-extractor-options: --javac-args --release 21
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
public class Test {
|
||||
|
||||
interface Intf { }
|
||||
static class Specific implements Intf { public String toString() { return "Specific"; } }
|
||||
static class Alternative implements Intf { public String toString() { return "Alternative"; } }
|
||||
|
||||
public static String caller() {
|
||||
|
||||
Alternative a = new Alternative(); // Instantiate this somewhere so there are at least two candidate types in general
|
||||
return test(new Specific());
|
||||
|
||||
}
|
||||
|
||||
public static String test(Object o) {
|
||||
|
||||
if (o instanceof Object o2) {
|
||||
// So we should know o2.toString is really Specific.toString():
|
||||
return o2.toString();
|
||||
}
|
||||
|
||||
switch (o) {
|
||||
case Object o2 when o2.hashCode() > 0 -> { return o2.toString(); } // Same goes for this `o2`
|
||||
default -> { return "Not an Intf"; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
//semmle-extractor-options: --javac-args --release 21
|
||||
@@ -0,0 +1,9 @@
|
||||
| Test.java:1:14:1:17 | super(...) | java.lang.Object.Object |
|
||||
| Test.java:4:16:4:23 | super(...) | java.lang.Object.Object |
|
||||
| Test.java:5:16:5:26 | super(...) | java.lang.Object.Object |
|
||||
| Test.java:9:21:9:37 | new Alternative(...) | Test$Alternative.Alternative |
|
||||
| Test.java:10:12:10:31 | test(...) | Test.test |
|
||||
| Test.java:10:17:10:30 | new Specific(...) | Test$Specific.Specific |
|
||||
| Test.java:18:14:18:26 | toString(...) | Test$Specific.toString |
|
||||
| Test.java:22:27:22:39 | hashCode(...) | java.lang.Object.hashCode |
|
||||
| Test.java:22:57:22:69 | toString(...) | Test$Specific.toString |
|
||||
@@ -0,0 +1,6 @@
|
||||
import java
|
||||
import semmle.code.java.dispatch.VirtualDispatch
|
||||
|
||||
from Call c, Callable c2
|
||||
where c2 = viableCallable(c)
|
||||
select c, c2.getQualifiedName()
|
||||
@@ -0,0 +1,78 @@
|
||||
Test.java:
|
||||
# 0| [CompilationUnit] Test
|
||||
# 1| 1: [Class] Test
|
||||
# 3| 2: [Method] test
|
||||
# 3| 3: [TypeAccess] void
|
||||
#-----| 4: (Parameters)
|
||||
# 3| 0: [Parameter] inp
|
||||
# 3| 0: [TypeAccess] boolean
|
||||
# 3| 5: [BlockStmt] { ... }
|
||||
# 5| 0: [LocalVariableDeclStmt] var ...;
|
||||
# 5| 0: [TypeAccess] String
|
||||
# 5| 1: [LocalVariableDeclExpr] directTaint
|
||||
# 5| 0: [MethodCall] source(...)
|
||||
# 6| 1: [LocalVariableDeclStmt] var ...;
|
||||
# 6| 0: [TypeAccess] String
|
||||
# 6| 1: [LocalVariableDeclExpr] indirectTaint
|
||||
# 6| 0: [MethodCall] source(...)
|
||||
# 8| 2: [LocalVariableDeclStmt] var ...;
|
||||
# 8| 0: [TypeAccess] Object
|
||||
# 8| 1: [LocalVariableDeclExpr] o
|
||||
# 8| 0: [ConditionalExpr] ...?...:...
|
||||
# 8| 0: [VarAccess] inp
|
||||
# 8| 1: [VarAccess] directTaint
|
||||
# 8| 2: [ClassInstanceExpr] new Outer(...)
|
||||
# 8| -3: [TypeAccess] Outer
|
||||
# 8| 0: [ClassInstanceExpr] new Inner(...)
|
||||
# 8| -3: [TypeAccess] Inner
|
||||
# 8| 0: [VarAccess] indirectTaint
|
||||
# 8| 1: [StringLiteral] "not tainted"
|
||||
# 8| 1: [StringLiteral] "not tainted 2"
|
||||
# 10| 3: [IfStmt] if (...)
|
||||
# 10| 0: [InstanceOfExpr] ...instanceof...
|
||||
# 10| 0: [VarAccess] o
|
||||
#-----| 2: (Single Local Variable Declaration)
|
||||
# 10| 0: [TypeAccess] String
|
||||
# 10| 1: [LocalVariableDeclExpr] s
|
||||
# 10| 1: [BlockStmt] { ... }
|
||||
# 11| 0: [ExprStmt] <Expr>;
|
||||
# 11| 0: [MethodCall] sink(...)
|
||||
# 11| 0: [VarAccess] s
|
||||
# 14| 4: [IfStmt] if (...)
|
||||
# 14| 0: [InstanceOfExpr] ...instanceof...
|
||||
# 14| 0: [VarAccess] o
|
||||
# 14| 2: [RecordPatternExpr] Outer(...)
|
||||
# 14| -2: [TypeAccess] String
|
||||
# 14| 0: [RecordPatternExpr] Inner(...)
|
||||
# 14| -2: [TypeAccess] String
|
||||
# 14| -1: [TypeAccess] String
|
||||
# 14| 0: [LocalVariableDeclExpr] tainted
|
||||
# 14| 1: [LocalVariableDeclExpr] notTainted
|
||||
# 14| 1: [LocalVariableDeclExpr] alsoNotTainted
|
||||
# 14| 1: [BlockStmt] { ... }
|
||||
# 15| 0: [ExprStmt] <Expr>;
|
||||
# 15| 0: [MethodCall] sink(...)
|
||||
# 15| 0: [VarAccess] tainted
|
||||
# 16| 1: [ExprStmt] <Expr>;
|
||||
# 16| 0: [MethodCall] sink(...)
|
||||
# 16| 0: [VarAccess] notTainted
|
||||
# 17| 2: [ExprStmt] <Expr>;
|
||||
# 17| 0: [MethodCall] sink(...)
|
||||
# 17| 0: [VarAccess] alsoNotTainted
|
||||
# 22| 3: [Method] source
|
||||
# 22| 3: [TypeAccess] String
|
||||
# 22| 5: [BlockStmt] { ... }
|
||||
# 22| 0: [ReturnStmt] return ...
|
||||
# 22| 0: [StringLiteral] "tainted"
|
||||
# 23| 4: [Method] sink
|
||||
# 23| 3: [TypeAccess] void
|
||||
#-----| 4: (Parameters)
|
||||
# 23| 0: [Parameter] sunk
|
||||
# 23| 0: [TypeAccess] String
|
||||
# 23| 5: [BlockStmt] { ... }
|
||||
# 27| 2: [Class] Outer
|
||||
# 27| 7: [FieldDeclaration] Inner i;
|
||||
# 27| 8: [FieldDeclaration] String otherField;
|
||||
# 28| 3: [Class] Inner
|
||||
# 28| 7: [FieldDeclaration] String taintedField;
|
||||
# 28| 8: [FieldDeclaration] String nonTaintedField;
|
||||
@@ -0,0 +1 @@
|
||||
semmle/code/java/PrintAst.ql
|
||||
28
java/ql/test/library-tests/pattern-instanceof/Test.java
Normal file
28
java/ql/test/library-tests/pattern-instanceof/Test.java
Normal file
@@ -0,0 +1,28 @@
|
||||
public class Test {
|
||||
|
||||
public static void test(boolean inp) {
|
||||
|
||||
String directTaint = source();
|
||||
String indirectTaint = source();
|
||||
|
||||
Object o = inp ? directTaint : new Outer(new Inner(indirectTaint, "not tainted"), "not tainted 2");
|
||||
|
||||
if (o instanceof String s) {
|
||||
sink(s);
|
||||
}
|
||||
|
||||
if (o instanceof Outer(Inner(String tainted, String notTainted), String alsoNotTainted)) {
|
||||
sink(tainted);
|
||||
sink(notTainted);
|
||||
sink(alsoNotTainted);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static String source() { return "tainted"; }
|
||||
public static void sink(String sunk) { }
|
||||
|
||||
}
|
||||
|
||||
record Outer(Inner i, String otherField) { }
|
||||
record Inner(String taintedField, String nonTaintedField) { }
|
||||
72
java/ql/test/library-tests/pattern-instanceof/cfg.expected
Normal file
72
java/ql/test/library-tests/pattern-instanceof/cfg.expected
Normal file
@@ -0,0 +1,72 @@
|
||||
| Test.java:1:14:1:17 | super(...) | Test.java:1:14:1:17 | Test |
|
||||
| Test.java:1:14:1:17 | { ... } | Test.java:1:14:1:17 | super(...) |
|
||||
| Test.java:3:40:20:3 | { ... } | Test.java:5:5:5:34 | var ...; |
|
||||
| Test.java:5:5:5:34 | var ...; | Test.java:5:26:5:33 | source(...) |
|
||||
| Test.java:5:12:5:33 | directTaint | Test.java:6:5:6:36 | var ...; |
|
||||
| Test.java:5:26:5:33 | source(...) | Test.java:5:12:5:33 | directTaint |
|
||||
| Test.java:6:5:6:36 | var ...; | Test.java:6:28:6:35 | source(...) |
|
||||
| Test.java:6:12:6:35 | indirectTaint | Test.java:8:5:8:103 | var ...; |
|
||||
| Test.java:6:28:6:35 | source(...) | Test.java:6:12:6:35 | indirectTaint |
|
||||
| Test.java:8:5:8:103 | var ...; | Test.java:8:16:8:102 | ...?...:... |
|
||||
| Test.java:8:12:8:102 | o | Test.java:10:5:10:30 | if (...) |
|
||||
| Test.java:8:16:8:18 | inp | Test.java:8:22:8:32 | directTaint |
|
||||
| Test.java:8:16:8:18 | inp | Test.java:8:56:8:68 | indirectTaint |
|
||||
| Test.java:8:16:8:102 | ...?...:... | Test.java:8:16:8:18 | inp |
|
||||
| Test.java:8:22:8:32 | directTaint | Test.java:8:12:8:102 | o |
|
||||
| Test.java:8:36:8:102 | new Outer(...) | Test.java:8:12:8:102 | o |
|
||||
| Test.java:8:46:8:84 | new Inner(...) | Test.java:8:87:8:101 | "not tainted 2" |
|
||||
| Test.java:8:56:8:68 | indirectTaint | Test.java:8:71:8:83 | "not tainted" |
|
||||
| Test.java:8:71:8:83 | "not tainted" | Test.java:8:46:8:84 | new Inner(...) |
|
||||
| Test.java:8:87:8:101 | "not tainted 2" | Test.java:8:36:8:102 | new Outer(...) |
|
||||
| Test.java:10:5:10:30 | if (...) | Test.java:10:9:10:9 | o |
|
||||
| Test.java:10:9:10:9 | o | Test.java:10:9:10:29 | ...instanceof... |
|
||||
| Test.java:10:9:10:29 | ...instanceof... | Test.java:10:29:10:29 | s |
|
||||
| Test.java:10:9:10:29 | ...instanceof... | Test.java:14:5:14:92 | if (...) |
|
||||
| Test.java:10:29:10:29 | s | Test.java:10:32:12:5 | { ... } |
|
||||
| Test.java:10:32:12:5 | { ... } | Test.java:11:7:11:14 | <Expr>; |
|
||||
| Test.java:11:7:11:13 | sink(...) | Test.java:14:5:14:92 | if (...) |
|
||||
| Test.java:11:7:11:14 | <Expr>; | Test.java:11:12:11:12 | s |
|
||||
| Test.java:11:12:11:12 | s | Test.java:11:7:11:13 | sink(...) |
|
||||
| Test.java:14:5:14:92 | if (...) | Test.java:14:9:14:9 | o |
|
||||
| Test.java:14:9:14:9 | o | Test.java:14:9:14:91 | ...instanceof... |
|
||||
| Test.java:14:9:14:91 | ...instanceof... | Test.java:3:22:3:25 | test |
|
||||
| Test.java:14:9:14:91 | ...instanceof... | Test.java:14:41:14:47 | tainted |
|
||||
| Test.java:14:22:14:91 | Outer(...) | Test.java:14:94:18:5 | { ... } |
|
||||
| Test.java:14:28:14:67 | Inner(...) | Test.java:14:77:14:90 | alsoNotTainted |
|
||||
| Test.java:14:41:14:47 | tainted | Test.java:14:57:14:66 | notTainted |
|
||||
| Test.java:14:57:14:66 | notTainted | Test.java:14:28:14:67 | Inner(...) |
|
||||
| Test.java:14:77:14:90 | alsoNotTainted | Test.java:14:22:14:91 | Outer(...) |
|
||||
| Test.java:14:94:18:5 | { ... } | Test.java:15:7:15:20 | <Expr>; |
|
||||
| Test.java:15:7:15:19 | sink(...) | Test.java:16:7:16:23 | <Expr>; |
|
||||
| Test.java:15:7:15:20 | <Expr>; | Test.java:15:12:15:18 | tainted |
|
||||
| Test.java:15:12:15:18 | tainted | Test.java:15:7:15:19 | sink(...) |
|
||||
| Test.java:16:7:16:22 | sink(...) | Test.java:17:7:17:27 | <Expr>; |
|
||||
| Test.java:16:7:16:23 | <Expr>; | Test.java:16:12:16:21 | notTainted |
|
||||
| Test.java:16:12:16:21 | notTainted | Test.java:16:7:16:22 | sink(...) |
|
||||
| Test.java:17:7:17:26 | sink(...) | Test.java:3:22:3:25 | test |
|
||||
| Test.java:17:7:17:27 | <Expr>; | Test.java:17:12:17:25 | alsoNotTainted |
|
||||
| Test.java:17:12:17:25 | alsoNotTainted | Test.java:17:7:17:26 | sink(...) |
|
||||
| Test.java:22:33:22:53 | { ... } | Test.java:22:42:22:50 | "tainted" |
|
||||
| Test.java:22:35:22:51 | return ... | Test.java:22:24:22:29 | source |
|
||||
| Test.java:22:42:22:50 | "tainted" | Test.java:22:35:22:51 | return ... |
|
||||
| Test.java:23:40:23:42 | { ... } | Test.java:23:22:23:25 | sink |
|
||||
| Test.java:27:8:27:12 | ...=... | Test.java:27:8:27:12 | <Expr>; |
|
||||
| Test.java:27:8:27:12 | ...=... | Test.java:27:8:27:12 | Outer |
|
||||
| Test.java:27:8:27:12 | <Expr>; | Test.java:27:8:27:12 | this |
|
||||
| Test.java:27:8:27:12 | <Expr>; | Test.java:27:8:27:12 | this |
|
||||
| Test.java:27:8:27:12 | i | Test.java:27:8:27:12 | ...=... |
|
||||
| Test.java:27:8:27:12 | otherField | Test.java:27:8:27:12 | ...=... |
|
||||
| Test.java:27:8:27:12 | super(...) | Test.java:27:8:27:12 | <Expr>; |
|
||||
| Test.java:27:8:27:12 | this | Test.java:27:8:27:12 | i |
|
||||
| Test.java:27:8:27:12 | this | Test.java:27:8:27:12 | otherField |
|
||||
| Test.java:27:8:27:12 | { ... } | Test.java:27:8:27:12 | super(...) |
|
||||
| Test.java:28:8:28:12 | ...=... | Test.java:28:8:28:12 | <Expr>; |
|
||||
| Test.java:28:8:28:12 | ...=... | Test.java:28:8:28:12 | Inner |
|
||||
| Test.java:28:8:28:12 | <Expr>; | Test.java:28:8:28:12 | this |
|
||||
| Test.java:28:8:28:12 | <Expr>; | Test.java:28:8:28:12 | this |
|
||||
| Test.java:28:8:28:12 | nonTaintedField | Test.java:28:8:28:12 | ...=... |
|
||||
| Test.java:28:8:28:12 | super(...) | Test.java:28:8:28:12 | <Expr>; |
|
||||
| Test.java:28:8:28:12 | taintedField | Test.java:28:8:28:12 | ...=... |
|
||||
| Test.java:28:8:28:12 | this | Test.java:28:8:28:12 | nonTaintedField |
|
||||
| Test.java:28:8:28:12 | this | Test.java:28:8:28:12 | taintedField |
|
||||
| Test.java:28:8:28:12 | { ... } | Test.java:28:8:28:12 | super(...) |
|
||||
5
java/ql/test/library-tests/pattern-instanceof/cfg.ql
Normal file
5
java/ql/test/library-tests/pattern-instanceof/cfg.ql
Normal file
@@ -0,0 +1,5 @@
|
||||
import java
|
||||
|
||||
from ControlFlowNode cn
|
||||
where cn.getFile().getBaseName() = "Test.java"
|
||||
select cn, cn.getASuccessor()
|
||||
@@ -0,0 +1,2 @@
|
||||
| Test.java:5:26:5:33 | source(...) | Test.java:11:12:11:12 | s |
|
||||
| Test.java:6:28:6:35 | source(...) | Test.java:15:12:15:18 | tainted |
|
||||
31
java/ql/test/library-tests/pattern-instanceof/dfg.ql
Normal file
31
java/ql/test/library-tests/pattern-instanceof/dfg.ql
Normal file
@@ -0,0 +1,31 @@
|
||||
import java
|
||||
import semmle.code.java.controlflow.Guards
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
|
||||
private predicate isSafe(Guard g, Expr checked, boolean branch) {
|
||||
exists(MethodCall mc | g = mc |
|
||||
mc.getMethod().hasName("isSafe") and
|
||||
checked = mc.getAnArgument() and
|
||||
branch = true
|
||||
)
|
||||
}
|
||||
|
||||
module TestConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr() = any(MethodCall mc | mc.getMethod().getName() = "source")
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(MethodCall mc | mc.getMethod().getName() = "sink").getAnArgument()
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node = DataFlow::BarrierGuard<isSafe/3>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
module Flow = DataFlow::Global<TestConfig>;
|
||||
|
||||
from DataFlow::Node source, DataFlow::Node sink
|
||||
where Flow::flow(source, sink)
|
||||
select source, sink
|
||||
1
java/ql/test/library-tests/pattern-instanceof/options
Normal file
1
java/ql/test/library-tests/pattern-instanceof/options
Normal file
@@ -0,0 +1 @@
|
||||
//semmle-extractor-options: --javac-args --release 21
|
||||
@@ -0,0 +1,31 @@
|
||||
public class Exhaustive {
|
||||
|
||||
enum E { A, B, C };
|
||||
sealed interface I permits X, Y { }
|
||||
final class X implements I { }
|
||||
final class Y implements I { }
|
||||
|
||||
public static void test(E e, I i, Object o) {
|
||||
|
||||
// Check the CFGs of three different ways to be exhaustive -- in particular there shouldn't be a fall-through nothing-matched edge.
|
||||
switch (o) {
|
||||
case String s -> { }
|
||||
case Object o2 -> { }
|
||||
}
|
||||
|
||||
// Exhaustiveness not yet detected by CodeQL, because it is legal to omit some enum entries without a `default` case,
|
||||
// so we'd need to check every enum entry in the type of E occurs in some case.
|
||||
switch (e) {
|
||||
case A -> { }
|
||||
case B -> { }
|
||||
case C -> { }
|
||||
}
|
||||
|
||||
switch (i) {
|
||||
case X x -> { }
|
||||
case Y y -> { }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
106
java/ql/test/library-tests/pattern-switch/cfg/Test.java
Normal file
106
java/ql/test/library-tests/pattern-switch/cfg/Test.java
Normal file
@@ -0,0 +1,106 @@
|
||||
public class Test {
|
||||
|
||||
public static void test(Object thing) {
|
||||
|
||||
switch (thing) {
|
||||
case String s -> System.out.println(s);
|
||||
case Integer i -> System.out.println("An integer: " + i);
|
||||
default -> { }
|
||||
}
|
||||
|
||||
switch (thing) {
|
||||
case String s:
|
||||
System.out.println(s);
|
||||
break;
|
||||
case Integer i:
|
||||
System.out.println("An integer:" + i);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
var thingAsString = switch(thing) {
|
||||
case String s -> s;
|
||||
case Integer i -> "An integer: " + i;
|
||||
default -> "Something else";
|
||||
};
|
||||
|
||||
var thingAsString2 = switch(thing) {
|
||||
case String s:
|
||||
yield s;
|
||||
case Integer i:
|
||||
yield "An integer: " + i;
|
||||
default:
|
||||
yield "Something else";
|
||||
};
|
||||
|
||||
switch(thing) {
|
||||
case String s when s.length() == 3:
|
||||
System.out.println("Length 3");
|
||||
break;
|
||||
case String s when s.length() == 5:
|
||||
System.out.println("Length 5");
|
||||
break;
|
||||
default:
|
||||
System.out.println("Anything else");
|
||||
break;
|
||||
}
|
||||
|
||||
switch(thing) {
|
||||
case String s when s.length() == 3 -> System.out.println("Length 3");
|
||||
case String s when s.length() == 5 -> System.out.println("Length 5");
|
||||
default -> { }
|
||||
}
|
||||
|
||||
switch((String)thing) {
|
||||
case "Const1":
|
||||
System.out.println("It's Const1!");
|
||||
case "Const2":
|
||||
System.out.println("It's Const1 or Const2!");
|
||||
break;
|
||||
case String s when s.length() <= 6:
|
||||
System.out.println("It's <= 6 chars long, and neither Const1 nor Const2");
|
||||
case "Const3":
|
||||
System.out.println("It's (<= 6 chars long, and neither Const1 nor Const2), or Const3");
|
||||
break;
|
||||
case "Const30":
|
||||
System.out.println("It's Const30");
|
||||
break;
|
||||
case null, default:
|
||||
System.out.println("It's null, or something else");
|
||||
}
|
||||
|
||||
switch(thing) {
|
||||
case String s:
|
||||
System.out.println(s);
|
||||
break;
|
||||
case null:
|
||||
System.out.println("It's null");
|
||||
break;
|
||||
case Integer i:
|
||||
System.out.println("An integer:" + i);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch(thing) {
|
||||
case A(B(int x, String y), float z):
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch(thing) {
|
||||
case A(B(var x, var y), var z):
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
record A(B b, float field3) { }
|
||||
record B(int field1, String field2) { }
|
||||
1
java/ql/test/library-tests/pattern-switch/cfg/options
Normal file
1
java/ql/test/library-tests/pattern-switch/cfg/options
Normal file
@@ -0,0 +1 @@
|
||||
//semmle-extractor-options: --javac-args --release 21
|
||||
313
java/ql/test/library-tests/pattern-switch/cfg/test.expected
Normal file
313
java/ql/test/library-tests/pattern-switch/cfg/test.expected
Normal file
@@ -0,0 +1,313 @@
|
||||
| Exhaustive.java:1:14:1:23 | super(...) | Exhaustive.java:1:14:1:23 | Exhaustive |
|
||||
| Exhaustive.java:1:14:1:23 | { ... } | Exhaustive.java:1:14:1:23 | super(...) |
|
||||
| Exhaustive.java:3:8:3:8 | super(...) | Exhaustive.java:3:8:3:8 | E |
|
||||
| Exhaustive.java:3:8:3:8 | { ... } | Exhaustive.java:3:8:3:8 | super(...) |
|
||||
| Exhaustive.java:3:8:3:8 | { ... } | Exhaustive.java:3:12:3:12 | <Expr>; |
|
||||
| Exhaustive.java:3:12:3:12 | ...=... | Exhaustive.java:3:15:3:15 | <Expr>; |
|
||||
| Exhaustive.java:3:12:3:12 | <Expr>; | Exhaustive.java:3:12:3:12 | new E(...) |
|
||||
| Exhaustive.java:3:12:3:12 | new E(...) | Exhaustive.java:3:12:3:12 | ...=... |
|
||||
| Exhaustive.java:3:15:3:15 | ...=... | Exhaustive.java:3:18:3:18 | <Expr>; |
|
||||
| Exhaustive.java:3:15:3:15 | <Expr>; | Exhaustive.java:3:15:3:15 | new E(...) |
|
||||
| Exhaustive.java:3:15:3:15 | new E(...) | Exhaustive.java:3:15:3:15 | ...=... |
|
||||
| Exhaustive.java:3:18:3:18 | ...=... | Exhaustive.java:3:8:3:8 | <clinit> |
|
||||
| Exhaustive.java:3:18:3:18 | <Expr>; | Exhaustive.java:3:18:3:18 | new E(...) |
|
||||
| Exhaustive.java:3:18:3:18 | new E(...) | Exhaustive.java:3:18:3:18 | ...=... |
|
||||
| Exhaustive.java:5:15:5:15 | super(...) | Exhaustive.java:5:15:5:15 | X |
|
||||
| Exhaustive.java:5:15:5:15 | { ... } | Exhaustive.java:5:15:5:15 | super(...) |
|
||||
| Exhaustive.java:6:15:6:15 | super(...) | Exhaustive.java:6:15:6:15 | Y |
|
||||
| Exhaustive.java:6:15:6:15 | { ... } | Exhaustive.java:6:15:6:15 | super(...) |
|
||||
| Exhaustive.java:8:47:29:3 | { ... } | Exhaustive.java:11:5:11:14 | switch (...) |
|
||||
| Exhaustive.java:11:5:11:14 | switch (...) | Exhaustive.java:11:13:11:13 | o |
|
||||
| Exhaustive.java:11:13:11:13 | o | Exhaustive.java:12:7:12:22 | case <Pattern> |
|
||||
| Exhaustive.java:12:7:12:22 | case <Pattern> | Exhaustive.java:12:19:12:19 | s |
|
||||
| Exhaustive.java:12:7:12:22 | case <Pattern> | Exhaustive.java:13:7:13:23 | case <Pattern> |
|
||||
| Exhaustive.java:12:19:12:19 | s | Exhaustive.java:12:24:12:26 | { ... } |
|
||||
| Exhaustive.java:12:24:12:26 | { ... } | Exhaustive.java:18:5:18:14 | switch (...) |
|
||||
| Exhaustive.java:13:7:13:23 | case <Pattern> | Exhaustive.java:13:19:13:20 | o2 |
|
||||
| Exhaustive.java:13:19:13:20 | o2 | Exhaustive.java:13:25:13:27 | { ... } |
|
||||
| Exhaustive.java:13:25:13:27 | { ... } | Exhaustive.java:18:5:18:14 | switch (...) |
|
||||
| Exhaustive.java:18:5:18:14 | switch (...) | Exhaustive.java:18:13:18:13 | e |
|
||||
| Exhaustive.java:18:13:18:13 | e | Exhaustive.java:19:7:19:15 | case ... |
|
||||
| Exhaustive.java:18:13:18:13 | e | Exhaustive.java:20:7:20:15 | case ... |
|
||||
| Exhaustive.java:18:13:18:13 | e | Exhaustive.java:21:7:21:15 | case ... |
|
||||
| Exhaustive.java:18:13:18:13 | e | Exhaustive.java:24:5:24:14 | switch (...) |
|
||||
| Exhaustive.java:19:7:19:15 | case ... | Exhaustive.java:19:17:19:19 | { ... } |
|
||||
| Exhaustive.java:19:17:19:19 | { ... } | Exhaustive.java:24:5:24:14 | switch (...) |
|
||||
| Exhaustive.java:20:7:20:15 | case ... | Exhaustive.java:20:17:20:19 | { ... } |
|
||||
| Exhaustive.java:20:17:20:19 | { ... } | Exhaustive.java:24:5:24:14 | switch (...) |
|
||||
| Exhaustive.java:21:7:21:15 | case ... | Exhaustive.java:21:17:21:19 | { ... } |
|
||||
| Exhaustive.java:21:17:21:19 | { ... } | Exhaustive.java:24:5:24:14 | switch (...) |
|
||||
| Exhaustive.java:24:5:24:14 | switch (...) | Exhaustive.java:24:13:24:13 | i |
|
||||
| Exhaustive.java:24:13:24:13 | i | Exhaustive.java:25:7:25:17 | case <Pattern> |
|
||||
| Exhaustive.java:25:7:25:17 | case <Pattern> | Exhaustive.java:25:14:25:14 | x |
|
||||
| Exhaustive.java:25:7:25:17 | case <Pattern> | Exhaustive.java:26:7:26:17 | case <Pattern> |
|
||||
| Exhaustive.java:25:14:25:14 | x | Exhaustive.java:25:19:25:21 | { ... } |
|
||||
| Exhaustive.java:25:19:25:21 | { ... } | Exhaustive.java:8:22:8:25 | test |
|
||||
| Exhaustive.java:26:7:26:17 | case <Pattern> | Exhaustive.java:26:14:26:14 | y |
|
||||
| Exhaustive.java:26:14:26:14 | y | Exhaustive.java:26:19:26:21 | { ... } |
|
||||
| Exhaustive.java:26:19:26:21 | { ... } | Exhaustive.java:8:22:8:25 | test |
|
||||
| Test.java:1:14:1:17 | super(...) | Test.java:1:14:1:17 | Test |
|
||||
| Test.java:1:14:1:17 | { ... } | Test.java:1:14:1:17 | super(...) |
|
||||
| Test.java:3:41:101:3 | { ... } | Test.java:5:6:5:19 | switch (...) |
|
||||
| Test.java:5:6:5:19 | switch (...) | Test.java:5:14:5:18 | thing |
|
||||
| Test.java:5:14:5:18 | thing | Test.java:6:8:6:23 | case <Pattern> |
|
||||
| Test.java:6:8:6:23 | case <Pattern> | Test.java:6:20:6:20 | s |
|
||||
| Test.java:6:8:6:23 | case <Pattern> | Test.java:7:8:7:24 | case <Pattern> |
|
||||
| Test.java:6:20:6:20 | s | Test.java:6:25:6:34 | System.out |
|
||||
| Test.java:6:25:6:34 | System.out | Test.java:6:44:6:44 | s |
|
||||
| Test.java:6:25:6:45 | println(...) | Test.java:11:6:11:19 | switch (...) |
|
||||
| Test.java:6:25:6:46 | <Expr>; | Test.java:6:25:6:34 | System.out |
|
||||
| Test.java:6:44:6:44 | s | Test.java:6:25:6:45 | println(...) |
|
||||
| Test.java:7:8:7:24 | case <Pattern> | Test.java:7:21:7:21 | i |
|
||||
| Test.java:7:8:7:24 | case <Pattern> | Test.java:8:8:8:17 | default |
|
||||
| Test.java:7:21:7:21 | i | Test.java:7:26:7:35 | System.out |
|
||||
| Test.java:7:26:7:35 | System.out | Test.java:7:45:7:58 | "An integer: " |
|
||||
| Test.java:7:26:7:63 | println(...) | Test.java:11:6:11:19 | switch (...) |
|
||||
| Test.java:7:26:7:64 | <Expr>; | Test.java:7:26:7:35 | System.out |
|
||||
| Test.java:7:45:7:58 | "An integer: " | Test.java:7:62:7:62 | i |
|
||||
| Test.java:7:45:7:62 | ... + ... | Test.java:7:26:7:63 | println(...) |
|
||||
| Test.java:7:62:7:62 | i | Test.java:7:45:7:62 | ... + ... |
|
||||
| Test.java:8:8:8:17 | default | Test.java:8:19:8:21 | { ... } |
|
||||
| Test.java:8:19:8:21 | { ... } | Test.java:11:6:11:19 | switch (...) |
|
||||
| Test.java:11:6:11:19 | switch (...) | Test.java:11:14:11:18 | thing |
|
||||
| Test.java:11:14:11:18 | thing | Test.java:12:8:12:21 | case <Pattern> |
|
||||
| Test.java:12:8:12:21 | case <Pattern> | Test.java:12:20:12:20 | s |
|
||||
| Test.java:12:8:12:21 | case <Pattern> | Test.java:15:8:15:22 | case <Pattern> |
|
||||
| Test.java:12:20:12:20 | s | Test.java:13:10:13:31 | <Expr>; |
|
||||
| Test.java:13:10:13:19 | System.out | Test.java:13:29:13:29 | s |
|
||||
| Test.java:13:10:13:30 | println(...) | Test.java:14:10:14:15 | break |
|
||||
| Test.java:13:10:13:31 | <Expr>; | Test.java:13:10:13:19 | System.out |
|
||||
| Test.java:13:29:13:29 | s | Test.java:13:10:13:30 | println(...) |
|
||||
| Test.java:14:10:14:15 | break | Test.java:22:6:26:7 | var ...; |
|
||||
| Test.java:15:8:15:22 | case <Pattern> | Test.java:15:21:15:21 | i |
|
||||
| Test.java:15:8:15:22 | case <Pattern> | Test.java:18:8:18:15 | default |
|
||||
| Test.java:15:21:15:21 | i | Test.java:16:10:16:47 | <Expr>; |
|
||||
| Test.java:16:10:16:19 | System.out | Test.java:16:29:16:41 | "An integer:" |
|
||||
| Test.java:16:10:16:46 | println(...) | Test.java:17:10:17:15 | break |
|
||||
| Test.java:16:10:16:47 | <Expr>; | Test.java:16:10:16:19 | System.out |
|
||||
| Test.java:16:29:16:41 | "An integer:" | Test.java:16:45:16:45 | i |
|
||||
| Test.java:16:29:16:45 | ... + ... | Test.java:16:10:16:46 | println(...) |
|
||||
| Test.java:16:45:16:45 | i | Test.java:16:29:16:45 | ... + ... |
|
||||
| Test.java:17:10:17:15 | break | Test.java:22:6:26:7 | var ...; |
|
||||
| Test.java:18:8:18:15 | default | Test.java:19:10:19:15 | break |
|
||||
| Test.java:19:10:19:15 | break | Test.java:22:6:26:7 | var ...; |
|
||||
| Test.java:22:6:26:7 | var ...; | Test.java:22:26:22:38 | switch (...) |
|
||||
| Test.java:22:10:22:38 | thingAsString | Test.java:28:6:35:7 | var ...; |
|
||||
| Test.java:22:26:22:38 | switch (...) | Test.java:22:33:22:37 | thing |
|
||||
| Test.java:22:33:22:37 | thing | Test.java:23:8:23:23 | case <Pattern> |
|
||||
| Test.java:23:8:23:23 | case <Pattern> | Test.java:23:20:23:20 | s |
|
||||
| Test.java:23:8:23:23 | case <Pattern> | Test.java:24:8:24:24 | case <Pattern> |
|
||||
| Test.java:23:20:23:20 | s | Test.java:23:25:23:25 | s |
|
||||
| Test.java:23:25:23:25 | s | Test.java:22:10:22:38 | thingAsString |
|
||||
| Test.java:24:8:24:24 | case <Pattern> | Test.java:24:21:24:21 | i |
|
||||
| Test.java:24:8:24:24 | case <Pattern> | Test.java:25:8:25:17 | default |
|
||||
| Test.java:24:21:24:21 | i | Test.java:24:26:24:39 | "An integer: " |
|
||||
| Test.java:24:26:24:39 | "An integer: " | Test.java:24:43:24:43 | i |
|
||||
| Test.java:24:26:24:43 | ... + ... | Test.java:22:10:22:38 | thingAsString |
|
||||
| Test.java:24:43:24:43 | i | Test.java:24:26:24:43 | ... + ... |
|
||||
| Test.java:25:8:25:17 | default | Test.java:25:19:25:34 | "Something else" |
|
||||
| Test.java:25:19:25:34 | "Something else" | Test.java:22:10:22:38 | thingAsString |
|
||||
| Test.java:28:6:35:7 | var ...; | Test.java:28:27:28:39 | switch (...) |
|
||||
| Test.java:28:10:28:39 | thingAsString2 | Test.java:37:6:37:18 | switch (...) |
|
||||
| Test.java:28:27:28:39 | switch (...) | Test.java:28:34:28:38 | thing |
|
||||
| Test.java:28:34:28:38 | thing | Test.java:29:8:29:21 | case <Pattern> |
|
||||
| Test.java:29:8:29:21 | case <Pattern> | Test.java:29:20:29:20 | s |
|
||||
| Test.java:29:8:29:21 | case <Pattern> | Test.java:31:8:31:22 | case <Pattern> |
|
||||
| Test.java:29:20:29:20 | s | Test.java:30:10:30:17 | yield ... |
|
||||
| Test.java:30:10:30:17 | yield ... | Test.java:30:16:30:16 | s |
|
||||
| Test.java:30:16:30:16 | s | Test.java:28:10:28:39 | thingAsString2 |
|
||||
| Test.java:31:8:31:22 | case <Pattern> | Test.java:31:21:31:21 | i |
|
||||
| Test.java:31:8:31:22 | case <Pattern> | Test.java:33:8:33:15 | default |
|
||||
| Test.java:31:21:31:21 | i | Test.java:32:10:32:34 | yield ... |
|
||||
| Test.java:32:10:32:34 | yield ... | Test.java:32:16:32:29 | "An integer: " |
|
||||
| Test.java:32:16:32:29 | "An integer: " | Test.java:32:33:32:33 | i |
|
||||
| Test.java:32:16:32:33 | ... + ... | Test.java:28:10:28:39 | thingAsString2 |
|
||||
| Test.java:32:33:32:33 | i | Test.java:32:16:32:33 | ... + ... |
|
||||
| Test.java:33:8:33:15 | default | Test.java:34:10:34:32 | yield ... |
|
||||
| Test.java:34:10:34:32 | yield ... | Test.java:34:16:34:31 | "Something else" |
|
||||
| Test.java:34:16:34:31 | "Something else" | Test.java:28:10:28:39 | thingAsString2 |
|
||||
| Test.java:37:6:37:18 | switch (...) | Test.java:37:13:37:17 | thing |
|
||||
| Test.java:37:13:37:17 | thing | Test.java:38:8:38:42 | case <Pattern> |
|
||||
| Test.java:38:8:38:42 | case <Pattern> | Test.java:38:20:38:20 | s |
|
||||
| Test.java:38:8:38:42 | case <Pattern> | Test.java:41:8:41:42 | case <Pattern> |
|
||||
| Test.java:38:20:38:20 | s | Test.java:38:27:38:27 | s |
|
||||
| Test.java:38:27:38:27 | s | Test.java:38:27:38:36 | length(...) |
|
||||
| Test.java:38:27:38:36 | length(...) | Test.java:38:41:38:41 | 3 |
|
||||
| Test.java:38:27:38:41 | ... == ... | Test.java:39:10:39:40 | <Expr>; |
|
||||
| Test.java:38:27:38:41 | ... == ... | Test.java:41:8:41:42 | case <Pattern> |
|
||||
| Test.java:38:41:38:41 | 3 | Test.java:38:27:38:41 | ... == ... |
|
||||
| Test.java:39:10:39:19 | System.out | Test.java:39:29:39:38 | "Length 3" |
|
||||
| Test.java:39:10:39:39 | println(...) | Test.java:40:10:40:15 | break |
|
||||
| Test.java:39:10:39:40 | <Expr>; | Test.java:39:10:39:19 | System.out |
|
||||
| Test.java:39:29:39:38 | "Length 3" | Test.java:39:10:39:39 | println(...) |
|
||||
| Test.java:40:10:40:15 | break | Test.java:49:6:49:18 | switch (...) |
|
||||
| Test.java:41:8:41:42 | case <Pattern> | Test.java:41:20:41:20 | s |
|
||||
| Test.java:41:8:41:42 | case <Pattern> | Test.java:44:8:44:15 | default |
|
||||
| Test.java:41:20:41:20 | s | Test.java:41:27:41:27 | s |
|
||||
| Test.java:41:27:41:27 | s | Test.java:41:27:41:36 | length(...) |
|
||||
| Test.java:41:27:41:36 | length(...) | Test.java:41:41:41:41 | 5 |
|
||||
| Test.java:41:27:41:41 | ... == ... | Test.java:42:10:42:40 | <Expr>; |
|
||||
| Test.java:41:27:41:41 | ... == ... | Test.java:44:8:44:15 | default |
|
||||
| Test.java:41:41:41:41 | 5 | Test.java:41:27:41:41 | ... == ... |
|
||||
| Test.java:42:10:42:19 | System.out | Test.java:42:29:42:38 | "Length 5" |
|
||||
| Test.java:42:10:42:39 | println(...) | Test.java:43:10:43:15 | break |
|
||||
| Test.java:42:10:42:40 | <Expr>; | Test.java:42:10:42:19 | System.out |
|
||||
| Test.java:42:29:42:38 | "Length 5" | Test.java:42:10:42:39 | println(...) |
|
||||
| Test.java:43:10:43:15 | break | Test.java:49:6:49:18 | switch (...) |
|
||||
| Test.java:44:8:44:15 | default | Test.java:45:10:45:45 | <Expr>; |
|
||||
| Test.java:45:10:45:19 | System.out | Test.java:45:29:45:43 | "Anything else" |
|
||||
| Test.java:45:10:45:44 | println(...) | Test.java:46:10:46:15 | break |
|
||||
| Test.java:45:10:45:45 | <Expr>; | Test.java:45:10:45:19 | System.out |
|
||||
| Test.java:45:29:45:43 | "Anything else" | Test.java:45:10:45:44 | println(...) |
|
||||
| Test.java:46:10:46:15 | break | Test.java:49:6:49:18 | switch (...) |
|
||||
| Test.java:49:6:49:18 | switch (...) | Test.java:49:13:49:17 | thing |
|
||||
| Test.java:49:13:49:17 | thing | Test.java:50:8:50:44 | case <Pattern> |
|
||||
| Test.java:50:8:50:44 | case <Pattern> | Test.java:50:20:50:20 | s |
|
||||
| Test.java:50:8:50:44 | case <Pattern> | Test.java:51:8:51:44 | case <Pattern> |
|
||||
| Test.java:50:20:50:20 | s | Test.java:50:27:50:27 | s |
|
||||
| Test.java:50:27:50:27 | s | Test.java:50:27:50:36 | length(...) |
|
||||
| Test.java:50:27:50:36 | length(...) | Test.java:50:41:50:41 | 3 |
|
||||
| Test.java:50:27:50:41 | ... == ... | Test.java:50:46:50:55 | System.out |
|
||||
| Test.java:50:27:50:41 | ... == ... | Test.java:51:8:51:44 | case <Pattern> |
|
||||
| Test.java:50:41:50:41 | 3 | Test.java:50:27:50:41 | ... == ... |
|
||||
| Test.java:50:46:50:55 | System.out | Test.java:50:65:50:74 | "Length 3" |
|
||||
| Test.java:50:46:50:75 | println(...) | Test.java:55:6:55:26 | switch (...) |
|
||||
| Test.java:50:46:50:76 | <Expr>; | Test.java:50:46:50:55 | System.out |
|
||||
| Test.java:50:65:50:74 | "Length 3" | Test.java:50:46:50:75 | println(...) |
|
||||
| Test.java:51:8:51:44 | case <Pattern> | Test.java:51:20:51:20 | s |
|
||||
| Test.java:51:8:51:44 | case <Pattern> | Test.java:52:8:52:17 | default |
|
||||
| Test.java:51:20:51:20 | s | Test.java:51:27:51:27 | s |
|
||||
| Test.java:51:27:51:27 | s | Test.java:51:27:51:36 | length(...) |
|
||||
| Test.java:51:27:51:36 | length(...) | Test.java:51:41:51:41 | 5 |
|
||||
| Test.java:51:27:51:41 | ... == ... | Test.java:51:46:51:55 | System.out |
|
||||
| Test.java:51:27:51:41 | ... == ... | Test.java:52:8:52:17 | default |
|
||||
| Test.java:51:41:51:41 | 5 | Test.java:51:27:51:41 | ... == ... |
|
||||
| Test.java:51:46:51:55 | System.out | Test.java:51:65:51:74 | "Length 5" |
|
||||
| Test.java:51:46:51:75 | println(...) | Test.java:55:6:55:26 | switch (...) |
|
||||
| Test.java:51:46:51:76 | <Expr>; | Test.java:51:46:51:55 | System.out |
|
||||
| Test.java:51:65:51:74 | "Length 5" | Test.java:51:46:51:75 | println(...) |
|
||||
| Test.java:52:8:52:17 | default | Test.java:52:19:52:21 | { ... } |
|
||||
| Test.java:52:19:52:21 | { ... } | Test.java:55:6:55:26 | switch (...) |
|
||||
| Test.java:55:6:55:26 | switch (...) | Test.java:55:21:55:25 | thing |
|
||||
| Test.java:55:13:55:25 | (...)... | Test.java:56:8:56:21 | case ... |
|
||||
| Test.java:55:13:55:25 | (...)... | Test.java:58:8:58:21 | case ... |
|
||||
| Test.java:55:13:55:25 | (...)... | Test.java:61:8:61:42 | case <Pattern> |
|
||||
| Test.java:55:13:55:25 | (...)... | Test.java:69:8:69:26 | case null, default |
|
||||
| Test.java:55:21:55:25 | thing | Test.java:55:13:55:25 | (...)... |
|
||||
| Test.java:56:8:56:21 | case ... | Test.java:57:10:57:44 | <Expr>; |
|
||||
| Test.java:57:10:57:19 | System.out | Test.java:57:29:57:42 | "It's Const1!" |
|
||||
| Test.java:57:10:57:43 | println(...) | Test.java:58:8:58:21 | case ... |
|
||||
| Test.java:57:10:57:44 | <Expr>; | Test.java:57:10:57:19 | System.out |
|
||||
| Test.java:57:29:57:42 | "It's Const1!" | Test.java:57:10:57:43 | println(...) |
|
||||
| Test.java:58:8:58:21 | case ... | Test.java:59:10:59:54 | <Expr>; |
|
||||
| Test.java:59:10:59:19 | System.out | Test.java:59:29:59:52 | "It's Const1 or Const2!" |
|
||||
| Test.java:59:10:59:53 | println(...) | Test.java:60:10:60:15 | break |
|
||||
| Test.java:59:10:59:54 | <Expr>; | Test.java:59:10:59:19 | System.out |
|
||||
| Test.java:59:29:59:52 | "It's Const1 or Const2!" | Test.java:59:10:59:53 | println(...) |
|
||||
| Test.java:60:10:60:15 | break | Test.java:73:6:73:18 | switch (...) |
|
||||
| Test.java:61:8:61:42 | case <Pattern> | Test.java:61:20:61:20 | s |
|
||||
| Test.java:61:8:61:42 | case <Pattern> | Test.java:63:8:63:21 | case ... |
|
||||
| Test.java:61:8:61:42 | case <Pattern> | Test.java:66:8:66:22 | case ... |
|
||||
| Test.java:61:8:61:42 | case <Pattern> | Test.java:69:8:69:26 | case null, default |
|
||||
| Test.java:61:20:61:20 | s | Test.java:61:27:61:27 | s |
|
||||
| Test.java:61:27:61:27 | s | Test.java:61:27:61:36 | length(...) |
|
||||
| Test.java:61:27:61:36 | length(...) | Test.java:61:41:61:41 | 6 |
|
||||
| Test.java:61:27:61:41 | ... <= ... | Test.java:62:10:62:83 | <Expr>; |
|
||||
| Test.java:61:27:61:41 | ... <= ... | Test.java:63:8:63:21 | case ... |
|
||||
| Test.java:61:27:61:41 | ... <= ... | Test.java:66:8:66:22 | case ... |
|
||||
| Test.java:61:27:61:41 | ... <= ... | Test.java:69:8:69:26 | case null, default |
|
||||
| Test.java:61:41:61:41 | 6 | Test.java:61:27:61:41 | ... <= ... |
|
||||
| Test.java:62:10:62:19 | System.out | Test.java:62:29:62:81 | "It's <= 6 chars long, and neither Const1 nor Const2" |
|
||||
| Test.java:62:10:62:82 | println(...) | Test.java:63:8:63:21 | case ... |
|
||||
| Test.java:62:10:62:83 | <Expr>; | Test.java:62:10:62:19 | System.out |
|
||||
| Test.java:62:29:62:81 | "It's <= 6 chars long, and neither Const1 nor Const2" | Test.java:62:10:62:82 | println(...) |
|
||||
| Test.java:63:8:63:21 | case ... | Test.java:64:10:64:96 | <Expr>; |
|
||||
| Test.java:64:10:64:19 | System.out | Test.java:64:29:64:94 | "It's (<= 6 chars long, and neither Const1 nor Const2), or Const3" |
|
||||
| Test.java:64:10:64:95 | println(...) | Test.java:65:10:65:15 | break |
|
||||
| Test.java:64:10:64:96 | <Expr>; | Test.java:64:10:64:19 | System.out |
|
||||
| Test.java:64:29:64:94 | "It's (<= 6 chars long, and neither Const1 nor Const2), or Const3" | Test.java:64:10:64:95 | println(...) |
|
||||
| Test.java:65:10:65:15 | break | Test.java:73:6:73:18 | switch (...) |
|
||||
| Test.java:66:8:66:22 | case ... | Test.java:67:10:67:44 | <Expr>; |
|
||||
| Test.java:67:10:67:19 | System.out | Test.java:67:29:67:42 | "It's Const30" |
|
||||
| Test.java:67:10:67:43 | println(...) | Test.java:68:10:68:15 | break |
|
||||
| Test.java:67:10:67:44 | <Expr>; | Test.java:67:10:67:19 | System.out |
|
||||
| Test.java:67:29:67:42 | "It's Const30" | Test.java:67:10:67:43 | println(...) |
|
||||
| Test.java:68:10:68:15 | break | Test.java:73:6:73:18 | switch (...) |
|
||||
| Test.java:69:8:69:26 | case null, default | Test.java:70:10:70:60 | <Expr>; |
|
||||
| Test.java:70:10:70:19 | System.out | Test.java:70:29:70:58 | "It's null, or something else" |
|
||||
| Test.java:70:10:70:59 | println(...) | Test.java:73:6:73:18 | switch (...) |
|
||||
| Test.java:70:10:70:60 | <Expr>; | Test.java:70:10:70:19 | System.out |
|
||||
| Test.java:70:29:70:58 | "It's null, or something else" | Test.java:70:10:70:59 | println(...) |
|
||||
| Test.java:73:6:73:18 | switch (...) | Test.java:73:13:73:17 | thing |
|
||||
| Test.java:73:13:73:17 | thing | Test.java:74:8:74:21 | case <Pattern> |
|
||||
| Test.java:73:13:73:17 | thing | Test.java:77:8:77:17 | case ... |
|
||||
| Test.java:74:8:74:21 | case <Pattern> | Test.java:74:20:74:20 | s |
|
||||
| Test.java:74:8:74:21 | case <Pattern> | Test.java:80:8:80:22 | case <Pattern> |
|
||||
| Test.java:74:20:74:20 | s | Test.java:75:10:75:31 | <Expr>; |
|
||||
| Test.java:75:10:75:19 | System.out | Test.java:75:29:75:29 | s |
|
||||
| Test.java:75:10:75:30 | println(...) | Test.java:76:10:76:15 | break |
|
||||
| Test.java:75:10:75:31 | <Expr>; | Test.java:75:10:75:19 | System.out |
|
||||
| Test.java:75:29:75:29 | s | Test.java:75:10:75:30 | println(...) |
|
||||
| Test.java:76:10:76:15 | break | Test.java:87:6:87:18 | switch (...) |
|
||||
| Test.java:77:8:77:17 | case ... | Test.java:78:10:78:41 | <Expr>; |
|
||||
| Test.java:78:10:78:19 | System.out | Test.java:78:29:78:39 | "It's null" |
|
||||
| Test.java:78:10:78:40 | println(...) | Test.java:79:10:79:15 | break |
|
||||
| Test.java:78:10:78:41 | <Expr>; | Test.java:78:10:78:19 | System.out |
|
||||
| Test.java:78:29:78:39 | "It's null" | Test.java:78:10:78:40 | println(...) |
|
||||
| Test.java:79:10:79:15 | break | Test.java:87:6:87:18 | switch (...) |
|
||||
| Test.java:80:8:80:22 | case <Pattern> | Test.java:80:21:80:21 | i |
|
||||
| Test.java:80:8:80:22 | case <Pattern> | Test.java:83:8:83:15 | default |
|
||||
| Test.java:80:21:80:21 | i | Test.java:81:10:81:47 | <Expr>; |
|
||||
| Test.java:81:10:81:19 | System.out | Test.java:81:29:81:41 | "An integer:" |
|
||||
| Test.java:81:10:81:46 | println(...) | Test.java:82:10:82:15 | break |
|
||||
| Test.java:81:10:81:47 | <Expr>; | Test.java:81:10:81:19 | System.out |
|
||||
| Test.java:81:29:81:41 | "An integer:" | Test.java:81:45:81:45 | i |
|
||||
| Test.java:81:29:81:45 | ... + ... | Test.java:81:10:81:46 | println(...) |
|
||||
| Test.java:81:45:81:45 | i | Test.java:81:29:81:45 | ... + ... |
|
||||
| Test.java:82:10:82:15 | break | Test.java:87:6:87:18 | switch (...) |
|
||||
| Test.java:83:8:83:15 | default | Test.java:84:10:84:15 | break |
|
||||
| Test.java:84:10:84:15 | break | Test.java:87:6:87:18 | switch (...) |
|
||||
| Test.java:87:6:87:18 | switch (...) | Test.java:87:13:87:17 | thing |
|
||||
| Test.java:87:13:87:17 | thing | Test.java:88:8:88:43 | case <Pattern> |
|
||||
| Test.java:88:8:88:43 | case <Pattern> | Test.java:88:21:88:21 | x |
|
||||
| Test.java:88:8:88:43 | case <Pattern> | Test.java:90:8:90:15 | default |
|
||||
| Test.java:88:13:88:42 | A(...) | Test.java:89:10:89:15 | break |
|
||||
| Test.java:88:15:88:32 | B(...) | Test.java:88:41:88:41 | z |
|
||||
| Test.java:88:21:88:21 | x | Test.java:88:31:88:31 | y |
|
||||
| Test.java:88:31:88:31 | y | Test.java:88:15:88:32 | B(...) |
|
||||
| Test.java:88:41:88:41 | z | Test.java:88:13:88:42 | A(...) |
|
||||
| Test.java:89:10:89:15 | break | Test.java:94:6:94:18 | switch (...) |
|
||||
| Test.java:90:8:90:15 | default | Test.java:91:10:91:15 | break |
|
||||
| Test.java:91:10:91:15 | break | Test.java:94:6:94:18 | switch (...) |
|
||||
| Test.java:94:6:94:18 | switch (...) | Test.java:94:13:94:17 | thing |
|
||||
| Test.java:94:13:94:17 | thing | Test.java:95:8:95:38 | case <Pattern> |
|
||||
| Test.java:95:8:95:38 | case <Pattern> | Test.java:95:21:95:21 | x |
|
||||
| Test.java:95:8:95:38 | case <Pattern> | Test.java:97:8:97:15 | default |
|
||||
| Test.java:95:13:95:37 | A(...) | Test.java:96:10:96:15 | break |
|
||||
| Test.java:95:15:95:29 | B(...) | Test.java:95:36:95:36 | z |
|
||||
| Test.java:95:21:95:21 | x | Test.java:95:28:95:28 | y |
|
||||
| Test.java:95:28:95:28 | y | Test.java:95:15:95:29 | B(...) |
|
||||
| Test.java:95:36:95:36 | z | Test.java:95:13:95:37 | A(...) |
|
||||
| Test.java:96:10:96:15 | break | Test.java:3:22:3:25 | test |
|
||||
| Test.java:97:8:97:15 | default | Test.java:98:10:98:15 | break |
|
||||
| Test.java:98:10:98:15 | break | Test.java:3:22:3:25 | test |
|
||||
| Test.java:105:8:105:8 | ...=... | Test.java:105:8:105:8 | <Expr>; |
|
||||
| Test.java:105:8:105:8 | ...=... | Test.java:105:8:105:8 | A |
|
||||
| Test.java:105:8:105:8 | <Expr>; | Test.java:105:8:105:8 | this |
|
||||
| Test.java:105:8:105:8 | <Expr>; | Test.java:105:8:105:8 | this |
|
||||
| Test.java:105:8:105:8 | b | Test.java:105:8:105:8 | ...=... |
|
||||
| Test.java:105:8:105:8 | field3 | Test.java:105:8:105:8 | ...=... |
|
||||
| Test.java:105:8:105:8 | super(...) | Test.java:105:8:105:8 | <Expr>; |
|
||||
| Test.java:105:8:105:8 | this | Test.java:105:8:105:8 | b |
|
||||
| Test.java:105:8:105:8 | this | Test.java:105:8:105:8 | field3 |
|
||||
| Test.java:105:8:105:8 | { ... } | Test.java:105:8:105:8 | super(...) |
|
||||
| Test.java:106:8:106:8 | ...=... | Test.java:106:8:106:8 | <Expr>; |
|
||||
| Test.java:106:8:106:8 | ...=... | Test.java:106:8:106:8 | B |
|
||||
| Test.java:106:8:106:8 | <Expr>; | Test.java:106:8:106:8 | this |
|
||||
| Test.java:106:8:106:8 | <Expr>; | Test.java:106:8:106:8 | this |
|
||||
| Test.java:106:8:106:8 | field1 | Test.java:106:8:106:8 | ...=... |
|
||||
| Test.java:106:8:106:8 | field2 | Test.java:106:8:106:8 | ...=... |
|
||||
| Test.java:106:8:106:8 | super(...) | Test.java:106:8:106:8 | <Expr>; |
|
||||
| Test.java:106:8:106:8 | this | Test.java:106:8:106:8 | field1 |
|
||||
| Test.java:106:8:106:8 | this | Test.java:106:8:106:8 | field2 |
|
||||
| Test.java:106:8:106:8 | { ... } | Test.java:106:8:106:8 | super(...) |
|
||||
5
java/ql/test/library-tests/pattern-switch/cfg/test.ql
Normal file
5
java/ql/test/library-tests/pattern-switch/cfg/test.ql
Normal file
@@ -0,0 +1,5 @@
|
||||
import java
|
||||
|
||||
from ControlFlowNode cn
|
||||
where cn.getFile().getBaseName() = ["Test.java", "Exhaustive.java"]
|
||||
select cn, cn.getASuccessor()
|
||||
30
java/ql/test/library-tests/pattern-switch/dfg/GuardTest.java
Normal file
30
java/ql/test/library-tests/pattern-switch/dfg/GuardTest.java
Normal file
@@ -0,0 +1,30 @@
|
||||
public class GuardTest {
|
||||
|
||||
public static void sink(String s) { }
|
||||
public static boolean isSafe(String s) { return s.length() < 10; }
|
||||
|
||||
public static void test(Object o) {
|
||||
|
||||
switch (o) {
|
||||
|
||||
case String s:
|
||||
sink(s);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
switch (o) {
|
||||
|
||||
case String s when isSafe(s):
|
||||
sink(s);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
public class RecordTest {
|
||||
|
||||
interface I { }
|
||||
record Middle(String field) { }
|
||||
record A(Middle afield) implements I { }
|
||||
record B(Middle bfield) implements I { }
|
||||
|
||||
public static String sink(String s) { return s; }
|
||||
|
||||
public static void test(boolean inp) {
|
||||
|
||||
I i = inp ? new A(new Middle("A")) : new B(new Middle("B"));
|
||||
|
||||
switch(i) {
|
||||
case A(Middle(String field)):
|
||||
sink(field);
|
||||
break;
|
||||
case B(Middle(String field)):
|
||||
sink(field);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch(i) {
|
||||
case A(Middle(String field)) -> sink(field);
|
||||
case B(Middle(String field)) -> sink(field);
|
||||
default -> { }
|
||||
}
|
||||
|
||||
var x = switch(i) {
|
||||
case A(Middle(String field)):
|
||||
yield sink(field);
|
||||
case B(Middle(String field)):
|
||||
yield sink(field);
|
||||
default:
|
||||
yield "Default case";
|
||||
};
|
||||
|
||||
var y = switch(i) {
|
||||
case A(Middle(String field)) -> sink(field);
|
||||
case B(Middle(String field)) -> sink(field);
|
||||
default -> "Default case";
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
47
java/ql/test/library-tests/pattern-switch/dfg/Test.java
Normal file
47
java/ql/test/library-tests/pattern-switch/dfg/Test.java
Normal file
@@ -0,0 +1,47 @@
|
||||
public class Test {
|
||||
|
||||
interface I { String get(); }
|
||||
static class A implements I { public String afield; public A(String a) { this.afield = a; } public String get() { return afield; } }
|
||||
static class B implements I { public String bfield; public B(String b) { this.bfield = b; } public String get() { return bfield; } }
|
||||
|
||||
public static String sink(String s) { return s; }
|
||||
|
||||
public static void test(boolean inp) {
|
||||
|
||||
I i = inp ? new A("A") : new B("B");
|
||||
|
||||
switch(i) {
|
||||
case A a:
|
||||
sink(a.get());
|
||||
break;
|
||||
case B b:
|
||||
sink(b.get());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch(i) {
|
||||
case A a -> sink(a.get());
|
||||
case B b -> sink(b.get());
|
||||
default -> { }
|
||||
}
|
||||
|
||||
var x = switch(i) {
|
||||
case A a:
|
||||
yield sink(a.get());
|
||||
case B b:
|
||||
yield sink(b.get());
|
||||
default:
|
||||
yield "Default case";
|
||||
};
|
||||
|
||||
var y = switch(i) {
|
||||
case A a -> sink(a.get());
|
||||
case B b -> sink(b.get());
|
||||
default -> "Default case";
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
1
java/ql/test/library-tests/pattern-switch/dfg/options
Normal file
1
java/ql/test/library-tests/pattern-switch/dfg/options
Normal file
@@ -0,0 +1 @@
|
||||
//semmle-extractor-options: --javac-args --release 21
|
||||
17
java/ql/test/library-tests/pattern-switch/dfg/test.expected
Normal file
17
java/ql/test/library-tests/pattern-switch/dfg/test.expected
Normal file
@@ -0,0 +1,17 @@
|
||||
| GuardTest.java:6:27:6:34 | o | GuardTest.java:11:14:11:14 | s |
|
||||
| RecordTest.java:12:34:12:36 | "A" | RecordTest.java:16:14:16:18 | field |
|
||||
| RecordTest.java:12:34:12:36 | "A" | RecordTest.java:26:44:26:48 | field |
|
||||
| RecordTest.java:12:34:12:36 | "A" | RecordTest.java:33:20:33:24 | field |
|
||||
| RecordTest.java:12:34:12:36 | "A" | RecordTest.java:41:44:41:48 | field |
|
||||
| RecordTest.java:12:59:12:61 | "B" | RecordTest.java:19:14:19:18 | field |
|
||||
| RecordTest.java:12:59:12:61 | "B" | RecordTest.java:27:44:27:48 | field |
|
||||
| RecordTest.java:12:59:12:61 | "B" | RecordTest.java:35:20:35:24 | field |
|
||||
| RecordTest.java:12:59:12:61 | "B" | RecordTest.java:42:44:42:48 | field |
|
||||
| Test.java:11:23:11:25 | "A" | Test.java:15:14:15:20 | get(...) |
|
||||
| Test.java:11:23:11:25 | "A" | Test.java:25:24:25:30 | get(...) |
|
||||
| Test.java:11:23:11:25 | "A" | Test.java:32:20:32:26 | get(...) |
|
||||
| Test.java:11:23:11:25 | "A" | Test.java:40:24:40:30 | get(...) |
|
||||
| Test.java:11:36:11:38 | "B" | Test.java:18:14:18:20 | get(...) |
|
||||
| Test.java:11:36:11:38 | "B" | Test.java:26:24:26:30 | get(...) |
|
||||
| Test.java:11:36:11:38 | "B" | Test.java:34:20:34:26 | get(...) |
|
||||
| Test.java:11:36:11:38 | "B" | Test.java:41:24:41:30 | get(...) |
|
||||
31
java/ql/test/library-tests/pattern-switch/dfg/test.ql
Normal file
31
java/ql/test/library-tests/pattern-switch/dfg/test.ql
Normal file
@@ -0,0 +1,31 @@
|
||||
import java
|
||||
import semmle.code.java.controlflow.Guards
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
|
||||
private predicate isSafe(Guard g, Expr checked, boolean branch) {
|
||||
exists(MethodCall mc | g = mc |
|
||||
mc.getMethod().hasName("isSafe") and
|
||||
checked = mc.getAnArgument() and
|
||||
branch = true
|
||||
)
|
||||
}
|
||||
|
||||
module TestConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr() instanceof StringLiteral or source.asParameter().getCallable().hasName("test")
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(MethodCall mc | mc.getMethod().getName() = "sink").getAnArgument()
|
||||
}
|
||||
|
||||
predicate isBarrier(DataFlow::Node node) {
|
||||
node = DataFlow::BarrierGuard<isSafe/3>::getABarrierNode()
|
||||
}
|
||||
}
|
||||
|
||||
module Flow = DataFlow::Global<TestConfig>;
|
||||
|
||||
from DataFlow::Node source, DataFlow::Node sink
|
||||
where Flow::flow(source, sink)
|
||||
select source, sink
|
||||
50
java/ql/test/library-tests/prettyprint/Test.java
Normal file
50
java/ql/test/library-tests/prettyprint/Test.java
Normal file
@@ -0,0 +1,50 @@
|
||||
public class Test {
|
||||
|
||||
record S(int x) { }
|
||||
record R(S s, String y) { }
|
||||
|
||||
public static void test(Object o) {
|
||||
|
||||
switch(o) {
|
||||
case String s:
|
||||
break;
|
||||
case R(S(int x), String y):
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
switch(o) {
|
||||
case String s -> { }
|
||||
case R(S(int x), String y) -> { }
|
||||
case null, default -> { }
|
||||
}
|
||||
|
||||
var a = switch(o) {
|
||||
case String s:
|
||||
yield 1;
|
||||
case R(S(int x), String y):
|
||||
yield x;
|
||||
case null, default:
|
||||
yield 2;
|
||||
};
|
||||
|
||||
var b = switch(o) {
|
||||
case String s -> 1;
|
||||
case R(S(int x), String y) -> x;
|
||||
default -> 2;
|
||||
};
|
||||
|
||||
if (o instanceof String s) { }
|
||||
if (o instanceof R(S(int x), String y)) { }
|
||||
|
||||
switch(o) {
|
||||
case R(S(var x), var y) -> { }
|
||||
case null, default -> { }
|
||||
}
|
||||
|
||||
if (o instanceof R(S(var x), var y)) { }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
1
java/ql/test/library-tests/prettyprint/options
Normal file
1
java/ql/test/library-tests/prettyprint/options
Normal file
@@ -0,0 +1 @@
|
||||
//semmle-extractor-options: --javac-args --release 21
|
||||
88
java/ql/test/library-tests/prettyprint/pp.expected
Normal file
88
java/ql/test/library-tests/prettyprint/pp.expected
Normal file
@@ -0,0 +1,88 @@
|
||||
| Test.java:1:14:1:17 | Test | 0 | public class Test { |
|
||||
| Test.java:1:14:1:17 | Test | 1 | public Test() { |
|
||||
| Test.java:1:14:1:17 | Test | 2 | super(); |
|
||||
| Test.java:1:14:1:17 | Test | 3 | } |
|
||||
| Test.java:1:14:1:17 | Test | 4 | |
|
||||
| Test.java:1:14:1:17 | Test | 5 | static final class S { |
|
||||
| Test.java:1:14:1:17 | Test | 6 | public final boolean equals(Object p0) { <missing body> } |
|
||||
| Test.java:1:14:1:17 | Test | 7 | |
|
||||
| Test.java:1:14:1:17 | Test | 8 | public final int hashCode() { <missing body> } |
|
||||
| Test.java:1:14:1:17 | Test | 9 | |
|
||||
| Test.java:1:14:1:17 | Test | 10 | public final String toString() { <missing body> } |
|
||||
| Test.java:1:14:1:17 | Test | 11 | |
|
||||
| Test.java:1:14:1:17 | Test | 12 | public int x() { <missing body> } |
|
||||
| Test.java:1:14:1:17 | Test | 13 | |
|
||||
| Test.java:1:14:1:17 | Test | 14 | S(int x) { |
|
||||
| Test.java:1:14:1:17 | Test | 15 | super(); |
|
||||
| Test.java:1:14:1:17 | Test | 16 | this.x = x; |
|
||||
| Test.java:1:14:1:17 | Test | 17 | } |
|
||||
| Test.java:1:14:1:17 | Test | 18 | |
|
||||
| Test.java:1:14:1:17 | Test | 19 | private final int x; |
|
||||
| Test.java:1:14:1:17 | Test | 20 | } |
|
||||
| Test.java:1:14:1:17 | Test | 21 | |
|
||||
| Test.java:1:14:1:17 | Test | 22 | static final class R { |
|
||||
| Test.java:1:14:1:17 | Test | 23 | public final boolean equals(Object p0) { <missing body> } |
|
||||
| Test.java:1:14:1:17 | Test | 24 | |
|
||||
| Test.java:1:14:1:17 | Test | 25 | public final int hashCode() { <missing body> } |
|
||||
| Test.java:1:14:1:17 | Test | 26 | |
|
||||
| Test.java:1:14:1:17 | Test | 27 | public S s() { <missing body> } |
|
||||
| Test.java:1:14:1:17 | Test | 28 | |
|
||||
| Test.java:1:14:1:17 | Test | 29 | public final String toString() { <missing body> } |
|
||||
| Test.java:1:14:1:17 | Test | 30 | |
|
||||
| Test.java:1:14:1:17 | Test | 31 | public String y() { <missing body> } |
|
||||
| Test.java:1:14:1:17 | Test | 32 | |
|
||||
| Test.java:1:14:1:17 | Test | 33 | R(S s, String y) { |
|
||||
| Test.java:1:14:1:17 | Test | 34 | super(); |
|
||||
| Test.java:1:14:1:17 | Test | 35 | this.s = s; |
|
||||
| Test.java:1:14:1:17 | Test | 36 | this.y = y; |
|
||||
| Test.java:1:14:1:17 | Test | 37 | } |
|
||||
| Test.java:1:14:1:17 | Test | 38 | |
|
||||
| Test.java:1:14:1:17 | Test | 39 | private final S s; |
|
||||
| Test.java:1:14:1:17 | Test | 40 | |
|
||||
| Test.java:1:14:1:17 | Test | 41 | private final String y; |
|
||||
| Test.java:1:14:1:17 | Test | 42 | } |
|
||||
| Test.java:1:14:1:17 | Test | 43 | |
|
||||
| Test.java:1:14:1:17 | Test | 44 | public static void test(Object o) { |
|
||||
| Test.java:1:14:1:17 | Test | 45 | switch (o) { |
|
||||
| Test.java:1:14:1:17 | Test | 46 | case String s: |
|
||||
| Test.java:1:14:1:17 | Test | 47 | break; |
|
||||
| Test.java:1:14:1:17 | Test | 48 | case R(S(int x), String y): |
|
||||
| Test.java:1:14:1:17 | Test | 49 | break; |
|
||||
| Test.java:1:14:1:17 | Test | 50 | default: |
|
||||
| Test.java:1:14:1:17 | Test | 51 | break; |
|
||||
| Test.java:1:14:1:17 | Test | 52 | } |
|
||||
| Test.java:1:14:1:17 | Test | 53 | switch (o) { |
|
||||
| Test.java:1:14:1:17 | Test | 54 | case String s -> { |
|
||||
| Test.java:1:14:1:17 | Test | 55 | } |
|
||||
| Test.java:1:14:1:17 | Test | 56 | case R(S(int x), String y) -> { |
|
||||
| Test.java:1:14:1:17 | Test | 57 | } |
|
||||
| Test.java:1:14:1:17 | Test | 58 | case null, default -> { |
|
||||
| Test.java:1:14:1:17 | Test | 59 | } |
|
||||
| Test.java:1:14:1:17 | Test | 60 | } |
|
||||
| Test.java:1:14:1:17 | Test | 61 | var a = switch (o) { |
|
||||
| Test.java:1:14:1:17 | Test | 62 | case String s: |
|
||||
| Test.java:1:14:1:17 | Test | 63 | yield 1; |
|
||||
| Test.java:1:14:1:17 | Test | 64 | case R(S(int x), String y): |
|
||||
| Test.java:1:14:1:17 | Test | 65 | yield x; |
|
||||
| Test.java:1:14:1:17 | Test | 66 | case null, default: |
|
||||
| Test.java:1:14:1:17 | Test | 67 | yield 2; |
|
||||
| Test.java:1:14:1:17 | Test | 68 | }; |
|
||||
| Test.java:1:14:1:17 | Test | 69 | var b = switch (o) { |
|
||||
| Test.java:1:14:1:17 | Test | 70 | case String s -> 1; |
|
||||
| Test.java:1:14:1:17 | Test | 71 | case R(S(int x), String y) -> x; |
|
||||
| Test.java:1:14:1:17 | Test | 72 | default -> 2; |
|
||||
| Test.java:1:14:1:17 | Test | 73 | }; |
|
||||
| Test.java:1:14:1:17 | Test | 74 | if (o instanceof String s) { |
|
||||
| Test.java:1:14:1:17 | Test | 75 | } |
|
||||
| Test.java:1:14:1:17 | Test | 76 | if (o instanceof R(S(int x), String y)) { |
|
||||
| Test.java:1:14:1:17 | Test | 77 | } |
|
||||
| Test.java:1:14:1:17 | Test | 78 | switch (o) { |
|
||||
| Test.java:1:14:1:17 | Test | 79 | case R(S(var x), var y) -> { |
|
||||
| Test.java:1:14:1:17 | Test | 80 | } |
|
||||
| Test.java:1:14:1:17 | Test | 81 | case null, default -> { |
|
||||
| Test.java:1:14:1:17 | Test | 82 | } |
|
||||
| Test.java:1:14:1:17 | Test | 83 | } |
|
||||
| Test.java:1:14:1:17 | Test | 84 | if (o instanceof R(S(var x), var y)) { |
|
||||
| Test.java:1:14:1:17 | Test | 85 | } |
|
||||
| Test.java:1:14:1:17 | Test | 86 | } |
|
||||
| Test.java:1:14:1:17 | Test | 87 | } |
|
||||
5
java/ql/test/library-tests/prettyprint/pp.ql
Normal file
5
java/ql/test/library-tests/prettyprint/pp.ql
Normal file
@@ -0,0 +1,5 @@
|
||||
import semmle.code.java.PrettyPrintAst
|
||||
|
||||
from ClassOrInterface cori, string s, int line
|
||||
where pp(cori, s, line) and cori.fromSource()
|
||||
select cori, line, s order by line
|
||||
@@ -50,6 +50,61 @@ class A {
|
||||
if (thing instanceof String s) {
|
||||
throw new RuntimeException(s);
|
||||
}
|
||||
switch (thing) {
|
||||
case String s -> System.out.println(s);
|
||||
case Integer i -> System.out.println("An integer: " + i);
|
||||
default -> { }
|
||||
}
|
||||
switch (thing) {
|
||||
case String s:
|
||||
System.out.println(s);
|
||||
break;
|
||||
case Integer i:
|
||||
System.out.println("An integer:" + i);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
var thingAsString = switch(thing) {
|
||||
case String s -> s;
|
||||
case Integer i -> "An integer: " + i;
|
||||
default -> "Something else";
|
||||
};
|
||||
var thingAsString2 = switch(thing) {
|
||||
case String s:
|
||||
yield s;
|
||||
case Integer i:
|
||||
yield "An integer: " + i;
|
||||
default:
|
||||
yield "Something else";
|
||||
};
|
||||
var nullTest = switch(thing) {
|
||||
case null -> "Null";
|
||||
default -> "Not null";
|
||||
};
|
||||
var whenTest = switch((String)thing) {
|
||||
case "constant" -> "It's constant";
|
||||
case String s when s.length() == 3 -> "It's 3 letters long";
|
||||
case String s when s.length() == 5 -> "it's 5 letters long";
|
||||
default -> "It's something else";
|
||||
};
|
||||
var nullDefaultTest = switch(thing) {
|
||||
case String s -> "It's a string";
|
||||
case null, default -> "It's something else";
|
||||
};
|
||||
var qualifiedEnumTest = switch(thing) {
|
||||
case E.A -> "It's E.A";
|
||||
default -> "It's something else";
|
||||
};
|
||||
var unnecessaryQualifiedEnumTest = switch((E)thing) {
|
||||
case A -> "It's E.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) {
|
||||
@@ -70,4 +125,7 @@ class A {
|
||||
* Javadoc for fields
|
||||
*/
|
||||
int i, j, k;
|
||||
}
|
||||
}
|
||||
|
||||
record Inner(String field) { }
|
||||
record Middle(Inner inner) { }
|
||||
|
||||
@@ -110,45 +110,225 @@ A.java:
|
||||
# 48| 0: [ReturnStmt] return ...
|
||||
# 50| 1: [IfStmt] if (...)
|
||||
# 50| 0: [InstanceOfExpr] ...instanceof...
|
||||
#-----| 0: (Single Local Variable Declaration)
|
||||
# 50| 0: [VarAccess] thing
|
||||
#-----| 2: (Single Local Variable Declaration)
|
||||
# 50| 0: [TypeAccess] String
|
||||
# 50| 1: [LocalVariableDeclExpr] s
|
||||
# 50| 0: [VarAccess] thing
|
||||
# 50| 1: [BlockStmt] { ... }
|
||||
# 51| 0: [ThrowStmt] throw ...
|
||||
# 51| 0: [ClassInstanceExpr] new RuntimeException(...)
|
||||
# 51| -3: [TypeAccess] RuntimeException
|
||||
# 51| 0: [VarAccess] s
|
||||
# 55| 0: [CatchClause] catch (...)
|
||||
# 53| 2: [SwitchStmt] switch (...)
|
||||
# 53| -1: [VarAccess] thing
|
||||
# 54| 0: [PatternCase] case <Pattern>
|
||||
# 54| -1: [ExprStmt] <Expr>;
|
||||
# 54| 0: [MethodCall] println(...)
|
||||
# 54| -1: [VarAccess] System.out
|
||||
# 54| -1: [TypeAccess] System
|
||||
# 54| 0: [VarAccess] s
|
||||
#-----| 0: (Single Local Variable Declaration)
|
||||
# 54| 0: [TypeAccess] String
|
||||
# 54| 1: [LocalVariableDeclExpr] s
|
||||
# 55| 1: [PatternCase] case <Pattern>
|
||||
# 55| -1: [ExprStmt] <Expr>;
|
||||
# 55| 0: [MethodCall] println(...)
|
||||
# 55| -1: [VarAccess] System.out
|
||||
# 55| -1: [TypeAccess] System
|
||||
# 55| 0: [AddExpr] ... + ...
|
||||
# 55| 0: [StringLiteral] "An integer: "
|
||||
# 55| 1: [VarAccess] i
|
||||
#-----| 0: (Single Local Variable Declaration)
|
||||
# 55| 0: [TypeAccess] Integer
|
||||
# 55| 1: [LocalVariableDeclExpr] i
|
||||
# 56| 2: [DefaultCase] default
|
||||
# 56| -1: [BlockStmt] { ... }
|
||||
# 58| 3: [SwitchStmt] switch (...)
|
||||
# 58| -1: [VarAccess] thing
|
||||
# 59| 0: [PatternCase] case <Pattern>
|
||||
#-----| 0: (Single Local Variable Declaration)
|
||||
# 59| 0: [TypeAccess] String
|
||||
# 59| 1: [LocalVariableDeclExpr] s
|
||||
# 60| 1: [ExprStmt] <Expr>;
|
||||
# 60| 0: [MethodCall] println(...)
|
||||
# 60| -1: [VarAccess] System.out
|
||||
# 60| -1: [TypeAccess] System
|
||||
# 60| 0: [VarAccess] s
|
||||
# 61| 2: [BreakStmt] break
|
||||
# 62| 3: [PatternCase] case <Pattern>
|
||||
#-----| 0: (Single Local Variable Declaration)
|
||||
# 62| 0: [TypeAccess] Integer
|
||||
# 62| 1: [LocalVariableDeclExpr] i
|
||||
# 63| 4: [ExprStmt] <Expr>;
|
||||
# 63| 0: [MethodCall] println(...)
|
||||
# 63| -1: [VarAccess] System.out
|
||||
# 63| -1: [TypeAccess] System
|
||||
# 63| 0: [AddExpr] ... + ...
|
||||
# 63| 0: [StringLiteral] "An integer:"
|
||||
# 63| 1: [VarAccess] i
|
||||
# 64| 5: [BreakStmt] break
|
||||
# 65| 6: [DefaultCase] default
|
||||
# 66| 7: [BreakStmt] break
|
||||
# 68| 4: [LocalVariableDeclStmt] var ...;
|
||||
# 68| 1: [LocalVariableDeclExpr] thingAsString
|
||||
# 68| 0: [SwitchExpr] switch (...)
|
||||
# 68| -1: [VarAccess] thing
|
||||
# 69| 0: [PatternCase] case <Pattern>
|
||||
# 69| -1: [VarAccess] s
|
||||
#-----| 0: (Single Local Variable Declaration)
|
||||
# 69| 0: [TypeAccess] String
|
||||
# 69| 1: [LocalVariableDeclExpr] s
|
||||
# 70| 1: [PatternCase] case <Pattern>
|
||||
# 70| -1: [AddExpr] ... + ...
|
||||
# 70| 0: [StringLiteral] "An integer: "
|
||||
# 70| 1: [VarAccess] i
|
||||
#-----| 0: (Single Local Variable Declaration)
|
||||
# 70| 0: [TypeAccess] Integer
|
||||
# 70| 1: [LocalVariableDeclExpr] i
|
||||
# 71| 2: [DefaultCase] default
|
||||
# 71| -1: [StringLiteral] "Something else"
|
||||
# 73| 5: [LocalVariableDeclStmt] var ...;
|
||||
# 73| 1: [LocalVariableDeclExpr] thingAsString2
|
||||
# 73| 0: [SwitchExpr] switch (...)
|
||||
# 73| -1: [VarAccess] thing
|
||||
# 74| 0: [PatternCase] case <Pattern>
|
||||
#-----| 0: (Single Local Variable Declaration)
|
||||
# 74| 0: [TypeAccess] String
|
||||
# 74| 1: [LocalVariableDeclExpr] s
|
||||
# 75| 1: [YieldStmt] yield ...
|
||||
# 75| 0: [VarAccess] s
|
||||
# 76| 2: [PatternCase] case <Pattern>
|
||||
#-----| 0: (Single Local Variable Declaration)
|
||||
# 76| 0: [TypeAccess] Integer
|
||||
# 76| 1: [LocalVariableDeclExpr] i
|
||||
# 77| 3: [YieldStmt] yield ...
|
||||
# 77| 0: [AddExpr] ... + ...
|
||||
# 77| 0: [StringLiteral] "An integer: "
|
||||
# 77| 1: [VarAccess] i
|
||||
# 78| 4: [DefaultCase] default
|
||||
# 79| 5: [YieldStmt] yield ...
|
||||
# 79| 0: [StringLiteral] "Something else"
|
||||
# 81| 6: [LocalVariableDeclStmt] var ...;
|
||||
# 81| 1: [LocalVariableDeclExpr] nullTest
|
||||
# 81| 0: [SwitchExpr] switch (...)
|
||||
# 81| -1: [VarAccess] thing
|
||||
# 82| 0: [ConstCase] case ...
|
||||
# 82| -1: [StringLiteral] "Null"
|
||||
# 82| 0: [NullLiteral] null
|
||||
# 83| 1: [DefaultCase] default
|
||||
# 83| -1: [StringLiteral] "Not null"
|
||||
# 85| 7: [LocalVariableDeclStmt] var ...;
|
||||
# 85| 1: [LocalVariableDeclExpr] whenTest
|
||||
# 85| 0: [SwitchExpr] switch (...)
|
||||
# 85| -1: [CastExpr] (...)...
|
||||
# 85| 0: [TypeAccess] String
|
||||
# 85| 1: [VarAccess] thing
|
||||
# 86| 0: [ConstCase] case ...
|
||||
# 86| -1: [StringLiteral] "It's constant"
|
||||
# 86| 0: [StringLiteral] "constant"
|
||||
# 87| 1: [PatternCase] case <Pattern>
|
||||
# 87| -3: [EQExpr] ... == ...
|
||||
# 87| 0: [MethodCall] length(...)
|
||||
# 87| -1: [VarAccess] s
|
||||
# 87| 1: [IntegerLiteral] 3
|
||||
# 87| -1: [StringLiteral] "It's 3 letters long"
|
||||
#-----| 0: (Single Local Variable Declaration)
|
||||
# 87| 0: [TypeAccess] String
|
||||
# 87| 1: [LocalVariableDeclExpr] s
|
||||
# 88| 2: [PatternCase] case <Pattern>
|
||||
# 88| -3: [EQExpr] ... == ...
|
||||
# 88| 0: [MethodCall] length(...)
|
||||
# 88| -1: [VarAccess] s
|
||||
# 88| 1: [IntegerLiteral] 5
|
||||
# 88| -1: [StringLiteral] "it's 5 letters long"
|
||||
#-----| 0: (Single Local Variable Declaration)
|
||||
# 88| 0: [TypeAccess] String
|
||||
# 88| 1: [LocalVariableDeclExpr] s
|
||||
# 89| 3: [DefaultCase] default
|
||||
# 89| -1: [StringLiteral] "It's something else"
|
||||
# 91| 8: [LocalVariableDeclStmt] var ...;
|
||||
# 91| 1: [LocalVariableDeclExpr] nullDefaultTest
|
||||
# 91| 0: [SwitchExpr] switch (...)
|
||||
# 91| -1: [VarAccess] thing
|
||||
# 92| 0: [PatternCase] case <Pattern>
|
||||
# 92| -1: [StringLiteral] "It's a string"
|
||||
#-----| 0: (Single Local Variable Declaration)
|
||||
# 92| 0: [TypeAccess] String
|
||||
# 92| 1: [LocalVariableDeclExpr] s
|
||||
# 93| 1: [NullDefaultCase] case null, default
|
||||
# 93| -1: [StringLiteral] "It's something else"
|
||||
# 93| 0: [NullLiteral] null
|
||||
# 95| 9: [LocalVariableDeclStmt] var ...;
|
||||
# 95| 1: [LocalVariableDeclExpr] qualifiedEnumTest
|
||||
# 95| 0: [SwitchExpr] switch (...)
|
||||
# 95| -1: [VarAccess] thing
|
||||
# 96| 0: [ConstCase] case ...
|
||||
# 96| -1: [StringLiteral] "It's E.A"
|
||||
# 96| 0: [VarAccess] E.A
|
||||
# 96| -1: [TypeAccess] E
|
||||
# 97| 1: [DefaultCase] default
|
||||
# 97| -1: [StringLiteral] "It's something else"
|
||||
# 99| 10: [LocalVariableDeclStmt] var ...;
|
||||
# 99| 1: [LocalVariableDeclExpr] unnecessaryQualifiedEnumTest
|
||||
# 99| 0: [SwitchExpr] switch (...)
|
||||
# 99| -1: [CastExpr] (...)...
|
||||
# 99| 0: [TypeAccess] E
|
||||
# 99| 1: [VarAccess] thing
|
||||
# 100| 0: [ConstCase] case ...
|
||||
# 100| -1: [StringLiteral] "It's E.A"
|
||||
# 100| 0: [VarAccess] A
|
||||
# 101| 1: [ConstCase] case ...
|
||||
# 101| -1: [StringLiteral] "It's E.B"
|
||||
# 101| 0: [VarAccess] E.B
|
||||
# 101| -1: [TypeAccess] E
|
||||
# 102| 2: [DefaultCase] default
|
||||
# 102| -1: [StringLiteral] "It's something else"
|
||||
# 104| 11: [LocalVariableDeclStmt] var ...;
|
||||
# 104| 1: [LocalVariableDeclExpr] recordPatterntest
|
||||
# 104| 0: [SwitchExpr] switch (...)
|
||||
# 104| -1: [VarAccess] thing
|
||||
# 105| 0: [PatternCase] case <Pattern>
|
||||
# 105| -1: [VarAccess] field
|
||||
# 105| 0: [RecordPatternExpr] Middle(...)
|
||||
# 105| 0: [RecordPatternExpr] Inner(...)
|
||||
# 105| -1: [TypeAccess] String
|
||||
# 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)
|
||||
# 55| 0: [TypeAccess] RuntimeException
|
||||
# 55| 1: [LocalVariableDeclExpr] rte
|
||||
# 55| 1: [BlockStmt] { ... }
|
||||
# 56| 0: [ReturnStmt] return ...
|
||||
# 60| 10: [Class] E
|
||||
# 64| 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)
|
||||
# 61| 1: [Javadoc] /** Javadoc for enum constant */
|
||||
# 62| 0: [JavadocText] Javadoc for enum constant
|
||||
# 64| -1: [TypeAccess] E
|
||||
# 64| 0: [ClassInstanceExpr] new E(...)
|
||||
# 64| -3: [TypeAccess] E
|
||||
# 65| 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)
|
||||
# 61| 1: [Javadoc] /** Javadoc for enum constant */
|
||||
# 62| 0: [JavadocText] Javadoc for enum constant
|
||||
# 65| -1: [TypeAccess] E
|
||||
# 65| 0: [ClassInstanceExpr] new E(...)
|
||||
# 65| -3: [TypeAccess] E
|
||||
# 66| 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)
|
||||
# 61| 1: [Javadoc] /** Javadoc for enum constant */
|
||||
# 62| 0: [JavadocText] Javadoc for enum constant
|
||||
# 66| -1: [TypeAccess] E
|
||||
# 66| 0: [ClassInstanceExpr] new E(...)
|
||||
# 66| -3: [TypeAccess] E
|
||||
# 72| 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)
|
||||
# 69| 1: [Javadoc] /** Javadoc for fields */
|
||||
# 70| 0: [JavadocText] Javadoc for fields
|
||||
# 72| -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;
|
||||
|
||||
@@ -1 +1 @@
|
||||
//semmle-extractor-options: --javac-args -source 17 -target 17
|
||||
//semmle-extractor-options: --javac-args --release 21
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
public class Test {
|
||||
|
||||
public static int source() { return 0; }
|
||||
public static void sink(int x) { }
|
||||
|
||||
interface I { void take(int x); }
|
||||
static class C1 implements I { public void take(int x) { sink(x); } }
|
||||
static class C2 implements I { public void take(int x) { sink(x); } }
|
||||
record Wrapper(Object o) implements I { public void take(int x) { sink(x); } }
|
||||
record WrapperWrapper(Wrapper w) implements I { public void take(int x) { sink(x); } }
|
||||
|
||||
public static void test(int unknown, int alsoUnknown) {
|
||||
|
||||
I i = unknown == 0 ? new C1() : unknown == 1 ? new C2() : unknown == 2 ? new Wrapper(new Object()) : new WrapperWrapper(new Wrapper(new Object()));
|
||||
|
||||
switch(i) {
|
||||
case C1 c1 when alsoUnknown == 1 -> { }
|
||||
default -> i.take(source()); // Could call any implementation
|
||||
}
|
||||
|
||||
switch(i) {
|
||||
case C1 c1 -> { }
|
||||
default -> i.take(source()); // Can't call C1.take
|
||||
}
|
||||
|
||||
switch(i) {
|
||||
case C1 c1 -> { }
|
||||
case null, default -> i.take(source()); // Can't call C1.take (but we don't currently notice)
|
||||
}
|
||||
|
||||
switch(i) {
|
||||
case Wrapper w -> { }
|
||||
default -> i.take(source()); // Can't call Wrapper.take
|
||||
}
|
||||
|
||||
switch(i) {
|
||||
case Wrapper(Object o) -> { }
|
||||
default -> i.take(source()); // Can't call Wrapper.take
|
||||
}
|
||||
|
||||
switch(i) {
|
||||
case Wrapper(String s) -> { }
|
||||
default -> i.take(source()); // Could call any implementation, because this might be a Wrapper(Integer) for example.
|
||||
}
|
||||
|
||||
switch(i) {
|
||||
case WrapperWrapper(Wrapper(Object o)) -> { }
|
||||
default -> i.take(source()); // Can't call WrapperWrapper.take
|
||||
}
|
||||
|
||||
switch(i) {
|
||||
case WrapperWrapper(Wrapper(String s)) -> { }
|
||||
default -> i.take(source()); // Could call any implementation, because this might be a WrapperWrapper(Wrapper((Integer)) for example.
|
||||
}
|
||||
|
||||
switch(i) {
|
||||
case C1 c1: break;
|
||||
case null: default: i.take(source()); // Can't call C1.take (but we don't currently notice)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
//semmle-extractor-options: --javac-args --release 21
|
||||
@@ -0,0 +1,32 @@
|
||||
| Test.java:18:25:18:32 | source(...) | Test.java:7:65:7:65 | x |
|
||||
| Test.java:18:25:18:32 | source(...) | Test.java:8:65:8:65 | x |
|
||||
| Test.java:18:25:18:32 | source(...) | Test.java:9:74:9:74 | x |
|
||||
| Test.java:18:25:18:32 | source(...) | Test.java:10:82:10:82 | x |
|
||||
| Test.java:23:25:23:32 | source(...) | Test.java:8:65:8:65 | x |
|
||||
| Test.java:23:25:23:32 | source(...) | Test.java:9:74:9:74 | x |
|
||||
| Test.java:23:25:23:32 | source(...) | Test.java:10:82:10:82 | x |
|
||||
| Test.java:28:36:28:43 | source(...) | Test.java:7:65:7:65 | x |
|
||||
| Test.java:28:36:28:43 | source(...) | Test.java:8:65:8:65 | x |
|
||||
| Test.java:28:36:28:43 | source(...) | Test.java:9:74:9:74 | x |
|
||||
| Test.java:28:36:28:43 | source(...) | Test.java:10:82:10:82 | x |
|
||||
| Test.java:33:25:33:32 | source(...) | Test.java:7:65:7:65 | x |
|
||||
| Test.java:33:25:33:32 | source(...) | Test.java:8:65:8:65 | x |
|
||||
| Test.java:33:25:33:32 | source(...) | Test.java:10:82:10:82 | x |
|
||||
| Test.java:38:25:38:32 | source(...) | Test.java:7:65:7:65 | x |
|
||||
| Test.java:38:25:38:32 | source(...) | Test.java:8:65:8:65 | x |
|
||||
| Test.java:38:25:38:32 | source(...) | Test.java:10:82:10:82 | x |
|
||||
| Test.java:43:25:43:32 | source(...) | Test.java:7:65:7:65 | x |
|
||||
| Test.java:43:25:43:32 | source(...) | Test.java:8:65:8:65 | x |
|
||||
| Test.java:43:25:43:32 | source(...) | Test.java:9:74:9:74 | x |
|
||||
| Test.java:43:25:43:32 | source(...) | Test.java:10:82:10:82 | x |
|
||||
| Test.java:48:25:48:32 | source(...) | Test.java:7:65:7:65 | x |
|
||||
| Test.java:48:25:48:32 | source(...) | Test.java:8:65:8:65 | x |
|
||||
| Test.java:48:25:48:32 | source(...) | Test.java:9:74:9:74 | x |
|
||||
| Test.java:53:25:53:32 | source(...) | Test.java:7:65:7:65 | x |
|
||||
| Test.java:53:25:53:32 | source(...) | Test.java:8:65:8:65 | x |
|
||||
| Test.java:53:25:53:32 | source(...) | Test.java:9:74:9:74 | x |
|
||||
| Test.java:53:25:53:32 | source(...) | Test.java:10:82:10:82 | x |
|
||||
| Test.java:58:34:58:41 | source(...) | Test.java:7:65:7:65 | x |
|
||||
| Test.java:58:34:58:41 | source(...) | Test.java:8:65:8:65 | x |
|
||||
| Test.java:58:34:58:41 | source(...) | Test.java:9:74:9:74 | x |
|
||||
| Test.java:58:34:58:41 | source(...) | Test.java:10:82:10:82 | x |
|
||||
@@ -0,0 +1,18 @@
|
||||
import java
|
||||
import semmle.code.java.dataflow.DataFlow
|
||||
|
||||
module TestConfig implements DataFlow::ConfigSig {
|
||||
predicate isSource(DataFlow::Node source) {
|
||||
source.asExpr() = any(MethodCall mc | mc.getMethod().getName() = "source")
|
||||
}
|
||||
|
||||
predicate isSink(DataFlow::Node sink) {
|
||||
sink.asExpr() = any(MethodCall mc | mc.getMethod().getName() = "sink").getAnArgument()
|
||||
}
|
||||
}
|
||||
|
||||
module Flow = DataFlow::Global<TestConfig>;
|
||||
|
||||
from DataFlow::Node source, DataFlow::Node sink
|
||||
where Flow::flow(source, sink)
|
||||
select source, sink
|
||||
@@ -92,4 +92,30 @@ public class A extends ArrayList<Long> {
|
||||
Object r = n;
|
||||
}
|
||||
}
|
||||
|
||||
public void m9(Object[] xs, int i) {
|
||||
switch (xs[i]) {
|
||||
case Integer i2 -> {
|
||||
Object n = xs[i];
|
||||
Object r = n;
|
||||
}
|
||||
default -> { }
|
||||
}
|
||||
}
|
||||
|
||||
public void m10(Object o) {
|
||||
String s = "Hello world!";
|
||||
Object o2 = s; // Alas, the type information, it is lost
|
||||
|
||||
if (o2 instanceof CharSequence cs) {
|
||||
// Partially recovered statically, but we should know cs is an alias of o and therefore it's really a string.
|
||||
Object target = cs;
|
||||
}
|
||||
|
||||
// The same applies to a pattern case
|
||||
switch (o2) {
|
||||
case CharSequence cs -> { Object target = cs; }
|
||||
default -> { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,6 +44,10 @@ public class UnionTypes {
|
||||
if (x instanceof Inter) {
|
||||
x.hashCode();
|
||||
}
|
||||
var hashCode = switch (x) {
|
||||
case Inter i -> x.hashCode();
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
void m3(Object d) {
|
||||
|
||||
1
java/ql/test/library-tests/typeflow/options
Normal file
1
java/ql/test/library-tests/typeflow/options
Normal file
@@ -0,0 +1 @@
|
||||
//semmle-extractor-options: --javac-args --release 21
|
||||
@@ -13,4 +13,10 @@
|
||||
| A.java:67:22:67:22 | x | Integer | false |
|
||||
| A.java:70:23:70:24 | x2 | Integer | false |
|
||||
| A.java:92:18:92:18 | n | Integer | false |
|
||||
| A.java:100:20:100:20 | n | Integer | false |
|
||||
| A.java:110:9:110:10 | o2 | String | false |
|
||||
| A.java:112:23:112:24 | cs | String | false |
|
||||
| A.java:116:13:116:14 | o2 | String | false |
|
||||
| A.java:117:49:117:50 | cs | String | false |
|
||||
| UnionTypes.java:45:7:45:7 | x | Inter | false |
|
||||
| UnionTypes.java:48:23:48:23 | x | Inter | false |
|
||||
|
||||
@@ -19,6 +19,11 @@
|
||||
| UnionTypes.java:44:9:44:9 | x | 3 | A3 | false |
|
||||
| UnionTypes.java:45:7:45:7 | x | 2 | A1 | false |
|
||||
| UnionTypes.java:45:7:45:7 | x | 2 | A2 | true |
|
||||
| UnionTypes.java:51:7:51:7 | d | 3 | A1 | false |
|
||||
| UnionTypes.java:51:7:51:7 | d | 3 | A2 | false |
|
||||
| UnionTypes.java:51:7:51:7 | d | 3 | A3 | false |
|
||||
| UnionTypes.java:47:28:47:28 | x | 3 | A1 | false |
|
||||
| UnionTypes.java:47:28:47:28 | x | 3 | A2 | true |
|
||||
| UnionTypes.java:47:28:47:28 | x | 3 | A3 | false |
|
||||
| UnionTypes.java:48:23:48:23 | x | 2 | A1 | false |
|
||||
| UnionTypes.java:48:23:48:23 | x | 2 | A2 | true |
|
||||
| UnionTypes.java:55:7:55:7 | d | 3 | A1 | false |
|
||||
| UnionTypes.java:55:7:55:7 | d | 3 | A2 | false |
|
||||
| UnionTypes.java:55:7:55:7 | d | 3 | A3 | false |
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
public class Test {
|
||||
|
||||
private interface Intf { String get(); }
|
||||
private static class Specific implements Intf { public String get() { return "Specific"; } }
|
||||
private static class Alternative implements Intf { public String get() { return "Alternative"; } }
|
||||
|
||||
public static String caller() {
|
||||
|
||||
Alternative a = new Alternative(); // Instantiate this somewhere so there are at least two candidate types in general
|
||||
return test(new Specific());
|
||||
|
||||
}
|
||||
|
||||
public static String test(Object o) {
|
||||
|
||||
if (o instanceof Intf i) {
|
||||
// So we should know i.get is really Specific.get():
|
||||
return i.get();
|
||||
}
|
||||
|
||||
switch (o) {
|
||||
case Intf i -> { return i.get(); } // Same goes for this `i`
|
||||
default -> { return "Not an Intf"; }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
//semmle-extractor-options: --javac-args --release 21
|
||||
@@ -0,0 +1,8 @@
|
||||
| Test.java:1:14:1:17 | super(...) | java.lang.Object.Object |
|
||||
| Test.java:4:24:4:31 | super(...) | java.lang.Object.Object |
|
||||
| Test.java:5:24:5:34 | super(...) | java.lang.Object.Object |
|
||||
| Test.java:9:21:9:37 | new Alternative(...) | Test$Alternative.Alternative |
|
||||
| Test.java:10:12:10:31 | test(...) | Test.test |
|
||||
| Test.java:10:17:10:30 | new Specific(...) | Test$Specific.Specific |
|
||||
| Test.java:18:14:18:20 | get(...) | Test$Specific.get |
|
||||
| Test.java:22:31:22:37 | get(...) | Test$Specific.get |
|
||||
@@ -0,0 +1,6 @@
|
||||
import java
|
||||
import semmle.code.java.dispatch.VirtualDispatch
|
||||
|
||||
from Call c, Callable c2
|
||||
where c2 = viableCallable(c)
|
||||
select c, c2.getQualifiedName()
|
||||
27
java/ql/test/query-tests/Nullness/G.java
Normal file
27
java/ql/test/query-tests/Nullness/G.java
Normal file
@@ -0,0 +1,27 @@
|
||||
public class G {
|
||||
|
||||
public static void test(String s) {
|
||||
|
||||
if (s == null) {
|
||||
System.out.println("Is null");
|
||||
}
|
||||
|
||||
switch(s) { // OK; null case means this doesn't throw.
|
||||
case null -> System.out.println("Null");
|
||||
case "foo" -> System.out.println("Foo");
|
||||
default -> System.out.println("Something else");
|
||||
}
|
||||
|
||||
var x = switch(s) { // OK; null case (combined with default) means this doesn't throw.
|
||||
case "foo" -> "foo";
|
||||
case null, default -> "bar";
|
||||
};
|
||||
|
||||
switch(s) { // BAD; lack of a null case means this may throw.
|
||||
case "foo" -> System.out.println("Foo");
|
||||
case String s2 -> System.out.println("Other string of length " + s2.length());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -35,3 +35,4 @@
|
||||
| C.java:233:7:233:8 | xs | Variable $@ may be null at this access because of $@ assignment. | C.java:231:5:231:56 | int[] xs | xs | C.java:231:11:231:55 | xs | this |
|
||||
| F.java:11:5:11:7 | obj | Variable $@ may be null at this access as suggested by $@ null guard. | F.java:8:18:8:27 | obj | obj | F.java:9:9:9:19 | ... == ... | this |
|
||||
| F.java:17:5:17:7 | obj | Variable $@ may be null at this access as suggested by $@ null guard. | F.java:14:18:14:27 | obj | obj | F.java:15:9:15:19 | ... == ... | this |
|
||||
| G.java:20:12:20:12 | s | Variable $@ may be null at this access as suggested by $@ null guard. | G.java:3:27:3:34 | s | s | G.java:5:9:5:17 | ... == ... | this |
|
||||
|
||||
@@ -1 +1 @@
|
||||
//semmle-extractor-options: --javac-args -cp ${testdir}/../../stubs/junit-4.11:${testdir}/../../stubs/hamcrest-2.2:${testdir}/../../stubs/junit-jupiter-api-5.2.0
|
||||
//semmle-extractor-options: --javac-args --release 21 -cp ${testdir}/../../stubs/junit-4.11:${testdir}/../../stubs/hamcrest-2.2:${testdir}/../../stubs/junit-jupiter-api-5.2.0
|
||||
|
||||
@@ -9,3 +9,4 @@
|
||||
| Test.java:98:47:98:60 | queryFromField | Query built by concatenation with $@, which may be untrusted. | Test.java:97:8:97:19 | categoryName | this expression |
|
||||
| Test.java:108:47:108:61 | querySbToString | Query built by concatenation with $@, which may be untrusted. | Test.java:104:19:104:30 | categoryName | this expression |
|
||||
| Test.java:118:47:118:62 | querySb2ToString | Query built by concatenation with $@, which may be untrusted. | Test.java:114:46:114:57 | categoryName | this expression |
|
||||
| Test.java:221:81:221:111 | ... + ... | Query built by concatenation with $@, which may be untrusted. | Test.java:221:95:221:102 | category | this expression |
|
||||
|
||||
@@ -13,10 +13,13 @@ edges
|
||||
| Test.java:60:29:60:35 | querySb : StringBuilder | Test.java:60:29:60:46 | toString(...) : String |
|
||||
| Test.java:60:29:60:46 | toString(...) : String | Test.java:62:47:62:61 | querySbToString |
|
||||
| Test.java:183:33:183:45 | args : String[] | Test.java:209:47:209:68 | queryWithUserTableName |
|
||||
| Test.java:213:26:213:38 | args : String[] | Test.java:214:11:214:14 | args : String[] |
|
||||
| Test.java:213:26:213:38 | args : String[] | Test.java:218:14:218:17 | args : String[] |
|
||||
| Test.java:214:11:214:14 | args : String[] | Test.java:29:30:29:42 | args : String[] |
|
||||
| Test.java:218:14:218:17 | args : String[] | Test.java:183:33:183:45 | args : String[] |
|
||||
| Test.java:213:34:213:46 | args : String[] | Test.java:221:81:221:111 | ... + ... |
|
||||
| Test.java:227:26:227:38 | args : String[] | Test.java:228:11:228:14 | args : String[] |
|
||||
| Test.java:227:26:227:38 | args : String[] | Test.java:232:14:232:17 | args : String[] |
|
||||
| Test.java:227:26:227:38 | args : String[] | Test.java:233:15:233:18 | args : String[] |
|
||||
| Test.java:228:11:228:14 | args : String[] | Test.java:29:30:29:42 | args : String[] |
|
||||
| Test.java:232:14:232:17 | args : String[] | Test.java:183:33:183:45 | args : String[] |
|
||||
| Test.java:233:15:233:18 | args : String[] | Test.java:213:34:213:46 | args : String[] |
|
||||
nodes
|
||||
| Mongo.java:10:29:10:41 | args : String[] | semmle.label | args : String[] |
|
||||
| Mongo.java:17:45:17:67 | parse(...) | semmle.label | parse(...) |
|
||||
@@ -35,17 +38,21 @@ nodes
|
||||
| Test.java:78:46:78:50 | query | semmle.label | query |
|
||||
| Test.java:183:33:183:45 | args : String[] | semmle.label | args : String[] |
|
||||
| Test.java:209:47:209:68 | queryWithUserTableName | semmle.label | queryWithUserTableName |
|
||||
| Test.java:213:26:213:38 | args : String[] | semmle.label | args : String[] |
|
||||
| Test.java:214:11:214:14 | args : String[] | semmle.label | args : String[] |
|
||||
| Test.java:218:14:218:17 | args : String[] | semmle.label | args : String[] |
|
||||
| Test.java:213:34:213:46 | args : String[] | semmle.label | args : String[] |
|
||||
| Test.java:221:81:221:111 | ... + ... | semmle.label | ... + ... |
|
||||
| Test.java:227:26:227:38 | args : String[] | semmle.label | args : String[] |
|
||||
| Test.java:228:11:228:14 | args : String[] | semmle.label | args : String[] |
|
||||
| Test.java:232:14:232:17 | args : String[] | semmle.label | args : String[] |
|
||||
| Test.java:233:15:233:18 | args : String[] | semmle.label | args : String[] |
|
||||
subpaths
|
||||
#select
|
||||
| Mongo.java:17:45:17:67 | parse(...) | Mongo.java:10:29:10:41 | args : String[] | Mongo.java:17:45:17:67 | parse(...) | This query depends on a $@. | Mongo.java:10:29:10:41 | args | user-provided value |
|
||||
| Mongo.java:21:49:21:52 | json | Mongo.java:10:29:10:41 | args : String[] | Mongo.java:21:49:21:52 | json | This query depends on a $@. | Mongo.java:10:29:10:41 | args | user-provided value |
|
||||
| Test.java:36:47:36:52 | query1 | Test.java:213:26:213:38 | args : String[] | Test.java:36:47:36:52 | query1 | This query depends on a $@. | Test.java:213:26:213:38 | args | user-provided value |
|
||||
| Test.java:42:57:42:62 | query2 | Test.java:213:26:213:38 | args : String[] | Test.java:42:57:42:62 | query2 | This query depends on a $@. | Test.java:213:26:213:38 | args | user-provided value |
|
||||
| Test.java:50:62:50:67 | query3 | Test.java:213:26:213:38 | args : String[] | Test.java:50:62:50:67 | query3 | This query depends on a $@. | Test.java:213:26:213:38 | args | user-provided value |
|
||||
| Test.java:62:47:62:61 | querySbToString | Test.java:213:26:213:38 | args : String[] | Test.java:62:47:62:61 | querySbToString | This query depends on a $@. | Test.java:213:26:213:38 | args | user-provided value |
|
||||
| Test.java:70:40:70:44 | query | Test.java:213:26:213:38 | args : String[] | Test.java:70:40:70:44 | query | This query depends on a $@. | Test.java:213:26:213:38 | args | user-provided value |
|
||||
| Test.java:78:46:78:50 | query | Test.java:213:26:213:38 | args : String[] | Test.java:78:46:78:50 | query | This query depends on a $@. | Test.java:213:26:213:38 | args | user-provided value |
|
||||
| Test.java:209:47:209:68 | queryWithUserTableName | Test.java:213:26:213:38 | args : String[] | Test.java:209:47:209:68 | queryWithUserTableName | This query depends on a $@. | Test.java:213:26:213:38 | args | user-provided value |
|
||||
| Test.java:36:47:36:52 | query1 | Test.java:227:26:227:38 | args : String[] | Test.java:36:47:36:52 | query1 | This query depends on a $@. | Test.java:227:26:227:38 | args | user-provided value |
|
||||
| Test.java:42:57:42:62 | query2 | Test.java:227:26:227:38 | args : String[] | Test.java:42:57:42:62 | query2 | This query depends on a $@. | Test.java:227:26:227:38 | args | user-provided value |
|
||||
| Test.java:50:62:50:67 | query3 | Test.java:227:26:227:38 | args : String[] | Test.java:50:62:50:67 | query3 | This query depends on a $@. | Test.java:227:26:227:38 | args | user-provided value |
|
||||
| Test.java:62:47:62:61 | querySbToString | Test.java:227:26:227:38 | args : String[] | Test.java:62:47:62:61 | querySbToString | This query depends on a $@. | Test.java:227:26:227:38 | args | user-provided value |
|
||||
| Test.java:70:40:70:44 | query | Test.java:227:26:227:38 | args : String[] | Test.java:70:40:70:44 | query | This query depends on a $@. | Test.java:227:26:227:38 | args | user-provided value |
|
||||
| Test.java:78:46:78:50 | query | Test.java:227:26:227:38 | args : String[] | Test.java:78:46:78:50 | query | This query depends on a $@. | Test.java:227:26:227:38 | args | user-provided value |
|
||||
| Test.java:209:47:209:68 | queryWithUserTableName | Test.java:227:26:227:38 | args : String[] | Test.java:209:47:209:68 | queryWithUserTableName | This query depends on a $@. | Test.java:227:26:227:38 | args | user-provided value |
|
||||
| Test.java:221:81:221:111 | ... + ... | Test.java:227:26:227:38 | args : String[] | Test.java:221:81:221:111 | ... + ... | This query depends on a $@. | Test.java:227:26:227:38 | args | user-provided value |
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user