Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions java/ql/consistency-queries/AstConsistency.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import java
import ControlFlow::AstConsistency
192 changes: 192 additions & 0 deletions shared/controlflow/codeql/controlflow/AstConsistency.qll
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/**
* Provides consistency queries for `AstSig`.
*/
overlay[local?]
module;

private import codeql.util.Location
private import ControlFlowGraph
Comment thread
aschackmull marked this conversation as resolved.

module MakeAstConsistency<LocationSig Location, AstSig<Location> Ast> {
private import Ast

private AstNode getParent(AstNode child) { child = getChild(result, _) }

private predicate astMemberChild(AstNode parent, AstNode child, string member) {
callableGetBody(parent) = child and member = "Callable.getBody"
or
callableGetParameter(parent, _) = child and member = "Callable.getParameter"
or
parent.(Parameter).getDefaultValue() = child and member = "Parameter.getDefaultValue"
or
parent.(BlockStmt).getStmt(_) = child and member = "BlockStmt.getStmt"
or
parent.(BlockStmt).getLastStmt() = child and member = "BlockStmt.getLastStmt"
or
parent.(ExprStmt).getExpr() = child and member = "ExprStmt.getExpr"
or
parent.(IfStmt).getCondition() = child and member = "IfStmt.getCondition"
or
parent.(IfStmt).getThen() = child and member = "IfStmt.getThen"
or
parent.(IfStmt).getElse() = child and member = "IfStmt.getElse"
or
parent.(LoopStmt).getBody() = child and member = "LoopStmt.getBody"
or
parent.(WhileStmt).getCondition() = child and member = "WhileStmt.getCondition"
or
parent.(DoStmt).getCondition() = child and member = "DoStmt.getCondition"
or
parent.(ForStmt).getInit(_) = child and member = "ForStmt.getInit"
or
parent.(ForStmt).getCondition() = child and member = "ForStmt.getCondition"
or
parent.(ForStmt).getUpdate(_) = child and member = "ForStmt.getUpdate"
or
parent.(ForeachStmt).getVariable() = child and member = "ForeachStmt.getVariable"
or
parent.(ForeachStmt).getCollection() = child and member = "ForeachStmt.getCollection"
or
parent.(ReturnStmt).getExpr() = child and member = "ReturnStmt.getExpr"
or
parent.(Throw).getExpr() = child and member = "Throw.getExpr"
or
parent.(TryStmt).getBody() = child and member = "TryStmt.getBody"
or
parent.(TryStmt).getCatch(_) = child and member = "TryStmt.getCatch"
or
parent.(TryStmt).getFinally() = child and member = "TryStmt.getFinally"
or
getTryInit(parent, _) = child and member = "getTryInit"
or
getTryElse(parent) = child and member = "getTryElse"
or
parent.(CatchClause).getVariable() = child and member = "CatchClause.getVariable"
or
parent.(CatchClause).getCondition() = child and member = "CatchClause.getCondition"
or
parent.(CatchClause).getBody() = child and member = "CatchClause.getBody"
or
parent.(Switch).getExpr() = child and member = "Switch.getExpr"
or
parent.(Switch).getCase(_) = child and member = "Switch.getCase"
or
parent.(Switch).getStmt(_) = child and member = "Switch.getStmt"
or
parent.(Case).getAPattern() = child and member = "Case.getAPattern"
or
parent.(Case).getGuard() = child and member = "Case.getGuard"
or
parent.(Case).getBody() = child and member = "Case.getBody"
or
parent.(ConditionalExpr).getCondition() = child and member = "ConditionalExpr.getCondition"
or
parent.(ConditionalExpr).getThen() = child and member = "ConditionalExpr.getThen"
or
parent.(ConditionalExpr).getElse() = child and member = "ConditionalExpr.getElse"
or
parent.(BinaryExpr).getLeftOperand() = child and member = "BinaryExpr.getLeftOperand"
or
parent.(BinaryExpr).getRightOperand() = child and member = "BinaryExpr.getRightOperand"
or
parent.(UnaryExpr).getOperand() = child and member = "UnaryExpr.getOperand"
or
parent.(PatternMatchExpr).getExpr() = child and member = "PatternMatchExpr.getExpr"
or
parent.(PatternMatchExpr).getPattern() = child and member = "PatternMatchExpr.getPattern"
}

final private class FinalAstNode = AstNode;

private class StructuredAstNode extends FinalAstNode {
StructuredAstNode() {
this instanceof Callable or
this instanceof Parameter or
this instanceof BlockStmt or
this instanceof ExprStmt or
this instanceof IfStmt or
this instanceof LoopStmt or
this instanceof WhileStmt or
this instanceof DoStmt or
this instanceof ForStmt or
this instanceof ForeachStmt or
this instanceof ReturnStmt or
this instanceof Throw or
this instanceof TryStmt or
this instanceof CatchClause or
this instanceof Switch or
this instanceof Case or
this instanceof ConditionalExpr or
this instanceof BinaryExpr or
this instanceof UnaryExpr or
this instanceof PatternMatchExpr
}
}

module Consistency {
/** Holds if the consistency query `query` has `results` results. */
query predicate consistencyOverview(string query, int results) {
query = "multipleParents" and
results = strictcount(AstNode child | multipleParents(child, _, _))
or
query = "nonAncestorEnclosingCallable" and
results = strictcount(AstNode node | nonAncestorEnclosingCallable(node, _))
or
query = "childAtMultipleIndices" and
results = strictcount(AstNode child | childAtMultipleIndices(_, child, _, _))
or
query = "siblingsWithIdenticalIndex" and
results = strictcount(AstNode parent, int index | siblingsWithIdenticalIndex(parent, index, _, _))
or
query = "memberChildMissingFromGetChild" and
results = strictcount(AstNode parent, AstNode child | memberChildMissingFromGetChild(parent, child, _))
or
query = "getChildMissingFromMember" and
results = strictcount(AstNode parent, int index | getChildMissingFromMember(parent, index, _))
}

/** Holds if `child` has multiple AST parents. */
query predicate multipleParents(AstNode child, AstNode parent1, AstNode parent2) {
getChild(parent1, _) = child and
getChild(parent2, _) = child and
parent1 != parent2
}

/**
* Holds if `node` has an enclosing callable that is not reachable through
* parent steps.
*/
query predicate nonAncestorEnclosingCallable(AstNode node, Callable callable) {
callable = getEnclosingCallable(node) and
not getParent*(node) = callable
}

/** Holds if `child` is assigned multiple child indices under `parent`. */
query predicate childAtMultipleIndices(AstNode parent, AstNode child, int index1, int index2) {
getChild(parent, index1) = child and
getChild(parent, index2) = child and
index1 < index2
}

/** Holds if multiple children of `parent` share the same child index. */
query predicate siblingsWithIdenticalIndex(
AstNode parent, int index, AstNode child1, AstNode child2
) {
getChild(parent, index) = child1 and
getChild(parent, index) = child2 and
child1 != child2
}

/** Holds if a member child relation is not reflected by `getChild`. */
query predicate memberChildMissingFromGetChild(AstNode parent, AstNode child, string member) {
astMemberChild(parent, child, member) and
not getChild(parent, _) = child
}

/** Holds if a `getChild` relation for a structured AST node has no matching member predicate. */
query predicate getChildMissingFromMember(StructuredAstNode parent, int index, AstNode child) {

Check warning

Code scanning / CodeQL

Predicates starting with "get" or "as" should return a value Warning

This predicate starts with 'get' but does not return a value.
child = getChild(parent, index) and
not astMemberChild(parent, child, _)
}
}
}
4 changes: 4 additions & 0 deletions shared/controlflow/codeql/controlflow/ControlFlowGraph.qll
Original file line number Diff line number Diff line change
Expand Up @@ -2063,6 +2063,7 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {
*/

private import PrintGraph as Pp
private import AstConsistency

private class ControlFlowNodeAlias = ControlFlowNode;

Expand All @@ -2078,6 +2079,9 @@ module Make0<LocationSig Location, AstSig<Location> Ast> {

import Pp::PrintGraph<Location, PrintGraphInput>

/** Provides AST consistency queries. */
module AstConsistency = MakeAstConsistency<Location, Ast>::Consistency;

/** Provides a set of consistency queries. */
module Consistency {
/** Holds if the consistency query `query` has `results` results. */
Expand Down
Loading