diff --git a/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll index be489e515b4..d2ce186390a 100644 --- a/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll +++ b/go/ql/lib/semmle/go/controlflow/ControlFlowGraphShared.qll @@ -354,7 +354,15 @@ module GoCfg { ) } - predicate preOrderExpr(Ast::Expr e) { none() } + predicate preOrderExpr(Ast::Expr e) { + // The call of a `defer` statement is not invoked at the statement + // itself; its callee and arguments are evaluated in place, but the call + // is only invoked later, at function exit (modelled by the `defer-invoke` + // node and `deferExitStep`). Marking it as pre-order means no in-order + // "invocation" node (and hence no inline exceptional-exit edge) is + // created at the `defer` statement. + e = any(Go::DeferStmt s).getCall() + } predicate propagatesValue(Ast::AstNode child, Ast::AstNode parent) { child = parent.(Go::ParenExpr).getExpr() diff --git a/go/ql/lib/semmle/go/controlflow/IR.qll b/go/ql/lib/semmle/go/controlflow/IR.qll index 2181a1d9f29..bcac56c6253 100644 --- a/go/ql/lib/semmle/go/controlflow/IR.qll +++ b/go/ql/lib/semmle/go/controlflow/IR.qll @@ -218,6 +218,11 @@ module IR { EvalInstruction() { this.isIn(e) or + // The call of a `defer` statement is pre-order (it has no in-order + // "invocation" node at the statement), so its value is produced by the + // `defer-invoke` node that models the call at function exit. + this.isAdditional(e, "defer-invoke") + or // `NotExpr` and `LogicalBinaryExpr` are not in `postOrInOrder`, so they // don't have an `isIn` node. Only use the after-node when the // expression is not in a conditional context; otherwise the value is