mirror of
https://github.com/github/codeql.git
synced 2025-12-17 01:03:14 +01:00
Java: Make non-returning methods use local to global.
This commit is contained in:
@@ -431,115 +431,132 @@ private module ControlFlowGraphImpl {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A virtual method with a unique implementation. That is, the method does not
|
* Module containing a global non-returning method analysis. The result is injected back into
|
||||||
* participate in overriding and there are no call targets that could dispatch
|
* a local predicate `nonReturningMethodCall()`, which is used in further local predicates
|
||||||
* to both this and another method.
|
* within this file.
|
||||||
*/
|
*/
|
||||||
private class EffectivelyNonVirtualMethod extends SrcMethod {
|
overlay[global]
|
||||||
EffectivelyNonVirtualMethod() {
|
private module NonReturningAnalysis {
|
||||||
exists(this.getBody()) and
|
/**
|
||||||
this.isVirtual() and
|
* A virtual method with a unique implementation. That is, the method does not
|
||||||
not this = any(Method m).getASourceOverriddenMethod() and
|
* participate in overriding and there are no call targets that could dispatch
|
||||||
not this.overrides(_) and
|
* to both this and another method.
|
||||||
// guard against implicit overrides of default methods
|
*/
|
||||||
not this.getAPossibleImplementationOfSrcMethod() != this and
|
private class EffectivelyNonVirtualMethod extends SrcMethod {
|
||||||
// guard against interface implementations in inheriting subclasses
|
EffectivelyNonVirtualMethod() {
|
||||||
not exists(SrcMethod m |
|
exists(this.getBody()) and
|
||||||
1 < strictcount(m.getAPossibleImplementationOfSrcMethod()) and
|
this.isVirtual() and
|
||||||
this = m.getAPossibleImplementationOfSrcMethod()
|
not this = any(Method m).getASourceOverriddenMethod() and
|
||||||
) and
|
not this.overrides(_) and
|
||||||
// UnsupportedOperationException could indicate that this is meant to be overridden
|
// guard against implicit overrides of default methods
|
||||||
not exists(ClassInstanceExpr ex |
|
not this.getAPossibleImplementationOfSrcMethod() != this and
|
||||||
this.getBody().getLastStmt().(ThrowStmt).getExpr() = ex and
|
// guard against interface implementations in inheriting subclasses
|
||||||
ex.getConstructedType().hasQualifiedName("java.lang", "UnsupportedOperationException")
|
not exists(SrcMethod m |
|
||||||
) and
|
1 < strictcount(m.getAPossibleImplementationOfSrcMethod()) and
|
||||||
// an unused parameter could indicate that this is meant to be overridden
|
this = m.getAPossibleImplementationOfSrcMethod()
|
||||||
forall(Parameter p | p = this.getAParameter() | exists(p.getAnAccess()))
|
) and
|
||||||
|
// UnsupportedOperationException could indicate that this is meant to be overridden
|
||||||
|
not exists(ClassInstanceExpr ex |
|
||||||
|
this.getBody().getLastStmt().(ThrowStmt).getExpr() = ex and
|
||||||
|
ex.getConstructedType().hasQualifiedName("java.lang", "UnsupportedOperationException")
|
||||||
|
) and
|
||||||
|
// an unused parameter could indicate that this is meant to be overridden
|
||||||
|
forall(Parameter p | p = this.getAParameter() | exists(p.getAnAccess()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Gets a `MethodCall` that calls this method. */
|
||||||
|
MethodCall getAnAccess() { result.getMethod().getAPossibleImplementation() = this }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Gets a `MethodCall` that calls this method. */
|
/** Holds if a call to `m` indicates that `m` is expected to return. */
|
||||||
MethodCall getAnAccess() { result.getMethod().getAPossibleImplementation() = this }
|
private predicate expectedReturn(EffectivelyNonVirtualMethod m) {
|
||||||
}
|
exists(Stmt s, BlockStmt b |
|
||||||
|
m.getAnAccess().getEnclosingStmt() = s and
|
||||||
/** Holds if a call to `m` indicates that `m` is expected to return. */
|
b.getAStmt() = s and
|
||||||
private predicate expectedReturn(EffectivelyNonVirtualMethod m) {
|
not b.getLastStmt() = s
|
||||||
exists(Stmt s, BlockStmt b |
|
|
||||||
m.getAnAccess().getEnclosingStmt() = s and
|
|
||||||
b.getAStmt() = s and
|
|
||||||
not b.getLastStmt() = s
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a non-overridable method that always throws an exception or calls `exit`.
|
|
||||||
*/
|
|
||||||
private Method nonReturningMethod() {
|
|
||||||
result instanceof MethodExit
|
|
||||||
or
|
|
||||||
not result.isOverridable() and
|
|
||||||
exists(BlockStmt body |
|
|
||||||
body = result.getBody() and
|
|
||||||
not exists(ReturnStmt ret | ret.getEnclosingCallable() = result)
|
|
||||||
|
|
|
||||||
not result.getReturnType() instanceof VoidType or
|
|
||||||
body.getLastStmt() = nonReturningStmt()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a virtual method that always throws an exception or calls `exit`.
|
|
||||||
*/
|
|
||||||
private EffectivelyNonVirtualMethod likelyNonReturningMethod() {
|
|
||||||
result.getReturnType() instanceof VoidType and
|
|
||||||
not exists(ReturnStmt ret | ret.getEnclosingCallable() = result) and
|
|
||||||
not expectedReturn(result) and
|
|
||||||
forall(Parameter p | p = result.getAParameter() | exists(p.getAnAccess())) and
|
|
||||||
result.getBody().getLastStmt() = nonReturningStmt()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a `MethodCall` that always throws an exception or calls `exit`.
|
|
||||||
*/
|
|
||||||
private MethodCall nonReturningMethodCall() {
|
|
||||||
result.getMethod().getSourceDeclaration() = nonReturningMethod() or
|
|
||||||
result = likelyNonReturningMethod().getAnAccess()
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets a statement that always throws an exception or calls `exit`.
|
|
||||||
*/
|
|
||||||
private Stmt nonReturningStmt() {
|
|
||||||
result instanceof ThrowStmt
|
|
||||||
or
|
|
||||||
result.(ExprStmt).getExpr() = nonReturningExpr()
|
|
||||||
or
|
|
||||||
result.(BlockStmt).getLastStmt() = nonReturningStmt()
|
|
||||||
or
|
|
||||||
exists(IfStmt ifstmt | ifstmt = result |
|
|
||||||
ifstmt.getThen() = nonReturningStmt() and
|
|
||||||
ifstmt.getElse() = nonReturningStmt()
|
|
||||||
)
|
|
||||||
or
|
|
||||||
exists(TryStmt try | try = result |
|
|
||||||
try.getBlock() = nonReturningStmt() and
|
|
||||||
forall(CatchClause cc | cc = try.getACatchClause() | cc.getBlock() = nonReturningStmt())
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets an expression that always throws an exception or calls `exit`.
|
|
||||||
*/
|
|
||||||
private Expr nonReturningExpr() {
|
|
||||||
result = nonReturningMethodCall()
|
|
||||||
or
|
|
||||||
result.(StmtExpr).getStmt() = nonReturningStmt()
|
|
||||||
or
|
|
||||||
exists(WhenExpr whenexpr | whenexpr = result |
|
|
||||||
whenexpr.getBranch(_).isElseBranch() and
|
|
||||||
forex(WhenBranch whenbranch | whenbranch = whenexpr.getBranch(_) |
|
|
||||||
whenbranch.getRhs() = nonReturningStmt()
|
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a non-overridable method that always throws an exception or calls `exit`.
|
||||||
|
*/
|
||||||
|
private Method nonReturningMethod() {
|
||||||
|
result instanceof MethodExit
|
||||||
|
or
|
||||||
|
not result.isOverridable() and
|
||||||
|
exists(BlockStmt body |
|
||||||
|
body = result.getBody() and
|
||||||
|
not exists(ReturnStmt ret | ret.getEnclosingCallable() = result)
|
||||||
|
|
|
||||||
|
not result.getReturnType() instanceof VoidType or
|
||||||
|
body.getLastStmt() = nonReturningStmt()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a virtual method that always throws an exception or calls `exit`.
|
||||||
|
*/
|
||||||
|
private EffectivelyNonVirtualMethod likelyNonReturningMethod() {
|
||||||
|
result.getReturnType() instanceof VoidType and
|
||||||
|
not exists(ReturnStmt ret | ret.getEnclosingCallable() = result) and
|
||||||
|
not expectedReturn(result) and
|
||||||
|
forall(Parameter p | p = result.getAParameter() | exists(p.getAnAccess())) and
|
||||||
|
result.getBody().getLastStmt() = nonReturningStmt()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a `MethodCall` that always throws an exception or calls `exit`.
|
||||||
|
*/
|
||||||
|
private MethodCall nonReturningMethodCallGlobal() {
|
||||||
|
result.getMethod().getSourceDeclaration() = nonReturningMethod() or
|
||||||
|
result = likelyNonReturningMethod().getAnAccess()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a `MethodCall` that always throws an exception or calls `exit`.
|
||||||
|
*
|
||||||
|
* This predicate is local so gives the local anaylyis for base and the global
|
||||||
|
* analyis for overlay cases.
|
||||||
|
*/
|
||||||
|
overlay[local]
|
||||||
|
MethodCall nonReturningMethodCall() = localToGlobal(nonReturningMethodCallGlobal/0)(result)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a statement that always throws an exception or calls `exit`.
|
||||||
|
*/
|
||||||
|
private Stmt nonReturningStmt() {
|
||||||
|
result instanceof ThrowStmt
|
||||||
|
or
|
||||||
|
result.(ExprStmt).getExpr() = nonReturningExpr()
|
||||||
|
or
|
||||||
|
result.(BlockStmt).getLastStmt() = nonReturningStmt()
|
||||||
|
or
|
||||||
|
exists(IfStmt ifstmt | ifstmt = result |
|
||||||
|
ifstmt.getThen() = nonReturningStmt() and
|
||||||
|
ifstmt.getElse() = nonReturningStmt()
|
||||||
|
)
|
||||||
|
or
|
||||||
|
exists(TryStmt try | try = result |
|
||||||
|
try.getBlock() = nonReturningStmt() and
|
||||||
|
forall(CatchClause cc | cc = try.getACatchClause() | cc.getBlock() = nonReturningStmt())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets an expression that always throws an exception or calls `exit`.
|
||||||
|
*/
|
||||||
|
private Expr nonReturningExpr() {
|
||||||
|
result = nonReturningMethodCallGlobal()
|
||||||
|
or
|
||||||
|
result.(StmtExpr).getStmt() = nonReturningStmt()
|
||||||
|
or
|
||||||
|
exists(WhenExpr whenexpr | whenexpr = result |
|
||||||
|
whenexpr.getBranch(_).isElseBranch() and
|
||||||
|
forex(WhenBranch whenbranch | whenbranch = whenexpr.getBranch(_) |
|
||||||
|
whenbranch.getRhs() = nonReturningStmt()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Join order engineering -- first determine the switch block and the case indices required, then retrieve them.
|
// Join order engineering -- first determine the switch block and the case indices required, then retrieve them.
|
||||||
@@ -766,7 +783,7 @@ private module ControlFlowGraphImpl {
|
|||||||
not this instanceof BooleanLiteral and
|
not this instanceof BooleanLiteral and
|
||||||
not this instanceof ReturnStmt and
|
not this instanceof ReturnStmt and
|
||||||
not this instanceof ThrowStmt and
|
not this instanceof ThrowStmt and
|
||||||
not this = nonReturningMethodCall()
|
not this = NonReturningAnalysis::nonReturningMethodCall()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user