mirror of
https://github.com/github/codeql.git
synced 2025-12-24 04:36:35 +01:00
Handle 'case null, default:'
This commit is contained in:
@@ -14,5 +14,5 @@ where
|
||||
switch.getExpr().getType() = enum and
|
||||
missing.getDeclaringType() = enum and
|
||||
not switch.getAConstCase().getValue() = missing.getAnAccess() and
|
||||
not exists(switch.getDefaultCase())
|
||||
not exists(switch.getDefaultOrNullDefaultCase())
|
||||
select switch
|
||||
|
||||
@@ -992,6 +992,10 @@ providesWith(
|
||||
string serviceImpl: string ref
|
||||
);
|
||||
|
||||
isNullDefaultCase(
|
||||
int id: @case ref
|
||||
);
|
||||
|
||||
/*
|
||||
* Javadoc
|
||||
*/
|
||||
|
||||
@@ -1514,7 +1514,8 @@ class SwitchExpr extends Expr, StmtParent, @switchexpr {
|
||||
* 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)
|
||||
result =
|
||||
rank[i + 1](SwitchCase case, int idx | case.isNthChildOf(this, idx) | case order by idx)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1532,6 +1533,9 @@ class SwitchExpr extends Expr, StmtParent, @switchexpr {
|
||||
/** Gets the `default` case of this switch expression, if any. */
|
||||
DefaultCase getDefaultCase() { result = this.getACase() }
|
||||
|
||||
/** Gets the `default` or `case null, default` case of this switch statement, if any. */
|
||||
SwitchCase getDefaultOrNullDefaultCase() { result = this.getACase() and result.hasDefaultLabel() }
|
||||
|
||||
/** Gets the expression of this `switch` expression. */
|
||||
Expr getExpr() { result.getParent() = this }
|
||||
|
||||
@@ -1543,9 +1547,7 @@ class SwitchExpr extends Expr, StmtParent, @switchexpr {
|
||||
}
|
||||
|
||||
/** Holds if this switch has a case handling a null literal. */
|
||||
predicate hasNullCase() {
|
||||
this.getAConstCase().getValue(_) instanceof NullLiteral
|
||||
}
|
||||
predicate hasNullCase() { this.getAConstCase().getValue(_) instanceof NullLiteral }
|
||||
|
||||
/** Gets a printable representation of this expression. */
|
||||
override string toString() { result = "switch (...)" }
|
||||
@@ -1638,19 +1640,13 @@ class LocalVariableDeclExpr extends Expr, @localvariabledeclexpr {
|
||||
string getName() { result = this.getVariable().getName() }
|
||||
|
||||
/** Gets the switch statement or expression whose pattern declares this identifier, if any. */
|
||||
StmtParent getAssociatedSwitch() {
|
||||
result = this.getParent().(PatternCase).getParent()
|
||||
}
|
||||
StmtParent getAssociatedSwitch() { result = this.getParent().(PatternCase).getParent() }
|
||||
|
||||
/** Holds if this is a declaration stemming from a pattern switch case. */
|
||||
predicate hasAssociatedSwitch() {
|
||||
exists(this.getAssociatedSwitch())
|
||||
}
|
||||
predicate hasAssociatedSwitch() { exists(this.getAssociatedSwitch()) }
|
||||
|
||||
/** Gets the initializer expression of this local variable declaration expression, if any. */
|
||||
Expr getInit() {
|
||||
result.isNthChildOf(this, 0)
|
||||
}
|
||||
Expr getInit() { result.isNthChildOf(this, 0) }
|
||||
|
||||
/** Holds if this variable declaration implicitly initializes the variable. */
|
||||
predicate hasImplicitInit() {
|
||||
|
||||
@@ -746,7 +746,9 @@ private class PpSwitchCase extends PpAst, SwitchCase {
|
||||
override string getPart(int i) {
|
||||
i = 0 and result = "default" and this instanceof DefaultCase
|
||||
or
|
||||
i = 0 and result = "case " and this instanceof ConstCase
|
||||
i = 0 and result = "case " and not this instanceof DefaultCase
|
||||
or
|
||||
i = this.lastConstCaseValueIndex() and result = "default" and this instanceof NullDefaultCase
|
||||
or
|
||||
exists(int j | i = 2 * j and j != 0 and result = ", " and exists(this.(ConstCase).getValue(j)))
|
||||
or
|
||||
@@ -757,8 +759,13 @@ private class PpSwitchCase extends PpAst, SwitchCase {
|
||||
i = 3 + this.lastConstCaseValueIndex() and result = ";" and exists(this.getRuleExpression())
|
||||
}
|
||||
|
||||
private int getCaseDefaultOffset() {
|
||||
if this instanceof NullDefaultCase then result = 1 else result = 0
|
||||
}
|
||||
|
||||
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))) + this.getCaseDefaultOffset())
|
||||
}
|
||||
|
||||
override PpAst getChild(int i) {
|
||||
|
||||
@@ -406,6 +406,9 @@ class SwitchStmt extends Stmt, @switchstmt {
|
||||
/** Gets the `default` case of this switch statement, if any. */
|
||||
DefaultCase getDefaultCase() { result = this.getACase() }
|
||||
|
||||
/** Gets the `default` or `case null, default` case of this switch statement, if any. */
|
||||
SwitchCase getDefaultOrNullDefaultCase() { result = this.getACase() and result.hasDefaultLabel() }
|
||||
|
||||
/** Gets the expression of this `switch` statement. */
|
||||
Expr getExpr() { result.getParent() = this }
|
||||
|
||||
@@ -487,14 +490,28 @@ class SwitchCase extends Stmt, @case {
|
||||
Stmt getRuleStatementOrExpressionStatement() {
|
||||
result.getParent() = this and result.getIndex() = -1
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if this case statement includes the default label, i.e. it is either `default`
|
||||
* or `case null, default`.
|
||||
*/
|
||||
predicate hasDefaultLabel() { this instanceof DefaultCase or this instanceof NullDefaultCase }
|
||||
}
|
||||
|
||||
/** 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 and e.getIndex() >= 0 and not e instanceof LocalVariableDeclExpr
|
||||
)
|
||||
// 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.
|
||||
and not isNullDefaultCase(this)
|
||||
}
|
||||
|
||||
/** Gets the `case` constant at index 0. */
|
||||
@@ -535,7 +552,11 @@ class PatternCase extends SwitchCase {
|
||||
override string getAPrimaryQlClass() { result = "PatternCase" }
|
||||
}
|
||||
|
||||
/** A `default` case of a `switch` statement */
|
||||
/**
|
||||
* A `default` case of a `switch` statement.
|
||||
*
|
||||
* Note this does not include `case null, default` -- for that, see `NullDefaultCase`.
|
||||
*/
|
||||
class DefaultCase extends SwitchCase {
|
||||
DefaultCase() { not exists(Expr e | e.getParent() = this | e.getIndex() >= 0) }
|
||||
|
||||
@@ -548,6 +569,19 @@ class DefaultCase extends SwitchCase {
|
||||
override string getAPrimaryQlClass() { result = "DefaultCase" }
|
||||
}
|
||||
|
||||
/** A `case null, default` statement of a `switch` statement or expression. */
|
||||
class NullDefaultCase extends SwitchCase {
|
||||
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. */
|
||||
|
||||
@@ -170,10 +170,11 @@ 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()
|
||||
else result = this.getDefaultCase()
|
||||
else result = this.getDefaultOrNullDefaultCase()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -55,9 +55,13 @@ predicate implies_v1(Guard g1, boolean b1, Guard g2, boolean b2) {
|
||||
)
|
||||
)
|
||||
or
|
||||
g1.(DefaultCase).getSwitch().getAConstCase() = g2 and b1 = true and b2 = false
|
||||
exists(SwitchCase sc | g1 = sc and sc.hasDefaultLabel() |
|
||||
sc.getSwitch().getAConstCase() = g2 and b1 = true and b2 = false
|
||||
)
|
||||
or
|
||||
g1.(DefaultCase).getSwitchExpr().getAConstCase() = g2 and b1 = true and b2 = false
|
||||
exists(SwitchCase sc | g1 = sc and sc.hasDefaultLabel() |
|
||||
sc.getSwitchExpr().getAConstCase() = g2 and b1 = true and b2 = false
|
||||
)
|
||||
or
|
||||
exists(MethodCall check, int argIndex | check = g1 |
|
||||
conditionCheckArgument(check, argIndex, _) and
|
||||
|
||||
@@ -15,5 +15,5 @@ import java
|
||||
from SwitchStmt switch
|
||||
where
|
||||
not switch.getExpr().getType() instanceof EnumType and
|
||||
not exists(switch.getDefaultCase())
|
||||
not exists(switch.getDefaultOrNullDefaultCase())
|
||||
select switch, "Switch statement does not have a default case."
|
||||
|
||||
@@ -17,6 +17,6 @@ from SwitchStmt switch, EnumType enum, EnumConstant missing
|
||||
where
|
||||
switch.getExpr().getType() = enum and
|
||||
missing.getDeclaringType() = enum and
|
||||
not exists(switch.getDefaultCase()) and
|
||||
not exists(switch.getDefaultOrNullDefaultCase()) and
|
||||
not switch.getAConstCase().getValue() = missing.getAnAccess()
|
||||
select switch, "Switch statement does not have a case for $@.", missing, missing.getName()
|
||||
|
||||
@@ -52,6 +52,24 @@ public class Test {
|
||||
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");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -88,6 +88,10 @@ class A {
|
||||
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";
|
||||
};
|
||||
}
|
||||
}
|
||||
catch (RuntimeException rte) {
|
||||
|
||||
@@ -246,36 +246,48 @@ A.java:
|
||||
# 88| 1: [LocalVariableDeclExpr] s
|
||||
# 89| 3: [DefaultCase] default
|
||||
# 89| -1: [StringLiteral] "It's something else"
|
||||
# 93| 0: [CatchClause] catch (...)
|
||||
# 91| 8: [LocalVariableDeclStmt] var ...;
|
||||
# 91| 1: [LocalVariableDeclExpr] nullDefaultTest
|
||||
# 91| 0: [SwitchExpr] switch (...)
|
||||
# 91| -1: [VarAccess] thing
|
||||
# 92| 0: [PatternCase] case T t ...
|
||||
# 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
|
||||
# 97| 0: [CatchClause] catch (...)
|
||||
#-----| 0: (Single Local Variable Declaration)
|
||||
# 93| 0: [TypeAccess] RuntimeException
|
||||
# 93| 1: [LocalVariableDeclExpr] rte
|
||||
# 93| 1: [BlockStmt] { ... }
|
||||
# 94| 0: [ReturnStmt] return ...
|
||||
# 98| 10: [Class] E
|
||||
# 102| 3: [FieldDeclaration] E A;
|
||||
# 97| 0: [TypeAccess] RuntimeException
|
||||
# 97| 1: [LocalVariableDeclExpr] rte
|
||||
# 97| 1: [BlockStmt] { ... }
|
||||
# 98| 0: [ReturnStmt] return ...
|
||||
# 102| 10: [Class] E
|
||||
# 106| 3: [FieldDeclaration] E A;
|
||||
#-----| -3: (Javadoc)
|
||||
# 99| 1: [Javadoc] /** Javadoc for enum constant */
|
||||
# 100| 0: [JavadocText] Javadoc for enum constant
|
||||
# 102| -1: [TypeAccess] E
|
||||
# 102| 0: [ClassInstanceExpr] new E(...)
|
||||
# 102| -3: [TypeAccess] E
|
||||
# 103| 4: [FieldDeclaration] E B;
|
||||
# 103| 1: [Javadoc] /** Javadoc for enum constant */
|
||||
# 104| 0: [JavadocText] Javadoc for enum constant
|
||||
# 106| -1: [TypeAccess] E
|
||||
# 106| 0: [ClassInstanceExpr] new E(...)
|
||||
# 106| -3: [TypeAccess] E
|
||||
# 107| 4: [FieldDeclaration] E B;
|
||||
#-----| -3: (Javadoc)
|
||||
# 99| 1: [Javadoc] /** Javadoc for enum constant */
|
||||
# 100| 0: [JavadocText] Javadoc for enum constant
|
||||
# 103| -1: [TypeAccess] E
|
||||
# 103| 0: [ClassInstanceExpr] new E(...)
|
||||
# 103| -3: [TypeAccess] E
|
||||
# 104| 5: [FieldDeclaration] E C;
|
||||
# 103| 1: [Javadoc] /** Javadoc for enum constant */
|
||||
# 104| 0: [JavadocText] Javadoc for enum constant
|
||||
# 107| -1: [TypeAccess] E
|
||||
# 107| 0: [ClassInstanceExpr] new E(...)
|
||||
# 107| -3: [TypeAccess] E
|
||||
# 108| 5: [FieldDeclaration] E C;
|
||||
#-----| -3: (Javadoc)
|
||||
# 99| 1: [Javadoc] /** Javadoc for enum constant */
|
||||
# 100| 0: [JavadocText] Javadoc for enum constant
|
||||
# 104| -1: [TypeAccess] E
|
||||
# 104| 0: [ClassInstanceExpr] new E(...)
|
||||
# 104| -3: [TypeAccess] E
|
||||
# 110| 11: [FieldDeclaration] int i, ...;
|
||||
# 103| 1: [Javadoc] /** Javadoc for enum constant */
|
||||
# 104| 0: [JavadocText] Javadoc for enum constant
|
||||
# 108| -1: [TypeAccess] E
|
||||
# 108| 0: [ClassInstanceExpr] new E(...)
|
||||
# 108| -3: [TypeAccess] E
|
||||
# 114| 11: [FieldDeclaration] int i, ...;
|
||||
#-----| -3: (Javadoc)
|
||||
# 107| 1: [Javadoc] /** Javadoc for fields */
|
||||
# 108| 0: [JavadocText] Javadoc for fields
|
||||
# 110| -1: [TypeAccess] int
|
||||
# 111| 1: [Javadoc] /** Javadoc for fields */
|
||||
# 112| 0: [JavadocText] Javadoc for fields
|
||||
# 114| -1: [TypeAccess] int
|
||||
|
||||
Reference in New Issue
Block a user