diff --git a/ql/docs/learn-ql/ast.dot b/ql/docs/learn-ql/ast.dot index eca1e0c4e7b..fbf32b744ed 100644 --- a/ql/docs/learn-ql/ast.dot +++ b/ql/docs/learn-ql/ast.dot @@ -1,19 +1,20 @@ digraph ast { + graph [dpi=300]; "x" [shape=rect]; "y" [shape=rect]; - "z" [shape=rect]; "x + y" [shape=rect]; "(x + y)" [shape=rect]; + "z" [shape=rect]; "(x + y) * z" [shape=rect]; invis1 [style=invis]; invis2 [style=invis]; invis3 [style=invis]; - "(x + y) * z" -> "(x + y)" [label=0]; - "(x + y) * z" -> "z" [label=1]; - "(x + y)" -> "x + y" [label=0]; - "x + y" -> "x" [label=0]; - "x + y" -> "y" [label=1]; + "(x + y) * z" -> "(x + y)" [label=" 0"]; + "(x + y) * z" -> "z" [label=" 1"]; + "(x + y)" -> "x + y" [label=" 0"]; + "x + y" -> "x" [label=" 0"]; + "x + y" -> "y" [label=" 1"]; "z" -> invis1 [style=invis]; invis1 -> invis2 [style=invis]; diff --git a/ql/docs/learn-ql/ast.png b/ql/docs/learn-ql/ast.png index 46098b6f0d5..61a9f29b80a 100644 Binary files a/ql/docs/learn-ql/ast.png and b/ql/docs/learn-ql/ast.png differ diff --git a/ql/docs/learn-ql/cfg.dot b/ql/docs/learn-ql/cfg.dot index 1d4114467f0..47db3df4c57 100644 --- a/ql/docs/learn-ql/cfg.dot +++ b/ql/docs/learn-ql/cfg.dot @@ -1,4 +1,5 @@ digraph cfg { + graph [dpi=300]; rankdir=LR; "x := 0" -> "p != nil"; "p != nil" -> "x = p.f"; diff --git a/ql/docs/learn-ql/cfg.png b/ql/docs/learn-ql/cfg.png index 87c5187344f..1290dadb870 100644 Binary files a/ql/docs/learn-ql/cfg.png and b/ql/docs/learn-ql/cfg.png differ diff --git a/ql/docs/learn-ql/cfg2.dot b/ql/docs/learn-ql/cfg2.dot index 525eddb774e..b8dcd71ee25 100644 --- a/ql/docs/learn-ql/cfg2.dot +++ b/ql/docs/learn-ql/cfg2.dot @@ -1,4 +1,5 @@ digraph cfg2 { + graph [dpi=300]; rankdir=LR; "p != nil is true" [shape=box]; diff --git a/ql/docs/learn-ql/cfg2.png b/ql/docs/learn-ql/cfg2.png index adff43adf97..617fe4fe4dc 100644 Binary files a/ql/docs/learn-ql/cfg2.png and b/ql/docs/learn-ql/cfg2.png differ diff --git a/ql/docs/learn-ql/dfg.dot b/ql/docs/learn-ql/dfg.dot index 7a59581117e..82253c9b133 100644 --- a/ql/docs/learn-ql/dfg.dot +++ b/ql/docs/learn-ql/dfg.dot @@ -1,4 +1,5 @@ digraph dfg { + graph [dpi=300]; rankdir=LR; "x" [shape=diamond]; diff --git a/ql/docs/learn-ql/dfg.png b/ql/docs/learn-ql/dfg.png index d6c585c5e6c..6727af7b4ac 100644 Binary files a/ql/docs/learn-ql/dfg.png and b/ql/docs/learn-ql/dfg.png differ diff --git a/ql/docs/learn-ql/introduce-libraries.rst b/ql/docs/learn-ql/introduce-libraries.rst index 9b1b16d4568..c92f6f66c2f 100644 --- a/ql/docs/learn-ql/introduce-libraries.rst +++ b/ql/docs/learn-ql/introduce-libraries.rst @@ -35,6 +35,9 @@ about which function may be invoked by a given call (taking virtual dispatch thr account), as well as control-flow information about the order in which different operations may be executed at runtime. +As a rule of thumb, you normally want to use the AST only for superficial syntactic queries. Any +analysis involving deeper semantic properties of the program should be done on the DFG. + The rest of this tutorial briefly summarizes the most important classes and predicates provided by this library, including references to the `detailed API documentation `__ where applicable. We start by giving an overview of the AST @@ -114,7 +117,7 @@ Statements - ``CompoundAssignStmt``: an assignment statement with a compound operator, such as ``lhs += rhs`` - ``IncStmt``, ``DecStmt``: an increment statement or a decrement statement, respectively; use - ``getExpr()`` to access the expression being incremented or decremented + ``getOperand()`` to access the expression being incremented or decremented - ``BlockStmt``: a block of statements between curly braces; use ``getStmt(i)`` to access the ``i``\ th statement in a block - ``IfStmt``: an ``if`` statement; use ``getInit()``, ``getCond()``, ``getThen()``, and @@ -136,6 +139,8 @@ Statements deferred - ``SendStmt``: a send statement; use ``getChannel()`` and ``getValue()`` to access the channel and the value being sent over the channel, respectively +- ``RecvStmt``: a receive statement in a ``select`` statement; use ``getExpr()`` to access the + receiver expression, and ``getLhs(i)`` to access the ``i``\ th left-hand side - ``ReturnStmt``: a ``return`` statement; use ``getExpr(i)`` to access the ``i``\ th returned expression; if there is only a single returned expression you can use ``getExpr()`` instead - ``BranchStmt``: a statement that interrupts structured control flow; use ``getLabel()`` to get the @@ -152,7 +157,7 @@ Statements - ``SwitchStmt``: a ``switch`` statement; use ``getInit()`` to access the (optional) init statement, and ``getCase(i)`` to access the ``i``\ th ``case`` or ``default`` clause - - ``ExprSwitchStmt``: a ``switch`` statement examining the value of an expression + - ``ExpressionSwitchStmt``: a ``switch`` statement examining the value of an expression - ``TypeSwitchStmt``: a ``switch`` statement examining the type of an expression - ``CaseClause``: a ``case`` or ``default`` clause in a ``switch`` statement; use ``getExpr(i)`` to @@ -421,10 +426,10 @@ In CodeQL, data-flow nodes are represented by class ``DataFlow::Node``, and the are captured by the predicate ``DataFlow::localFlowStep``. The predicate ``DataFlow::localFlow`` generalizes this from a single flow step to zero or more flow steps. -Most expressions have a corresponding data-flow node (exceptions include type expressions, statement -labels, and other expressions that do not have a value). To map from the AST node of an expression to -the corresponding DFG node, use ``DataFlow::exprNode``. Note that the AST node and the DFG node are -different entities and cannot be used interchangeably. +Most expressions have a corresponding data-flow node; exceptions include type expressions, statement +labels and other expressions that do not have a value, as well as short-circuiting operators. To map +from the AST node of an expression to the corresponding DFG node, use ``DataFlow::exprNode``. Note +that the AST node and the DFG node are different entities and cannot be used interchangeably. There is also a predicate ``asExpr()`` on ``DataFlow::Node`` that allows you to recover the expression underlying a DFG node. However, this predicate should be used with caution, since many @@ -445,16 +450,17 @@ Important subclasses of ``DataFlow::Node`` include: corresponding AST node - ``DataFlow::BinaryOperationNode``: an operation involving a binary operator; each ``BinaryExpr`` has a corresponding ``BinaryOperationNode``, but there are also binary operations that are not - explicit at the AST level, such as those arising from compound assignments and increment/ - decrement statements; at the AST level, ``x + 1``, ``x += 1``, and ``x++`` are represented by - different kinds of AST nodes, while at the DFG level they are all modeled as a binary - operation node with operands ``x`` and ``1`` + explicit at the AST level, such as those arising from compound assignments and + increment/decrement statements; at the AST level, ``x + 1``, ``x += 1``, and ``x++`` are + represented by different kinds of AST nodes, while at the DFG level they are all modeled as a + binary operation node with operands ``x`` and ``1`` - ``DataFlow::UnaryOperationNode``: analogous, but for unary operators - - ``DataFlow::PointerDereferenceNode``: a pointer dereference, either explicit in an expression of - the form ``*p``, or implicit in a field or method reference through a pointer - - ``DataFlow::AddressOperationNode``: analogous, but for taking the address of an entity - - ``DataFlow::RelationalComparisonNode``, ``DataFlow::EqualityTestNode``: data-flow nodes - corresponding to ``RelationalComparisonExpr`` and ``EqualityTestExpr`` AST nodes + + - ``DataFlow::PointerDereferenceNode``: a pointer dereference, either explicit in an expression + of the form ``*p``, or implicit in a field or method reference through a pointer + - ``DataFlow::AddressOperationNode``: analogous, but for taking the address of an entity + - ``DataFlow::RelationalComparisonNode``, ``DataFlow::EqualityTestNode``: data-flow nodes + corresponding to ``RelationalComparisonExpr`` and ``EqualityTestExpr`` AST nodes Finally, classes ``Read`` and ``Write`` represent, respectively, a read or a write of a variable, a field, or an element of an array, a slice or a map. Use their member predicates ``readsVariable``, diff --git a/ql/docs/learn-ql/ssa.dot b/ql/docs/learn-ql/ssa.dot index f33579ad51d..c8bfb8c62a0 100644 --- a/ql/docs/learn-ql/ssa.dot +++ b/ql/docs/learn-ql/ssa.dot @@ -1,4 +1,5 @@ digraph ssa { + graph [dpi=300]; rankdir=LR; "x1" [shape=diamond,label=1>]; diff --git a/ql/docs/learn-ql/ssa.png b/ql/docs/learn-ql/ssa.png index a29bbe4633b..cd5ba3f29de 100644 Binary files a/ql/docs/learn-ql/ssa.png and b/ql/docs/learn-ql/ssa.png differ diff --git a/ql/src/InconsistentCode/ConstantLengthComparison.ql b/ql/src/InconsistentCode/ConstantLengthComparison.ql index bc4c9b0aa00..6058e287dec 100644 --- a/ql/src/InconsistentCode/ConstantLengthComparison.ql +++ b/ql/src/InconsistentCode/ConstantLengthComparison.ql @@ -16,7 +16,7 @@ from ControlFlow::ConditionGuardNode cond, DataFlow::CallNode lenA where // `i` is incremented in `fs` - fs.getPost().(IncStmt).getExpr() = i.getAReference() and + fs.getPost().(IncStmt).getOperand() = i.getAReference() and // `idx` reads `a[i]` idx.reads(a.getANode(), i.getARead()) and // `lenA` is `len(a)` diff --git a/ql/src/InconsistentCode/InconsistentLoopOrientation.ql b/ql/src/InconsistentCode/InconsistentLoopOrientation.ql index 7eea9dbbeab..fa5051ed5c7 100644 --- a/ql/src/InconsistentCode/InconsistentLoopOrientation.ql +++ b/ql/src/InconsistentCode/InconsistentLoopOrientation.ql @@ -36,7 +36,7 @@ predicate bounds(RelationalComparisonExpr test, Variable v, string direction) { * downward. */ predicate updates(IncDecStmt upd, Variable v, string direction) { - upd.getExpr() = v.getAReference() and + upd.getOperand() = v.getAReference() and ( upd instanceof IncStmt and direction = "upward" or diff --git a/ql/src/semmle/go/Expr.qll b/ql/src/semmle/go/Expr.qll index 2ccd8f8f565..fa8de1369d3 100644 --- a/ql/src/semmle/go/Expr.qll +++ b/ql/src/semmle/go/Expr.qll @@ -1253,7 +1253,7 @@ class ReferenceExpr extends Expr { predicate isLvalue() { this = any(Assignment assgn).getLhs(_) or - this = any(IncDecStmt ids).getExpr() + this = any(IncDecStmt ids).getOperand() or exists(RangeStmt rs | this = rs.getKey() or @@ -1271,7 +1271,7 @@ class ReferenceExpr extends Expr { or this = any(CompoundAssignStmt cmp).getLhs(_) or - this = any(IncDecStmt ids).getExpr() + this = any(IncDecStmt ids).getOperand() } } diff --git a/ql/src/semmle/go/Stmt.qll b/ql/src/semmle/go/Stmt.qll index a0d84869556..7a1a951c974 100644 --- a/ql/src/semmle/go/Stmt.qll +++ b/ql/src/semmle/go/Stmt.qll @@ -103,8 +103,8 @@ class SendStmt extends @sendstmt, Stmt { * An increment or decrement statement. */ class IncDecStmt extends @incdecstmt, Stmt { - /** Gets the expression. */ - Expr getExpr() { result = getChildExpr(0) } + /** Gets the expression being incremented or decremented. */ + Expr getOperand() { result = getChildExpr(0) } /** Gets the increment or decrement operator. */ string getOperator() { none() } diff --git a/ql/src/semmle/go/controlflow/ControlFlowGraphImpl.qll b/ql/src/semmle/go/controlflow/ControlFlowGraphImpl.qll index f02df29cc8a..d86f717dbc9 100644 --- a/ql/src/semmle/go/controlflow/ControlFlowGraphImpl.qll +++ b/ql/src/semmle/go/controlflow/ControlFlowGraphImpl.qll @@ -309,7 +309,7 @@ newtype TWriteTarget = ) ) or - exists(IncDecStmt ids | write = MkIncDecNode(ids) | lhs = ids.getExpr().stripParens()) + exists(IncDecStmt ids | write = MkIncDecNode(ids) | lhs = ids.getOperand().stripParens()) or exists(ParameterOrReceiver parm | write = MkParameterInit(parm) | lhs = parm.getDeclaration()) or @@ -1385,7 +1385,7 @@ module CFG { } private class IncDecTree extends ControlFlowTree, IncDecStmt { - override predicate firstNode(ControlFlow::Node first) { firstNode(getExpr(), first) } + override predicate firstNode(ControlFlow::Node first) { firstNode(getOperand(), first) } override predicate lastNode(ControlFlow::Node last, Completion cmpl) { ControlFlowTree.super.lastNode(last, cmpl) @@ -1395,7 +1395,7 @@ module CFG { } override predicate succ(ControlFlow::Node pred, ControlFlow::Node succ) { - lastNode(getExpr(), pred, normalCompletion()) and + lastNode(getOperand(), pred, normalCompletion()) and succ = MkImplicitOne(this) or pred = MkImplicitOne(this) and diff --git a/ql/src/semmle/go/controlflow/IR.qll b/ql/src/semmle/go/controlflow/IR.qll index bca948f9f7d..b3c655b12b7 100644 --- a/ql/src/semmle/go/controlflow/IR.qll +++ b/ql/src/semmle/go/controlflow/IR.qll @@ -843,7 +843,7 @@ module IR { /** Gets the corresponding increment or decrement statement. */ IncDecStmt getStmt() { result = ids } - override Type getResultType() { result = ids.getExpr().getType() } + override Type getResultType() { result = ids.getOperand().getType() } override ControlFlow::Root getRoot() { result.isRootOf(ids) } @@ -867,7 +867,7 @@ module IR { /** Gets the corresponding increment or decrement statement. */ IncDecStmt getStmt() { result = ids } - override Type getResultType() { result = ids.getExpr().getType() } + override Type getResultType() { result = ids.getOperand().getType() } override ControlFlow::Root getRoot() { result.isRootOf(ids) } diff --git a/ql/src/semmle/go/dataflow/internal/DataFlowUtil.qll b/ql/src/semmle/go/dataflow/internal/DataFlowUtil.qll index 466a18d8b04..4bdf572789f 100644 --- a/ql/src/semmle/go/dataflow/internal/DataFlowUtil.qll +++ b/ql/src/semmle/go/dataflow/internal/DataFlowUtil.qll @@ -536,7 +536,7 @@ class BinaryOperationNode extends Node { exists(IR::EvalIncDecRhsInstruction rhs, IncDecStmt ids | rhs = asInstruction() and ids = rhs.getStmt() | - left = exprNode(ids.getExpr()) and + left = exprNode(ids.getOperand()) and right = instructionNode(any(IR::EvalImplicitOneInstruction one | one.getStmt() = ids)) and op = ids.getOperator().charAt(0) )