mirror of
https://github.com/github/codeql.git
synced 2026-05-01 11:45:14 +02:00
Java: Rename StmtExpr to ValueDiscardingExpr
As mentioned by aschackmull during review, StatementExpression as defined by the JLS only lists possible types of expressions, it does _not_ specify that their value is discarded. Therefore, for example any method call could be considered a StatementExpression. The name ValueDiscardingExpr was chosen as replacement because the JLS uses the phrase "if the expression has a value, the value is discarded" multiple times.
This commit is contained in:
@@ -1,4 +0,0 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* The QL class `StmtExpr` has been added to model statement expressions, that is, expressions whose result is discarded.
|
||||
@@ -0,0 +1,4 @@
|
||||
---
|
||||
category: feature
|
||||
---
|
||||
* The QL class `ValueDiscardingExpr` has been added, representing expressions for which the value of the expression as a whole is discarded.
|
||||
@@ -84,7 +84,7 @@ class CollectionMutation extends MethodAccess {
|
||||
CollectionMutation() { this.getMethod() instanceof CollectionMutator }
|
||||
|
||||
/** Holds if the result of this call is not immediately discarded. */
|
||||
predicate resultIsChecked() { not this instanceof StmtExpr }
|
||||
predicate resultIsChecked() { not this instanceof ValueDiscardingExpr }
|
||||
}
|
||||
|
||||
/** A method that queries the contents of a collection without mutating it. */
|
||||
|
||||
@@ -2135,38 +2135,45 @@ class Argument extends Expr {
|
||||
}
|
||||
|
||||
/**
|
||||
* A statement expression, as specified by JLS 17 section 14.8.
|
||||
* The result of a statement expression, if any, is discarded.
|
||||
* An expression for which the value of the expression as a whole is discarded. Only cases
|
||||
* of discarded values at the language level (as described by the JLS) are considered;
|
||||
* data flow, for example to determine if an assigned variable value is ever read, is not
|
||||
* considered. Such expressions can for example appear as part of an `ExprStmt` or as
|
||||
* initializer of a `for` loop.
|
||||
*
|
||||
* Not to be confused with `ExprStmt`; while the child of an `ExprStmt` is always
|
||||
* a `StmtExpr`, the opposite is not true. A `StmtExpr` occurs for example also
|
||||
* as 'init' of a `for` statement.
|
||||
* For example, for the statement `i++;` the value of the increment expression, that is the
|
||||
* old value of variable `i`, is discarded. Whereas for the statement `println(i++);` the
|
||||
* value of the increment expression is not discarded but used as argument for the method call.
|
||||
*/
|
||||
class StmtExpr extends Expr {
|
||||
StmtExpr() {
|
||||
this = any(ExprStmt s).getExpr()
|
||||
or
|
||||
this = any(ForStmt s).getAnInit() and not this instanceof LocalVariableDeclExpr
|
||||
or
|
||||
this = any(ForStmt s).getAnUpdate()
|
||||
or
|
||||
// Only applies to SwitchStmt, but not to SwitchExpr, see JLS 17 section 14.11.2
|
||||
this = any(SwitchStmt s).getACase().getRuleExpression()
|
||||
or
|
||||
// TODO: Workarounds for https://github.com/github/codeql/issues/3605
|
||||
exists(LambdaExpr lambda |
|
||||
this = lambda.getExprBody() and
|
||||
lambda.asMethod().getReturnType() instanceof VoidType
|
||||
)
|
||||
or
|
||||
exists(MemberRefExpr memberRef, Method implicitMethod, Method overridden |
|
||||
implicitMethod = memberRef.asMethod()
|
||||
|
|
||||
this.getParent().(ReturnStmt).getEnclosingCallable() = implicitMethod and
|
||||
// asMethod() has bogus method with wrong return type as result, e.g. `run(): String` (overriding `Runnable.run(): void`)
|
||||
// Therefore need to check the overridden method
|
||||
implicitMethod.getSourceDeclaration().overridesOrInstantiates*(overridden) and
|
||||
overridden.getReturnType() instanceof VoidType
|
||||
)
|
||||
class ValueDiscardingExpr extends Expr {
|
||||
ValueDiscardingExpr() {
|
||||
(
|
||||
this = any(ExprStmt s).getExpr()
|
||||
or
|
||||
this = any(ForStmt s).getAnInit() and not this instanceof LocalVariableDeclExpr
|
||||
or
|
||||
this = any(ForStmt s).getAnUpdate()
|
||||
or
|
||||
// Only applies to SwitchStmt, but not to SwitchExpr, see JLS 17 section 14.11.2
|
||||
this = any(SwitchStmt s).getACase().getRuleExpression()
|
||||
or
|
||||
// TODO: Workarounds for https://github.com/github/codeql/issues/3605
|
||||
exists(LambdaExpr lambda |
|
||||
this = lambda.getExprBody() and
|
||||
lambda.asMethod().getReturnType() instanceof VoidType
|
||||
)
|
||||
or
|
||||
exists(MemberRefExpr memberRef, Method implicitMethod, Method overridden |
|
||||
implicitMethod = memberRef.asMethod()
|
||||
|
|
||||
this.getParent().(ReturnStmt).getEnclosingCallable() = implicitMethod and
|
||||
// asMethod() has bogus method with wrong return type as result, e.g. `run(): String` (overriding `Runnable.run(): void`)
|
||||
// Therefore need to check the overridden method
|
||||
implicitMethod.getSourceDeclaration().overridesOrInstantiates*(overridden) and
|
||||
overridden.getReturnType() instanceof VoidType
|
||||
)
|
||||
) and
|
||||
// Ignore if this expression is a method call with `void` as return type
|
||||
not getType() instanceof VoidType
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ class MapMutation extends MethodAccess {
|
||||
MapMutation() { this.getMethod() instanceof MapMutator }
|
||||
|
||||
/** Holds if the result of this call is not immediately discarded. */
|
||||
predicate resultIsChecked() { not this instanceof StmtExpr }
|
||||
predicate resultIsChecked() { not this instanceof ValueDiscardingExpr }
|
||||
}
|
||||
|
||||
/** A method that queries the contents of the map it belongs to without mutating it. */
|
||||
|
||||
@@ -18,7 +18,7 @@ import Chaining
|
||||
|
||||
predicate checkedMethodCall(MethodAccess ma) {
|
||||
relevantMethodCall(ma, _) and
|
||||
not ma instanceof StmtExpr
|
||||
not ma instanceof ValueDiscardingExpr
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,7 +45,7 @@ predicate unboundedQueue(RefType t) {
|
||||
|
||||
from MethodAccess ma, SpecialMethod m
|
||||
where
|
||||
ma instanceof StmtExpr and
|
||||
ma instanceof ValueDiscardingExpr and
|
||||
m = ma.getMethod() and
|
||||
(
|
||||
m.isMethod("java.util", "Queue", "offer", 1) and not unboundedQueue(m.getDeclaringType())
|
||||
|
||||
@@ -21,7 +21,7 @@ private class HostnameVerificationCall extends MethodAccess {
|
||||
}
|
||||
|
||||
/** Holds if the result of the call is not used. */
|
||||
predicate isIgnored() { this instanceof StmtExpr }
|
||||
predicate isIgnored() { this instanceof ValueDiscardingExpr }
|
||||
}
|
||||
|
||||
from HostnameVerificationCall verification
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
| StmtExpr.java:7:9:7:18 | toString(...) |
|
||||
| StmtExpr.java:13:9:13:13 | ...=... |
|
||||
| StmtExpr.java:14:9:14:11 | ...++ |
|
||||
| StmtExpr.java:15:9:15:11 | ++... |
|
||||
| StmtExpr.java:16:9:16:11 | ...-- |
|
||||
| StmtExpr.java:17:9:17:11 | --... |
|
||||
| StmtExpr.java:19:9:19:20 | new Object(...) |
|
||||
| StmtExpr.java:22:9:22:28 | clone(...) |
|
||||
| StmtExpr.java:25:14:25:39 | println(...) |
|
||||
| StmtExpr.java:30:17:30:44 | println(...) |
|
||||
| StmtExpr.java:45:24:45:33 | toString(...) |
|
||||
| StmtExpr.java:58:28:58:37 | toString(...) |
|
||||
| StmtExpr.java:60:13:60:22 | toString(...) |
|
||||
| StmtExpr.java:66:23:66:36 | toString(...) |
|
||||
@@ -1,68 +0,0 @@
|
||||
package StmtExpr;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class StmtExpr {
|
||||
void test() {
|
||||
toString();
|
||||
|
||||
// LocalVariableDeclarationStatement with init is not a StatementExpression
|
||||
String s = toString();
|
||||
|
||||
int i;
|
||||
i = 0;
|
||||
i++;
|
||||
++i;
|
||||
i--;
|
||||
--i;
|
||||
|
||||
new Object();
|
||||
// ArrayCreationExpression cannot be a StatementExpression, but a method access
|
||||
// on it can be
|
||||
new int[] {}.clone();
|
||||
|
||||
// for statement init can be StatementExpression
|
||||
for (System.out.println("init");;) {
|
||||
break;
|
||||
}
|
||||
|
||||
// for statement update is StatementExpression
|
||||
for (;; System.out.println("update")) {
|
||||
break;
|
||||
}
|
||||
|
||||
// variable declaration and condition are not StatementExpressions
|
||||
for (int i1 = 0; i1 < 10;) { }
|
||||
for (int i1, i2 = 0; i2 < 10;) { }
|
||||
for (;;) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Not a StatementExpression
|
||||
for (int i2 : new int[] {1}) { }
|
||||
|
||||
switch(1) {
|
||||
default -> toString(); // StatementExpression
|
||||
}
|
||||
// SwitchExpression has no StatementExpression
|
||||
String s2 = switch(1) {
|
||||
default -> toString();
|
||||
};
|
||||
|
||||
// Lambda with non-void return type has no StatementExpression
|
||||
Supplier<Object> supplier1 = () -> toString();
|
||||
Supplier<Object> supplier2 = () -> {
|
||||
return toString();
|
||||
};
|
||||
// Lambda with void return type has StatementExpression
|
||||
Runnable r = () -> toString();
|
||||
Runnable r2 = () -> {
|
||||
toString();
|
||||
};
|
||||
|
||||
// Method reference with non-void return type has no StatementExpression
|
||||
Supplier<Object> supplier3 = StmtExpr::new;
|
||||
// Method reference with void return type has StatementExpression in implicit method body
|
||||
Runnable r3 = this::toString;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
import java
|
||||
|
||||
from StmtExpr e
|
||||
select e
|
||||
@@ -0,0 +1,16 @@
|
||||
| ValueDiscardingExpr.java:9:9:9:18 | toString(...) |
|
||||
| ValueDiscardingExpr.java:18:9:18:13 | ...=... |
|
||||
| ValueDiscardingExpr.java:19:9:19:11 | ...++ |
|
||||
| ValueDiscardingExpr.java:20:9:20:11 | ++... |
|
||||
| ValueDiscardingExpr.java:21:9:21:11 | ...-- |
|
||||
| ValueDiscardingExpr.java:22:9:22:11 | --... |
|
||||
| ValueDiscardingExpr.java:24:9:24:20 | new Object(...) |
|
||||
| ValueDiscardingExpr.java:27:9:27:28 | clone(...) |
|
||||
| ValueDiscardingExpr.java:30:14:30:38 | append(...) |
|
||||
| ValueDiscardingExpr.java:35:17:35:43 | append(...) |
|
||||
| ValueDiscardingExpr.java:55:24:55:33 | toString(...) |
|
||||
| ValueDiscardingExpr.java:74:29:74:38 | toString(...) |
|
||||
| ValueDiscardingExpr.java:76:13:76:22 | toString(...) |
|
||||
| ValueDiscardingExpr.java:90:23:90:35 | new StmtExpr(...) |
|
||||
| ValueDiscardingExpr.java:91:23:91:36 | toString(...) |
|
||||
| ValueDiscardingExpr.java:95:25:95:37 | new String[] |
|
||||
@@ -0,0 +1,100 @@
|
||||
package StmtExpr;
|
||||
|
||||
import java.util.function.IntConsumer;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class StmtExpr {
|
||||
void test() {
|
||||
toString();
|
||||
|
||||
// Method call with `void` return type is not a ValueDiscardingExpr
|
||||
System.out.println("test");
|
||||
|
||||
// LocalVariableDeclarationStatement with init is not a ValueDiscardingExpr
|
||||
String s = toString();
|
||||
|
||||
int i;
|
||||
i = 0;
|
||||
i++;
|
||||
++i;
|
||||
i--;
|
||||
--i;
|
||||
|
||||
new Object();
|
||||
// Language specification does not permit ArrayCreationExpression at locations where its
|
||||
// value would be discard, but the value of a method access on it can be discarded
|
||||
new int[] {}.clone();
|
||||
|
||||
// for statement init can discard value
|
||||
for (System.out.append("init");;) {
|
||||
break;
|
||||
}
|
||||
|
||||
// for statement update discards value
|
||||
for (;; System.out.append("update")) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Method call with `void` return type is not a ValueDiscardingExpr
|
||||
for (;; System.out.println("update")) {
|
||||
break;
|
||||
}
|
||||
|
||||
// variable declaration and condition are not ValueDiscardingExpr
|
||||
for (int i1 = 0; i1 < 10;) { }
|
||||
for (int i1, i2 = 0; i2 < 10;) { }
|
||||
for (;;) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Not a ValueDiscardingExpr
|
||||
for (int i2 : new int[] {1}) { }
|
||||
|
||||
switch(1) {
|
||||
default -> toString(); // ValueDiscardingExpr
|
||||
}
|
||||
|
||||
switch(1) {
|
||||
// Method call with `void` return type is not a ValueDiscardingExpr
|
||||
default -> System.out.println();
|
||||
}
|
||||
|
||||
String s2 = switch(1) {
|
||||
// Expression in SwitchExpression case rule is not a ValueDiscardingExpr
|
||||
default -> toString();
|
||||
};
|
||||
|
||||
// Expression in lambda with non-void return type is not a ValueDiscardingExpr
|
||||
Supplier<Object> supplier1 = () -> toString();
|
||||
Supplier<Object> supplier2 = () -> {
|
||||
return toString();
|
||||
};
|
||||
// Expression in lambda with void return type is ValueDiscardingExpr
|
||||
Runnable r1 = () -> toString();
|
||||
Runnable r2 = () -> {
|
||||
toString();
|
||||
};
|
||||
// Lambda with method call with `void` return type is not a ValueDiscardingExpr
|
||||
Runnable r3 = () -> System.out.println();
|
||||
Runnable r4 = () -> {
|
||||
System.out.println();
|
||||
};
|
||||
|
||||
// Method reference with non-void return type has no ValueDiscardingExpr
|
||||
Supplier<Object> supplier3 = StmtExpr::new;
|
||||
IntFunction<Object> f = String[]::new;
|
||||
Supplier<Object> supplier4 = this::toString;
|
||||
|
||||
// Method reference with void return type has ValueDiscardingExpr in implicit method body
|
||||
Runnable r5 = StmtExpr::new;
|
||||
Runnable r6 = this::toString;
|
||||
// Interestingly a method reference expression with function type (int)->void allows usage of
|
||||
// array creation expressions, even though a regular StatementExpression would not allow it,
|
||||
// for example the ExpressionStatement `new String[1];` is not allowed by the JLS
|
||||
IntConsumer c = String[]::new;
|
||||
|
||||
// Method reference referring to method with `void` return type is not a ValueDiscardingExpr
|
||||
Runnable r7 = System.out::println;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
import java
|
||||
|
||||
from ValueDiscardingExpr e
|
||||
select e
|
||||
Reference in New Issue
Block a user