]> granicus.if.org Git - clang/commitdiff
Re-applying r214962.
authorManuel Klimek <klimek@google.com>
Thu, 7 Aug 2014 10:42:17 +0000 (10:42 +0000)
committerManuel Klimek <klimek@google.com>
Thu, 7 Aug 2014 10:42:17 +0000 (10:42 +0000)
Changes to the original patch:
- model the CFG for temporary destructors in conditional operators so that
  the destructors of the true and false branch are always exclusive. This
  is necessary because we must not have impossible paths for the path
  based analysis to work.
- add multiple regression tests with ternary operators

Original description:
Fix modelling of non-lifetime-extended temporary destructors in the
analyzer.

Changes to the CFG:
When creating the CFG for temporary destructors, we create a structure
that mirrors the branch structure of the conditionally executed
temporary constructors in a full expression.
The branches we create use a CXXBindTemporaryExpr as terminator which
corresponds to the temporary constructor which must have been executed
to enter the destruction branch.

2. Changes to the Analyzer:
When we visit a CXXBindTemporaryExpr we mark the CXXBindTemporaryExpr as
executed in the state; when we reach a branch that contains the
corresponding CXXBindTemporaryExpr as terminator, we branch out
depending on whether the corresponding CXXBindTemporaryExpr was marked
as executed.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@215096 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
include/clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h
lib/Analysis/CFG.cpp
lib/StaticAnalyzer/Core/CoreEngine.cpp
lib/StaticAnalyzer/Core/ExprEngine.cpp
test/Analysis/temp-obj-dtors-cfg-output.cpp
test/Analysis/temporaries.cpp
test/SemaCXX/return-noreturn.cpp

index 76ace6d7cc2aa199fe2a53f51cd189e6e00eca16..67ff910f176e239150bb6dff5b1076c707cb5df1 100644 (file)
@@ -95,6 +95,8 @@ private:
 
   void HandleBranch(const Stmt *Cond, const Stmt *Term, const CFGBlock *B,
                     ExplodedNode *Pred);
+  void HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
+                                    const CFGBlock *B, ExplodedNode *Pred);
 
   /// Handle conditional logic for running static initializers.
   void HandleStaticInit(const DeclStmt *DS, const CFGBlock *B,
index 0fb4a245916f5c4488a98be7602182c6cb64da40..4929b11428bf33a376db8be3bb8a1cf881aad1ec 100644 (file)
@@ -227,6 +227,15 @@ public:
                      const CFGBlock *DstT,
                      const CFGBlock *DstF) override;
 
+  /// Called by CoreEngine.
+  /// Used to generate successor nodes for temporary destructors depending
+  /// on whether the corresponding constructor was visited.
+  void processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
+                                     NodeBuilderContext &BldCtx,
+                                     ExplodedNode *Pred, ExplodedNodeSet &Dst,
+                                     const CFGBlock *DstT,
+                                     const CFGBlock *DstF) override;
+
   /// Called by CoreEngine.  Used to processing branching behavior
   /// at static initalizers.
   void processStaticInitializer(const DeclStmt *DS,
@@ -408,7 +417,11 @@ public:
   void VisitIncrementDecrementOperator(const UnaryOperator* U,
                                        ExplodedNode *Pred,
                                        ExplodedNodeSet &Dst);
-  
+
+  void VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE,
+                                 ExplodedNodeSet &PreVisit,
+                                 ExplodedNodeSet &Dst);
+
   void VisitCXXCatchStmt(const CXXCatchStmt *CS, ExplodedNode *Pred,
                          ExplodedNodeSet &Dst);
 
index 3482e8d27dbb4e5094fde8a2dbdb5cf77fc63fd7..fd6673301f7f11ebb7ec1df06157a278e421d701 100644 (file)
@@ -72,6 +72,16 @@ public:
                              const CFGBlock *DstT,
                              const CFGBlock *DstF) = 0;
 
+  /// Called by CoreEngine.
+  /// Used to generate successor nodes for temporary destructors depending
+  /// on whether the corresponding constructor was visited.
+  virtual void processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
+                                             NodeBuilderContext &BldCtx,
+                                             ExplodedNode *Pred,
+                                             ExplodedNodeSet &Dst,
+                                             const CFGBlock *DstT,
+                                             const CFGBlock *DstF) = 0;
+
   /// Called by CoreEngine.  Used to processing branching behavior
   /// at static initalizers.
   virtual void processStaticInitializer(const DeclStmt *DS,
index b949c9ea5904fd6e1f33c21f10caaa9561e517ff..20438b478779fc364598786d70341a444c8964e5 100644 (file)
@@ -300,7 +300,7 @@ class CFGBuilder {
   CFGBlock *SwitchTerminatedBlock;
   CFGBlock *DefaultCaseBlock;
   CFGBlock *TryTerminatedBlock;
-  
+
   // Current position in local scope.
   LocalScope::const_iterator ScopePos;
 
@@ -410,16 +410,76 @@ private:
   CFGBlock *VisitChildren(Stmt *S);
   CFGBlock *VisitNoRecurse(Expr *E, AddStmtChoice asc);
 
+  /// When creating the CFG for temporary destructors, we want to mirror the
+  /// branch structure of the corresponding constructor calls.
+  /// Thus, while visiting a statement for temporary destructors, we keep a
+  /// context to keep track of the following information:
+  /// - whether a subexpression is executed unconditionally
+  /// - if a subexpression is executed conditionally, the first
+  ///   CXXBindTemporaryExpr we encounter in that subexpression (which
+  ///   corresponds to the last temporary destructor we have to call for this
+  ///   subexpression) and the CFG block at that point (which will become the
+  ///   successor block when inserting the decision point).
+  ///
+  /// That way, we can build the branch structure for temporary destructors as
+  /// follows:
+  /// 1. If a subexpression is executed unconditionally, we add the temporary
+  ///    destructor calls to the current block.
+  /// 2. If a subexpression is executed conditionally, when we encounter a
+  ///    CXXBindTemporaryExpr:
+  ///    a) If it is the first temporary destructor call in the subexpression,
+  ///       we remember the CXXBindTemporaryExpr and the current block in the
+  ///       TempDtorContext; we start a new block, and insert the temporary
+  ///       destructor call.
+  ///    b) Otherwise, add the temporary destructor call to the current block.
+  ///  3. When we finished visiting a conditionally executed subexpression,
+  ///     and we found at least one temporary constructor during the visitation
+  ///     (2.a has executed), we insert a decision block that uses the
+  ///     CXXBindTemporaryExpr as terminator, and branches to the current block
+  ///     if the CXXBindTemporaryExpr was marked executed, and otherwise
+  ///     branches to the stored successor.
+  struct TempDtorContext {
+    TempDtorContext(bool IsConditional)
+        : IsConditional(IsConditional),
+          Succ(nullptr),
+          TerminatorExpr(nullptr) {}
+
+    /// Returns whether we need to start a new branch for a temporary destructor
+    /// call. This is the case when the the temporary destructor is
+    /// conditionally executed, and it is the first one we encounter while
+    /// visiting a subexpression - other temporary destructors at the same level
+    /// will be added to the same block and are executed under the same
+    /// condition.
+    bool needsTempDtorBranch() const {
+      return IsConditional && !TerminatorExpr;
+    }
+
+    /// Remember the successor S of a temporary destructor decision branch for
+    /// the corresponding CXXBindTemporaryExpr E.
+    void setDecisionPoint(CFGBlock *S, CXXBindTemporaryExpr *E) {
+      Succ = S;
+      TerminatorExpr = E;
+    }
+
+    const bool IsConditional;
+    CFGBlock *Succ;
+    CXXBindTemporaryExpr *TerminatorExpr;
+  };
+
   // Visitors to walk an AST and generate destructors of temporaries in
   // full expression.
-  CFGBlock *VisitForTemporaryDtors(Stmt *E, bool BindToTemporary = false);
-  CFGBlock *VisitChildrenForTemporaryDtors(Stmt *E);
-  CFGBlock *VisitBinaryOperatorForTemporaryDtors(BinaryOperator *E);
-  CFGBlock *VisitCXXBindTemporaryExprForTemporaryDtors(CXXBindTemporaryExpr *E,
-      bool BindToTemporary);
-  CFGBlock *
-  VisitConditionalOperatorForTemporaryDtors(AbstractConditionalOperator *E,
-                                            bool BindToTemporary);
+  CFGBlock *VisitForTemporaryDtors(Stmt *E, bool BindToTemporary,
+                                   TempDtorContext &Context);
+  CFGBlock *VisitChildrenForTemporaryDtors(Stmt *E, TempDtorContext &Context);
+  CFGBlock *VisitBinaryOperatorForTemporaryDtors(BinaryOperator *E,
+                                                 TempDtorContext &Context);
+  CFGBlock *VisitCXXBindTemporaryExprForTemporaryDtors(
+      CXXBindTemporaryExpr *E, bool BindToTemporary, TempDtorContext &Context);
+  CFGBlock *VisitConditionalOperatorForTemporaryDtors(
+      AbstractConditionalOperator *E, bool BindToTemporary,
+      TempDtorContext &Context);
+  void InsertTempDtorDecisionBlock(const TempDtorContext &Context,
+                                   CFGBlock *FalseSucc = nullptr);
 
   // NYS == Not Yet Supported
   CFGBlock *NYS() {
@@ -1010,7 +1070,9 @@ CFGBlock *CFGBuilder::addInitializer(CXXCtorInitializer *I) {
 
     if (BuildOpts.AddTemporaryDtors && HasTemporaries) {
       // Generate destructors for temporaries in initialization expression.
-      VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr());
+      TempDtorContext Context(/*IsConditional=*/false);
+      VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr(),
+                             /*BindToTemporary=*/false, Context);
     }
   }
 
@@ -1967,7 +2029,9 @@ CFGBlock *CFGBuilder::VisitDeclSubExpr(DeclStmt *DS) {
 
     if (BuildOpts.AddTemporaryDtors && HasTemporaries) {
       // Generate destructors for temporaries in initialization expression.
-      VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr());
+      TempDtorContext Context(/*IsConditional=*/false);
+      VisitForTemporaryDtors(cast<ExprWithCleanups>(Init)->getSubExpr(),
+                             /*BindToTemporary=*/false, Context);
     }
   }
 
@@ -3347,7 +3411,8 @@ CFGBlock *CFGBuilder::VisitExprWithCleanups(ExprWithCleanups *E,
   if (BuildOpts.AddTemporaryDtors) {
     // If adding implicit destructors visit the full expression for adding
     // destructors of temporaries.
-    VisitForTemporaryDtors(E->getSubExpr());
+    TempDtorContext Context(/*IsConditional=*/false);
+    VisitForTemporaryDtors(E->getSubExpr(), false, Context);
 
     // Full expression has to be added as CFGStmt so it will be sequenced
     // before destructors of it's temporaries.
@@ -3456,7 +3521,8 @@ CFGBlock *CFGBuilder::VisitIndirectGotoStmt(IndirectGotoStmt *I) {
   return addStmt(I->getTarget());
 }
 
-CFGBlock *CFGBuilder::VisitForTemporaryDtors(Stmt *E, bool BindToTemporary) {
+CFGBlock *CFGBuilder::VisitForTemporaryDtors(Stmt *E, bool BindToTemporary,
+                                             TempDtorContext &Context) {
   assert(BuildOpts.AddImplicitDtors && BuildOpts.AddTemporaryDtors);
 
 tryAgain:
@@ -3466,19 +3532,20 @@ tryAgain:
   }
   switch (E->getStmtClass()) {
     default:
-      return VisitChildrenForTemporaryDtors(E);
+      return VisitChildrenForTemporaryDtors(E, Context);
 
     case Stmt::BinaryOperatorClass:
-      return VisitBinaryOperatorForTemporaryDtors(cast<BinaryOperator>(E));
+      return VisitBinaryOperatorForTemporaryDtors(cast<BinaryOperator>(E),
+                                                  Context);
 
     case Stmt::CXXBindTemporaryExprClass:
       return VisitCXXBindTemporaryExprForTemporaryDtors(
-          cast<CXXBindTemporaryExpr>(E), BindToTemporary);
+          cast<CXXBindTemporaryExpr>(E), BindToTemporary, Context);
 
     case Stmt::BinaryConditionalOperatorClass:
     case Stmt::ConditionalOperatorClass:
       return VisitConditionalOperatorForTemporaryDtors(
-          cast<AbstractConditionalOperator>(E), BindToTemporary);
+          cast<AbstractConditionalOperator>(E), BindToTemporary, Context);
 
     case Stmt::ImplicitCastExprClass:
       // For implicit cast we want BindToTemporary to be passed further.
@@ -3507,7 +3574,7 @@ tryAgain:
       // Visit the skipped comma operator left-hand sides for other temporaries.
       for (const Expr *CommaLHS : CommaLHSs) {
         VisitForTemporaryDtors(const_cast<Expr *>(CommaLHS),
-                               /*BindToTemporary=*/false);
+                               /*BindToTemporary=*/false, Context);
       }
       goto tryAgain;
     }
@@ -3523,7 +3590,8 @@ tryAgain:
       auto *LE = cast<LambdaExpr>(E);
       CFGBlock *B = Block;
       for (Expr *Init : LE->capture_inits()) {
-        if (CFGBlock *R = VisitForTemporaryDtors(Init))
+        if (CFGBlock *R = VisitForTemporaryDtors(
+                Init, /*BindToTemporary=*/false, Context))
           B = R;
       }
       return B;
@@ -3539,7 +3607,13 @@ tryAgain:
   }
 }
 
-CFGBlock *CFGBuilder::VisitChildrenForTemporaryDtors(Stmt *E) {
+CFGBlock *CFGBuilder::VisitChildrenForTemporaryDtors(Stmt *E,
+                                                     TempDtorContext &Context) {
+  if (isa<LambdaExpr>(E)) {
+    // Do not visit the children of lambdas; they have their own CFGs.
+    return Block;
+  }
+
   // When visiting children for destructors we want to visit them in reverse
   // order that they will appear in the CFG.  Because the CFG is built
   // bottom-up, this means we visit them in their natural order, which
@@ -3547,165 +3621,114 @@ CFGBlock *CFGBuilder::VisitChildrenForTemporaryDtors(Stmt *E) {
   CFGBlock *B = Block;
   for (Stmt::child_range I = E->children(); I; ++I) {
     if (Stmt *Child = *I)
-      if (CFGBlock *R = VisitForTemporaryDtors(Child))
+      if (CFGBlock *R = VisitForTemporaryDtors(Child, false, Context))
         B = R;
   }
   return B;
 }
 
-CFGBlock *CFGBuilder::VisitBinaryOperatorForTemporaryDtors(BinaryOperator *E) {
+CFGBlock *CFGBuilder::VisitBinaryOperatorForTemporaryDtors(
+    BinaryOperator *E, TempDtorContext &Context) {
   if (E->isLogicalOp()) {
-    // Destructors for temporaries in LHS expression should be called after
-    // those for RHS expression. Even if this will unnecessarily create a block,
-    // this block will be used at least by the full expression.
-    autoCreateBlock();
-    CFGBlock *ConfluenceBlock = VisitForTemporaryDtors(E->getLHS());
-    if (badCFG)
-      return nullptr;
-
-    Succ = ConfluenceBlock;
-    Block = nullptr;
-    CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS());
-
-    if (RHSBlock) {
-      if (badCFG)
-        return nullptr;
-
-      // If RHS expression did produce destructors we need to connect created
-      // blocks to CFG in same manner as for binary operator itself.
-      CFGBlock *LHSBlock = createBlock(false);
-      LHSBlock->setTerminator(CFGTerminator(E, true));
-
-      // For binary operator LHS block is before RHS in list of predecessors
-      // of ConfluenceBlock.
-      std::reverse(ConfluenceBlock->pred_begin(),
-          ConfluenceBlock->pred_end());
-
-      // See if this is a known constant.
-      TryResult KnownVal = tryEvaluateBool(E->getLHS());
-      if (KnownVal.isKnown() && (E->getOpcode() == BO_LOr))
-        KnownVal.negate();
-
-      // Link LHSBlock with RHSBlock exactly the same way as for binary operator
-      // itself.
-      if (E->getOpcode() == BO_LOr) {
-        addSuccessor(LHSBlock, KnownVal.isTrue() ? nullptr : ConfluenceBlock);
-        addSuccessor(LHSBlock, KnownVal.isFalse() ? nullptr : RHSBlock);
-      } else {
-        assert (E->getOpcode() == BO_LAnd);
-        addSuccessor(LHSBlock, KnownVal.isFalse() ? nullptr : RHSBlock);
-        addSuccessor(LHSBlock, KnownVal.isTrue() ? nullptr : ConfluenceBlock);
-      }
-
-      Block = LHSBlock;
-      return LHSBlock;
-    }
-
-    Block = ConfluenceBlock;
-    return ConfluenceBlock;
+    VisitForTemporaryDtors(E->getLHS(), false, Context);
+    // We do not know at CFG-construction time whether the right-hand-side was
+    // executed, thus we add a branch node that depends on the temporary
+    // constructor call.
+    TempDtorContext RHSContext(/*IsConditional=*/true);
+    VisitForTemporaryDtors(E->getRHS(), false, RHSContext);
+    InsertTempDtorDecisionBlock(RHSContext);
+    return Block;
   }
 
   if (E->isAssignmentOp()) {
     // For assignment operator (=) LHS expression is visited
     // before RHS expression. For destructors visit them in reverse order.
-    CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS());
-    CFGBlock *LHSBlock = VisitForTemporaryDtors(E->getLHS());
+    CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS(), false, Context);
+    CFGBlock *LHSBlock = VisitForTemporaryDtors(E->getLHS(), false, Context);
     return LHSBlock ? LHSBlock : RHSBlock;
   }
 
   // For any other binary operator RHS expression is visited before
   // LHS expression (order of children). For destructors visit them in reverse
   // order.
-  CFGBlock *LHSBlock = VisitForTemporaryDtors(E->getLHS());
-  CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS());
+  CFGBlock *LHSBlock = VisitForTemporaryDtors(E->getLHS(), false, Context);
+  CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getRHS(), false, Context);
   return RHSBlock ? RHSBlock : LHSBlock;
 }
 
 CFGBlock *CFGBuilder::VisitCXXBindTemporaryExprForTemporaryDtors(
-    CXXBindTemporaryExpr *E, bool BindToTemporary) {
+    CXXBindTemporaryExpr *E, bool BindToTemporary, TempDtorContext &Context) {
   // First add destructors for temporaries in subexpression.
-  CFGBlock *B = VisitForTemporaryDtors(E->getSubExpr());
+  CFGBlock *B = VisitForTemporaryDtors(E->getSubExpr(), false, Context);
   if (!BindToTemporary) {
     // If lifetime of temporary is not prolonged (by assigning to constant
     // reference) add destructor for it.
 
-    // If the destructor is marked as a no-return destructor, we need to create
-    // a new block for the destructor which does not have as a successor
-    // anything built thus far. Control won't flow out of this block.
     const CXXDestructorDecl *Dtor = E->getTemporary()->getDestructor();
+
     if (Dtor->isNoReturn()) {
-      Succ = B;
+      // If the destructor is marked as a no-return destructor, we need to
+      // create a new block for the destructor which does not have as a
+      // successor anything built thus far. Control won't flow out of this
+      // block.
+      if (B) Succ = B;
       Block = createNoReturnBlock();
+    } else if (Context.needsTempDtorBranch()) {
+      // If we need to introduce a branch, we add a new block that we will hook
+      // up to a decision block later.
+      if (B) Succ = B;
+      Block = createBlock();
     } else {
       autoCreateBlock();
     }
-
+    if (Context.needsTempDtorBranch()) {
+      Context.setDecisionPoint(Succ, E);
+    }
     appendTemporaryDtor(Block, E);
+
     B = Block;
   }
   return B;
 }
 
-CFGBlock *CFGBuilder::VisitConditionalOperatorForTemporaryDtors(
-    AbstractConditionalOperator *E, bool BindToTemporary) {
-  // First add destructors for condition expression.  Even if this will
-  // unnecessarily create a block, this block will be used at least by the full
-  // expression.
-  autoCreateBlock();
-  CFGBlock *ConfluenceBlock = VisitForTemporaryDtors(E->getCond());
-  if (badCFG)
-    return nullptr;
-  if (BinaryConditionalOperator *BCO
-        = dyn_cast<BinaryConditionalOperator>(E)) {
-    ConfluenceBlock = VisitForTemporaryDtors(BCO->getCommon());
-    if (badCFG)
-      return nullptr;
-  }
-
-  // Try to add block with destructors for LHS expression.
-  CFGBlock *LHSBlock = nullptr;
-  Succ = ConfluenceBlock;
-  Block = nullptr;
-  LHSBlock = VisitForTemporaryDtors(E->getTrueExpr(), BindToTemporary);
-  if (badCFG)
-    return nullptr;
-
-  // Try to add block with destructors for RHS expression;
-  Succ = ConfluenceBlock;
-  Block = nullptr;
-  CFGBlock *RHSBlock = VisitForTemporaryDtors(E->getFalseExpr(),
-                                              BindToTemporary);
-  if (badCFG)
-    return nullptr;
-
-  if (!RHSBlock && !LHSBlock) {
-    // If neither LHS nor RHS expression had temporaries to destroy don't create
-    // more blocks.
-    Block = ConfluenceBlock;
-    return Block;
+void CFGBuilder::InsertTempDtorDecisionBlock(const TempDtorContext &Context,
+                                             CFGBlock *FalseSucc) {
+  if (!Context.TerminatorExpr) {
+    // If no temporary was found, we do not need to insert a decision point.
+    return;
   }
+  assert(Context.TerminatorExpr);
+  CFGBlock *Decision = createBlock(false);
+  Decision->setTerminator(CFGTerminator(Context.TerminatorExpr, true));
+  addSuccessor(Decision, Block);
+  addSuccessor(Decision, FalseSucc ? FalseSucc : Context.Succ);
+  Block = Decision;
+}
 
-  Block = createBlock(false);
-  Block->setTerminator(CFGTerminator(E, true));
-  assert(Block->getTerminator().isTemporaryDtorsBranch());
-
-  // See if this is a known constant.
-  const TryResult &KnownVal = tryEvaluateBool(E->getCond());
-
-  if (LHSBlock) {
-    addSuccessor(Block, LHSBlock, !KnownVal.isFalse());
-  } else if (KnownVal.isFalse()) {
-    addSuccessor(Block, nullptr);
+CFGBlock *CFGBuilder::VisitConditionalOperatorForTemporaryDtors(
+    AbstractConditionalOperator *E, bool BindToTemporary,
+    TempDtorContext &Context) {
+  VisitForTemporaryDtors(E->getCond(), false, Context);
+  CFGBlock *ConditionBlock = Block;
+  CFGBlock *ConditionSucc = Succ;
+
+  TempDtorContext TrueContext(/*IsConditional=*/true);
+  VisitForTemporaryDtors(E->getTrueExpr(), BindToTemporary, TrueContext);
+  CFGBlock *TrueBlock = Block;
+
+  Block = ConditionBlock;
+  Succ = ConditionSucc;
+  TempDtorContext FalseContext(/*IsConditional=*/true);
+  VisitForTemporaryDtors(E->getFalseExpr(), BindToTemporary, FalseContext);
+
+  if (TrueContext.TerminatorExpr && FalseContext.TerminatorExpr) {
+    InsertTempDtorDecisionBlock(FalseContext, TrueBlock);
+  } else if (TrueContext.TerminatorExpr) {
+    Block = TrueBlock;
+    InsertTempDtorDecisionBlock(TrueContext);
   } else {
-    addSuccessor(Block, ConfluenceBlock);
-    std::reverse(ConfluenceBlock->pred_begin(), ConfluenceBlock->pred_end());
+    InsertTempDtorDecisionBlock(FalseContext);
   }
-
-  if (!RHSBlock)
-    RHSBlock = ConfluenceBlock;
-
-  addSuccessor(Block, RHSBlock, !KnownVal.isTrue());
-
   return Block;
 }
 
index 4623c358a9e2c980ad8112d43569f6a5adcfffa5..2a7c8e170d82c35a0c52d29aadf32e29425e274b 100644 (file)
@@ -14,6 +14,7 @@
 
 #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h"
 #include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
 #include "clang/AST/StmtCXX.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
@@ -346,6 +347,11 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
       default:
         llvm_unreachable("Analysis for this terminator not implemented.");
 
+      case Stmt::CXXBindTemporaryExprClass:
+        HandleCleanupTemporaryBranch(
+            cast<CXXBindTemporaryExpr>(B->getTerminator().getStmt()), B, Pred);
+        return;
+
       // Model static initializers.
       case Stmt::DeclStmtClass:
         HandleStaticInit(cast<DeclStmt>(Term), B, Pred);
@@ -461,6 +467,17 @@ void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
   enqueue(Dst);
 }
 
+void CoreEngine::HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
+                                              const CFGBlock *B,
+                                              ExplodedNode *Pred) {
+  assert(B->succ_size() == 2);
+  NodeBuilderContext Ctx(*this, B, Pred);
+  ExplodedNodeSet Dst;
+  SubEng.processCleanupTemporaryBranch(BTE, Ctx, Pred, Dst, *(B->succ_begin()),
+                                       *(B->succ_begin() + 1));
+  // Enqueue the new frontier onto the worklist.
+  enqueue(Dst);
+}
 
 void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B,
                                   ExplodedNode *Pred) {
index f4636ef18aaed187860d05abccc9b83034ddefa5..b30a4417e9a96e5817445bb67183e9a4bc266f8d 100644 (file)
@@ -51,6 +51,15 @@ STATISTIC(NumMaxBlockCountReachedInInlined,
 STATISTIC(NumTimesRetriedWithoutInlining,
             "The # of times we re-evaluated a call without inlining");
 
+typedef std::pair<const CXXBindTemporaryExpr *, const StackFrameContext *>
+    CXXBindTemporaryContext;
+
+// Keeps track of whether CXXBindTemporaryExpr nodes have been evaluated.
+// The StackFrameContext assures that nested calls due to inlined recursive
+// functions do not interfere.
+REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedTemporariesSet,
+                                 llvm::ImmutableSet<CXXBindTemporaryContext>)
+
 //===----------------------------------------------------------------------===//
 // Engine construction and deletion.
 //===----------------------------------------------------------------------===//
@@ -659,13 +668,59 @@ void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
 void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
                                       ExplodedNode *Pred,
                                       ExplodedNodeSet &Dst) {
+  ExplodedNodeSet CleanDtorState;
+  StmtNodeBuilder StmtBldr(Pred, CleanDtorState, *currBldrCtx);
+  ProgramStateRef State = Pred->getState();
+  assert(State->contains<InitializedTemporariesSet>(
+      std::make_pair(D.getBindTemporaryExpr(), Pred->getStackFrame())));
+  State = State->remove<InitializedTemporariesSet>(
+      std::make_pair(D.getBindTemporaryExpr(), Pred->getStackFrame()));
+  StmtBldr.generateNode(D.getBindTemporaryExpr(), Pred, State);
 
   QualType varType = D.getBindTemporaryExpr()->getSubExpr()->getType();
-
-  // FIXME: Inlining of temporary destructors is not supported yet anyway, so we
-  // just put a NULL region for now. This will need to be changed later.
+  assert(CleanDtorState.size() == 1);
+  ExplodedNode *CleanPred = *CleanDtorState.begin();
+  // FIXME: Inlining of temporary destructors is not supported yet anyway, so
+  // we just put a NULL region for now. This will need to be changed later.
   VisitCXXDestructor(varType, nullptr, D.getBindTemporaryExpr(),
-                     /*IsBase=*/ false, Pred, Dst);
+                     /*IsBase=*/false, CleanPred, Dst);
+}
+
+void ExprEngine::processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
+                                               NodeBuilderContext &BldCtx,
+                                               ExplodedNode *Pred,
+                                               ExplodedNodeSet &Dst,
+                                               const CFGBlock *DstT,
+                                               const CFGBlock *DstF) {
+  BranchNodeBuilder TempDtorBuilder(Pred, Dst, BldCtx, DstT, DstF);
+  if (Pred->getState()->contains<InitializedTemporariesSet>(
+          std::make_pair(BTE, Pred->getStackFrame()))) {
+    TempDtorBuilder.markInfeasible(false);
+    TempDtorBuilder.generateNode(Pred->getState(), true, Pred);
+  } else {
+    TempDtorBuilder.markInfeasible(true);
+    TempDtorBuilder.generateNode(Pred->getState(), false, Pred);
+  }
+}
+
+void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE,
+                                           ExplodedNodeSet &PreVisit,
+                                           ExplodedNodeSet &Dst) {
+  if (!getAnalysisManager().options.includeTemporaryDtorsInCFG()) {
+    // In case we don't have temporary destructors in the CFG, do not mark
+    // the initialization - we would otherwise never clean it up.
+    Dst = PreVisit;
+    return;
+  }
+  StmtNodeBuilder StmtBldr(PreVisit, Dst, *currBldrCtx);
+  for (ExplodedNode *Node : PreVisit) {
+    ProgramStateRef State = Node->getState();
+    assert(!State->contains<InitializedTemporariesSet>(
+        std::make_pair(BTE, Node->getStackFrame())));
+    State = State->add<InitializedTemporariesSet>(
+        std::make_pair(BTE, Node->getStackFrame()));
+    StmtBldr.generateNode(BTE, Node, State);
+  }
 }
 
 void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
@@ -773,6 +828,17 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
       // Handled due to fully linearised CFG.
       break;
 
+    case Stmt::CXXBindTemporaryExprClass: {
+      Bldr.takeNodes(Pred);
+      ExplodedNodeSet PreVisit;
+      getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
+      ExplodedNodeSet Next;
+      VisitCXXBindTemporaryExpr(cast<CXXBindTemporaryExpr>(S), PreVisit, Next);
+      getCheckerManager().runCheckersForPostStmt(Dst, Next, S, *this);
+      Bldr.addNodes(Dst);
+      break;
+    }
+
     // Cases not handled yet; but will handle some day.
     case Stmt::DesignatedInitExprClass:
     case Stmt::ExtVectorElementExprClass:
@@ -810,7 +876,6 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
     case Stmt::SizeOfPackExprClass:
     case Stmt::StringLiteralClass:
     case Stmt::ObjCStringLiteralClass:
-    case Stmt::CXXBindTemporaryExprClass:
     case Stmt::CXXPseudoDestructorExprClass:
     case Stmt::SubstNonTypeTemplateParmExprClass:
     case Stmt::CXXNullPtrLiteralExprClass: {
@@ -1405,11 +1470,8 @@ static const Stmt *ResolveCondition(const Stmt *Condition,
   if (!BO || !BO->isLogicalOp())
     return Condition;
 
-  // FIXME: This is a workaround until we handle temporary destructor branches
-  // correctly; currently, temporary destructor branches lead to blocks that
-  // only have a terminator (and no statements). These blocks violate the
-  // invariant this function assumes.
-  if (B->getTerminator().isTemporaryDtorsBranch()) return Condition;
+  assert(!B->getTerminator().isTemporaryDtorsBranch() &&
+         "Temporary destructor branches handled by processBindTemporary.");
 
   // For logical operations, we still have the case where some branches
   // use the traditional "merge" approach and others sink the branch
@@ -1438,6 +1500,8 @@ void ExprEngine::processBranch(const Stmt *Condition, const Stmt *Term,
                                ExplodedNodeSet &Dst,
                                const CFGBlock *DstT,
                                const CFGBlock *DstF) {
+  assert((!Condition || !isa<CXXBindTemporaryExpr>(Condition)) &&
+         "CXXBindTemporaryExprs are handled by processBindTemporary.");
   const LocationContext *LCtx = Pred->getLocationContext();
   PrettyStackTraceLocationContext StackCrashInfo(LCtx);
   currBldrCtx = &BldCtx;
@@ -1601,10 +1665,29 @@ void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) {
     builder.generateNode(I, state);
 }
 
+#if 0
+static bool stackFrameDoesNotContainInitializedTemporaries(ExplodedNode &Pred) {
+  const StackFrameContext* Frame = Pred.getStackFrame();
+  const llvm::ImmutableSet<CXXBindTemporaryContext> &Set =
+      Pred.getState()->get<InitializedTemporariesSet>();
+  return std::find_if(Set.begin(), Set.end(),
+                      [&](const CXXBindTemporaryContext &Ctx) {
+                        if (Ctx.second == Frame) {
+                          Ctx.first->dump();
+                          llvm::errs() << "\n";
+                        }
+           return Ctx.second == Frame;
+         }) == Set.end();
+}
+#endif
+
 /// ProcessEndPath - Called by CoreEngine.  Used to generate end-of-path
 ///  nodes when the control reaches the end of a function.
 void ExprEngine::processEndOfFunction(NodeBuilderContext& BC,
                                       ExplodedNode *Pred) {
+  // FIXME: Assert that stackFrameDoesNotContainInitializedTemporaries(*Pred)).
+  // We currently cannot enable this assert, as lifetime extended temporaries
+  // are not modelled correctly.
   PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext());
   StateMgr.EndPath(Pred->getState());
 
index 5fb33d36b8552f833b9175e5e22a00371610a667..491c68e1bc8b08b09a76124ad268aad9fa9bd3be 100644 (file)
@@ -324,7 +324,7 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:   [B3]
 // CHECK:     1: [B5.8] && [B4.5]
 // CHECK:     2: [B5.3]([B3.1])
-// CHECK:     T: (Temp Dtor) [B5.8] && ...
+// CHECK:     T: (Temp Dtor) [B4.2]
 // CHECK:     Preds (2): B4 B5
 // CHECK:     Succs (2): B2 B1
 // CHECK:   [B4]
@@ -354,7 +354,7 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:   [B7]
 // CHECK:     1: [B9.5] && [B8.5]
 // CHECK:     2: bool a = A() && B();
-// CHECK:     T: (Temp Dtor) [B9.5] && ...
+// CHECK:     T: (Temp Dtor) [B8.2]
 // CHECK:     Preds (2): B8 B9
 // CHECK:     Succs (2): B6 B5
 // CHECK:   [B8]
@@ -390,9 +390,9 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:   [B3]
 // CHECK:     1: [B5.8] || [B4.5]
 // CHECK:     2: [B5.3]([B3.1])
-// CHECK:     T: (Temp Dtor) [B5.8] || ...
+// CHECK:     T: (Temp Dtor) [B4.2]
 // CHECK:     Preds (2): B4 B5
-// CHECK:     Succs (2): B1 B2
+// CHECK:     Succs (2): B2 B1
 // CHECK:   [B4]
 // CHECK:     1: B() (CXXConstructExpr, class B)
 // CHECK:     2: [B4.1] (BindTemporary)
@@ -420,9 +420,9 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:   [B7]
 // CHECK:     1: [B9.5] || [B8.5]
 // CHECK:     2: bool a = A() || B();
-// CHECK:     T: (Temp Dtor) [B9.5] || ...
+// CHECK:     T: (Temp Dtor) [B8.2]
 // CHECK:     Preds (2): B8 B9
-// CHECK:     Succs (2): B5 B6
+// CHECK:     Succs (2): B6 B5
 // CHECK:   [B8]
 // CHECK:     1: B() (CXXConstructExpr, class B)
 // CHECK:     2: [B8.1] (BindTemporary)
@@ -492,9 +492,9 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     3: [B7.2]
 // CHECK:     4: [B7.3] (CXXConstructExpr, class A)
 // CHECK:     5: A a = B() ? A() : A(B());
-// CHECK:     T: (Temp Dtor) [B10.5] ? ... : ...
+// CHECK:     T: (Temp Dtor) [B9.2]
 // CHECK:     Preds (2): B8 B9
-// CHECK:     Succs (2): B5 B6
+// CHECK:     Succs (2): B6 B5
 // CHECK:   [B8]
 // CHECK:     1: A() (CXXConstructExpr, class A)
 // CHECK:     2: [B8.1] (BindTemporary)
@@ -533,7 +533,6 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Succs (2): B8 B9
 // CHECK:   [B0 (EXIT)]
 // CHECK:     Preds (1): B1
-// CHECK:  C() : b_(true)
 // CHECK:   [B2 (ENTRY)]
 // CHECK:     Succs (1): B1
 // CHECK:   [B1]
@@ -543,12 +542,10 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Succs (1): B0
 // CHECK:   [B0 (EXIT)]
 // CHECK:     Preds (1): B1
-// CHECK:  ~C()
 // CHECK:   [B1 (ENTRY)]
 // CHECK:     Succs (1): B0
 // CHECK:   [B0 (EXIT)]
 // CHECK:     Preds (1): B1
-// CHECK:  operator bool()
 // CHECK:   [B2 (ENTRY)]
 // CHECK:     Succs (1): B1
 // CHECK:   [B1]
@@ -560,7 +557,6 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Succs (1): B0
 // CHECK:   [B0 (EXIT)]
 // CHECK:     Preds (1): B1
-// CHECK:  D() : b_(true)
 // CHECK:   [B2 (ENTRY)]
 // CHECK:     Succs (1): B1
 // CHECK:   [B1]
@@ -570,7 +566,6 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Succs (1): B0
 // CHECK:   [B0 (EXIT)]
 // CHECK:     Preds (1): B1
-// CHECK:  operator bool()
 // CHECK:   [B2 (ENTRY)]
 // CHECK:     Succs (1): B1
 // CHECK:   [B1]
@@ -582,7 +577,6 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Succs (1): B0
 // CHECK:   [B0 (EXIT)]
 // CHECK:     Preds (1): B1
-// CHECK:  int test_cond_unnamed_custom_destructor()
 // CHECK:   [B4 (ENTRY)]
 // CHECK:     Succs (1): B3
 // CHECK:   [B1]
@@ -607,7 +601,6 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Succs (2): B2 B1
 // CHECK:   [B0 (EXIT)]
 // CHECK:     Preds (2): B1 B2
-// CHECK:  int test_cond_named_custom_destructor()
 // CHECK:   [B5 (ENTRY)]
 // CHECK:     Succs (1): B4
 // CHECK:   [B1]
@@ -642,7 +635,6 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Succs (2): B3 B2
 // CHECK:   [B0 (EXIT)]
 // CHECK:     Preds (3): B1 B2 B3
-// CHECK:  int test_cond_unnamed_auto_destructor()
 // CHECK:   [B4 (ENTRY)]
 // CHECK:     Succs (1): B3
 // CHECK:   [B1]
@@ -665,7 +657,6 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Succs (2): B2 B1
 // CHECK:   [B0 (EXIT)]
 // CHECK:     Preds (2): B1 B2
-// CHECK:  int test_cond_named_auto_destructor()
 // CHECK:   [B4 (ENTRY)]
 // CHECK:     Succs (1): B3
 // CHECK:   [B1]
@@ -718,9 +709,9 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     2: [B4.1] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     3: [B4.2]
 // CHECK:     4: [B7.3]([B4.3])
-// CHECK:     T: (Temp Dtor) [B7.8] ? ... : ...
+// CHECK:     T: (Temp Dtor) [B6.2]
 // CHECK:     Preds (2): B5 B6
-// CHECK:     Succs (2): B2 B3
+// CHECK:     Succs (2): B3 B2
 // CHECK:   [B5]
 // CHECK:     1: A() (CXXConstructExpr, class A)
 // CHECK:     2: [B5.1] (BindTemporary)
@@ -775,9 +766,9 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     2: [B10.1] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     3: [B10.2]
 // CHECK:     4: const A &a = B() ? A() : A(B());
-// CHECK:     T: (Temp Dtor) [B13.5] ? ... : ...
+// CHECK:     T: (Temp Dtor) [B12.2]
 // CHECK:     Preds (2): B11 B12
-// CHECK:     Succs (2): B8 B9
+// CHECK:     Succs (2): B9 B8
 // CHECK:   [B11]
 // CHECK:     1: A() (CXXConstructExpr, class A)
 // CHECK:     2: [B11.1] (BindTemporary)
@@ -819,9 +810,8 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:   [B8 (ENTRY)]
 // CHECK:     Succs (1): B7
 // CHECK:   [B1]
-// CHECK:     1: ~A() (Temporary object destructor)
-// CHECK:     2: int b;
-// CHECK:     3: [B4.5].~A() (Implicit destructor)
+// CHECK:     1: int b;
+// CHECK:     2: [B4.5].~A() (Implicit destructor)
 // CHECK:     Preds (2): B2 B3
 // CHECK:     Succs (1): B0
 // CHECK:   [B2]
@@ -839,9 +829,9 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     3: [B4.2]
 // CHECK:     4: [B4.3] (CXXConstructExpr, class A)
 // CHECK:     5: A a = A() ?: A();
-// CHECK:     T: (Temp Dtor) [B7.5] ? ... : ...
+// CHECK:     T: (Temp Dtor) [B6.2]
 // CHECK:     Preds (2): B5 B6
-// CHECK:     Succs (2): B2 B3
+// CHECK:     Succs (2): B3 B2
 // CHECK:   [B5]
 // CHECK:     1: [B7.2] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     2: [B5.1]
@@ -872,9 +862,8 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:   [B13 (ENTRY)]
 // CHECK:     Succs (1): B12
 // CHECK:   [B1]
-// CHECK:     1: ~A() (Temporary object destructor)
-// CHECK:     2: int b;
-// CHECK:     3: [B9.4].~A() (Implicit destructor)
+// CHECK:     1: int b;
+// CHECK:     2: [B9.4].~A() (Implicit destructor)
 // CHECK:     Preds (2): B2 B3
 // CHECK:     Succs (1): B0
 // CHECK:   [B2]
@@ -887,15 +876,15 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Preds (1): B4
 // CHECK:     Succs (1): B1
 // CHECK:   [B4]
-// CHECK:     1: [B7.5] ?: [B6.6]
+// CHECK:     1: [B7.4] ?: [B6.6]
 // CHECK:     2: [B4.1] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     3: [B4.2]
-// CHECK:     4: [B7.3]([B4.3])
-// CHECK:     T: (Temp Dtor) [B7.8] ? ... : ...
+// CHECK:     4: [B7.2]([B4.3])
+// CHECK:     T: (Temp Dtor) [B6.2]
 // CHECK:     Preds (2): B5 B6
-// CHECK:     Succs (2): B2 B3
+// CHECK:     Succs (2): B3 B2
 // CHECK:   [B5]
-// CHECK:     1: [B7.5] (ImplicitCastExpr, NoOp, const class A)
+// CHECK:     1: [B7.4] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     2: [B5.1]
 // CHECK:     3: [B5.2] (CXXConstructExpr, class A)
 // CHECK:     4: [B5.3] (BindTemporary)
@@ -911,16 +900,15 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Preds (1): B7
 // CHECK:     Succs (1): B4
 // CHECK:   [B7]
-// CHECK:     1: ~A() (Temporary object destructor)
-// CHECK:     2: foo
-// CHECK:     3: [B7.2] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(const class A &))
-// CHECK:     4: A() (CXXConstructExpr, class A)
-// CHECK:     5: [B7.4] (BindTemporary)
-// CHECK:     6: [B7.5].operator bool
-// CHECK:     7: [B7.5]
-// CHECK:     8: [B7.7] (ImplicitCastExpr, UserDefinedConversion, _Bool)
-// CHECK:     T: [B7.8] ? ... : ...
-// CHECK:     Preds (2): B9 B8
+// CHECK:     1: foo
+// CHECK:     2: [B7.1] (ImplicitCastExpr, FunctionToPointerDecay, void (*)(const class A &))
+// CHECK:     3: A() (CXXConstructExpr, class A)
+// CHECK:     4: [B7.3] (BindTemporary)
+// CHECK:     5: [B7.4].operator bool
+// CHECK:     6: [B7.4]
+// CHECK:     7: [B7.6] (ImplicitCastExpr, UserDefinedConversion, _Bool)
+// CHECK:     T: [B7.7] ? ... : ...
+// CHECK:     Preds (2): B8 B9
 // CHECK:     Succs (2): B5 B6
 // CHECK:   [B8]
 // CHECK:     1: ~A() (Temporary object destructor)
@@ -931,9 +919,9 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     2: [B9.1] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     3: [B9.2]
 // CHECK:     4: const A &a = A() ?: A();
-// CHECK:     T: (Temp Dtor) [B12.5] ? ... : ...
+// CHECK:     T: (Temp Dtor) [B11.2]
 // CHECK:     Preds (2): B10 B11
-// CHECK:     Succs (2): B7 B8
+// CHECK:     Succs (2): B8 B7
 // CHECK:   [B10]
 // CHECK:     1: [B12.2] (ImplicitCastExpr, NoOp, const class A)
 // CHECK:     2: [B10.1]
@@ -1089,6 +1077,7 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Succs (1): B2
 // CHECK:   [B1]
 // CHECK:     1: int b;
+// CHECK:     Preds (1): B2(Unreachable)
 // CHECK:     Succs (1): B0
 // CHECK:   [B2 (NORETURN)]
 // CHECK:     1: int a;
@@ -1105,6 +1094,7 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Succs (1): B2
 // CHECK:   [B1]
 // CHECK:     1: int b;
+// CHECK:     Preds (1): B2(Unreachable)
 // CHECK:     Succs (1): B0
 // CHECK:   [B2 (NORETURN)]
 // CHECK:     1: int a;
@@ -1117,7 +1107,6 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Succs (1): B0
 // CHECK:   [B0 (EXIT)]
 // CHECK:     Preds (2): B1 B2
-// CHECK:  int testConsistencyNestedSimple(bool value)
 // CHECK:   [B9 (ENTRY)]
 // CHECK:     Succs (1): B8
 // CHECK:   [B1]
@@ -1132,7 +1121,7 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Succs (1): B0
 // CHECK:   [B3]
 // CHECK:     T: if [B5.1]
-// CHECK:     Preds (1): B5
+// CHECK:     Preds (2): B4(Unreachable) B5
 // CHECK:     Succs (2): B2 B1
 // CHECK:   [B4 (NORETURN)]
 // CHECK:     1: ~NoReturn() (Temporary object destructor)
@@ -1140,9 +1129,9 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Succs (1): B0
 // CHECK:   [B5]
 // CHECK:     1: [B7.3] || [B6.7]
-// CHECK:     T: (Temp Dtor) [B7.3] || ...
+// CHECK:     T: (Temp Dtor) [B6.4]
 // CHECK:     Preds (2): B6 B7
-// CHECK:     Succs (2): B3 B4
+// CHECK:     Succs (2): B4 B3
 // CHECK:   [B6]
 // CHECK:     1: check
 // CHECK:     2: [B6.1] (ImplicitCastExpr, FunctionToPointerDecay, _Bool (*)(const class NoReturn &))
@@ -1168,7 +1157,6 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Succs (2): B7 B1
 // CHECK:   [B0 (EXIT)]
 // CHECK:     Preds (3): B1 B2 B4
-// CHECK:  int testConsistencyNestedComplex(bool value)
 // CHECK:   [B10 (ENTRY)]
 // CHECK:     Succs (1): B9
 // CHECK:   [B1]
@@ -1183,7 +1171,7 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Succs (1): B0
 // CHECK:   [B3]
 // CHECK:     T: if [B5.1]
-// CHECK:     Preds (1): B5
+// CHECK:     Preds (2): B4(Unreachable) B5
 // CHECK:     Succs (2): B2 B1
 // CHECK:   [B4 (NORETURN)]
 // CHECK:     1: ~NoReturn() (Temporary object destructor)
@@ -1191,9 +1179,9 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Succs (1): B0
 // CHECK:   [B5]
 // CHECK:     1: [B8.3] || [B7.3] || [B6.7]
-// CHECK:     T: (Temp Dtor) [B8.3] || [B7.3] || ...
+// CHECK:     T: (Temp Dtor) [B6.4]
 // CHECK:     Preds (3): B6 B7 B8
-// CHECK:     Succs (2): B3 B4
+// CHECK:     Succs (2): B4 B3
 // CHECK:   [B6]
 // CHECK:     1: check
 // CHECK:     2: [B6.1] (ImplicitCastExpr, FunctionToPointerDecay, _Bool (*)(const class NoReturn &))
@@ -1226,7 +1214,6 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Succs (2): B8 B1
 // CHECK:   [B0 (EXIT)]
 // CHECK:     Preds (3): B1 B2 B4
-// CHECK:  int testConsistencyNestedNormalReturn(bool value)
 // CHECK:   [B10 (ENTRY)]
 // CHECK:     Succs (1): B9
 // CHECK:   [B1]
@@ -1241,7 +1228,7 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Succs (1): B0
 // CHECK:   [B3]
 // CHECK:     T: if [B5.1]
-// CHECK:     Preds (1): B5
+// CHECK:     Preds (2): B4(Unreachable) B5
 // CHECK:     Succs (2): B2 B1
 // CHECK:   [B4 (NORETURN)]
 // CHECK:     1: ~NoReturn() (Temporary object destructor)
@@ -1249,9 +1236,9 @@ int testConsistencyNestedNormalReturn(bool value) {
 // CHECK:     Succs (1): B0
 // CHECK:   [B5]
 // CHECK:     1: [B8.3] || [B7.2] || [B6.7]
-// CHECK:     T: (Temp Dtor) [B8.3] || [B7.2] || ...
+// CHECK:     T: (Temp Dtor) [B6.4]
 // CHECK:     Preds (3): B6 B7 B8
-// CHECK:     Succs (2): B3 B4
+// CHECK:     Succs (2): B4 B3
 // CHECK:   [B6]
 // CHECK:     1: check
 // CHECK:     2: [B6.1] (ImplicitCastExpr, FunctionToPointerDecay, _Bool (*)(const class NoReturn &))
index c57d984a1dc8458bd595e34eb9e14fddb2d5bacb..165e65a5659deb8470bbf433672a23283e95870f 100644 (file)
@@ -1,8 +1,9 @@
 // RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -w -std=c++03 %s
 // RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -verify -w -std=c++11 %s
-// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -DTEMPORARY_DTORS -verify -w -analyzer-config cfg-temporary-dtors=true %s 
+// RUN: %clang_cc1 -analyze -analyzer-checker=core,debug.ExprInspection -DTEMPORARY_DTORS -verify -w -analyzer-config cfg-temporary-dtors=true %s -std=c++11
 
 extern bool clang_analyzer_eval(bool);
+extern bool clang_analyzer_warnIfReached();
 
 struct Trivial {
   Trivial(int x) : value(x) {}
@@ -111,13 +112,13 @@ namespace compound_literals {
 }
 
 namespace destructors {
-  void testPR16664andPR18159Crash() {
-    struct Dtor {
-      ~Dtor();
-    };
-    extern bool coin();
-    extern bool check(const Dtor &);
+  struct Dtor {
+    ~Dtor();
+  };
+  extern bool coin();
+  extern bool check(const Dtor &);
 
+  void testPR16664andPR18159Crash() {
     // Regression test: we used to assert here when tmp dtors are enabled.
     // PR16664 and PR18159
     if (coin() && (coin() || coin() || check(Dtor()))) {
@@ -193,8 +194,7 @@ namespace destructors {
                 (i == 4 || i == 4 ||
                  compute(i == 5 && (i == 4 || check(NoReturnDtor()))))) ||
         i != 4) {
-      // FIXME: This shouldn't cause a warning.
-      clang_analyzer_eval(true);  // expected-warning{{TRUE}}
+      clang_analyzer_eval(true);  // no warning, unreachable code
     }
   }
 
@@ -211,8 +211,7 @@ namespace destructors {
   void testConsistencyNestedComplex(bool value) {
     if (value) {
       if (!value || !value || check(NoReturnDtor())) {
-        // FIXME: This shouldn't cause a warning.
-        clang_analyzer_eval(true); // expected-warning{{TRUE}}
+        clang_analyzer_eval(true);  // no warning, unreachable code
       }
     }
   }
@@ -225,6 +224,120 @@ namespace destructors {
       }
     }
   }
+  // PR16664 and PR18159
+  void testConsistencyNestedComplexMidBranch(bool value) {
+    if (value) {
+      if (!value || !value || check(NoReturnDtor()) || value) {
+        clang_analyzer_eval(true);  // no warning, unreachable code
+      }
+    }
+  }
+
+  // PR16664 and PR18159
+  void testConsistencyNestedComplexNestedBranch(bool value) {
+    if (value) {
+      if (!value || (!value || check(NoReturnDtor()) || value)) {
+        clang_analyzer_eval(true);  // no warning, unreachable code
+      }
+    }
+  }
+
+  // PR16664 and PR18159
+  void testConsistencyNestedVariableModification(bool value) {
+    bool other = true;
+    if (value) {
+      if (!other || !value || (other = false) || check(NoReturnDtor()) ||
+          !other) {
+        clang_analyzer_eval(true);  // no warning, unreachable code
+      }
+    }
+  }
+
+  void testTernaryNoReturnTrueBranch(bool value) {
+    if (value) {
+      bool b = value && (value ? check(NoReturnDtor()) : true);
+      clang_analyzer_eval(true);  // no warning, unreachable code
+    }
+  }
+  void testTernaryNoReturnFalseBranch(bool value) {
+    if (value) {
+      bool b = !value && !value ? true : check(NoReturnDtor());
+      clang_analyzer_eval(true);  // no warning, unreachable code
+    }
+  }
+  void testTernaryIgnoreNoreturnBranch(bool value) {
+    if (value) {
+      bool b = !value && !value ? check(NoReturnDtor()) : true;
+      clang_analyzer_eval(true);  // expected-warning{{TRUE}}
+    }
+  }
+  void testTernaryTrueBranchReached(bool value) {
+    value ? clang_analyzer_warnIfReached() : // expected-warning{{REACHABLE}}
+            check(NoReturnDtor());
+  }
+  void testTernaryFalseBranchReached(bool value) {
+    value ? check(NoReturnDtor()) :
+            clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+  }
+
+  void testLoop() {
+    for (int i = 0; i < 10; ++i) {
+      if (i < 3 && (i >= 2 || check(NoReturnDtor()))) {
+        clang_analyzer_eval(true);  // no warning, unreachable code
+      }
+    }
+  }
+
+  bool testRecursiveFrames(bool isInner) {
+    if (isInner ||
+        (clang_analyzer_warnIfReached(), false) || // expected-warning{{REACHABLE}}
+        check(NoReturnDtor()) ||
+        testRecursiveFrames(true)) {
+      clang_analyzer_warnIfReached(); // expected-warning{{REACHABLE}}
+    }
+  }
+  void testRecursiveFramesStart() { testRecursiveFrames(false); }
+
+  void testLambdas() {
+    // This is the test we would like to write:
+    // []() { check(NoReturnDtor()); } != nullptr || check(Dtor());
+    // But currently the analyzer stops when it encounters a lambda:
+    [] {};
+    // The CFG for this now looks correct, but we still do not reach the line
+    // below.
+    clang_analyzer_warnIfReached(); // FIXME: Should warn.
+  }
+
+  void testGnuExpressionStatements(int v) {
+    ({ ++v; v == 10 || check(NoReturnDtor()); v == 42; }) || v == 23;
+    clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
+
+    ({ ++v; check(NoReturnDtor()); v == 42; }) || v == 23;
+    clang_analyzer_warnIfReached();  // no warning, unreachable code
+  }
+
+  void testGnuExpressionStatementsDestructionPoint(int v) {
+    // In normal context, the temporary destructor runs at the end of the full
+    // statement, thus the last statement is reached.
+    (++v, check(NoReturnDtor()), v == 42),
+        clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
+
+    // GNU expression statements execute temporary destructors within the
+    // blocks, thus the last statement is not reached.
+    ({ ++v; check(NoReturnDtor()); v == 42; }),
+        clang_analyzer_warnIfReached();  // no warning, unreachable code
+  }
+
+  void testMultipleTemporaries(bool value) {
+    if (value) {
+      // FIXME: Find a way to verify construction order.
+      // ~Dtor should run before ~NoReturnDtor() because construction order is
+      // guaranteed by comma operator.
+      if (!value || check((NoReturnDtor(), Dtor())) || value) {
+        clang_analyzer_eval(true);  // no warning, unreachable code
+      }
+    }
+  }
 
   void testBinaryOperatorShortcut(bool value) {
     if (value) {
@@ -234,6 +347,52 @@ namespace destructors {
     }
   }
 
+  void testIfAtEndOfLoop() {
+    int y = 0;
+    while (true) {
+      if (y > 0) {
+        clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
+      }
+      ++y;
+      // Test that the CFG gets hooked up correctly when temporary destructors
+      // are handled after a statically known branch condition.
+      if (true) (void)0; else (void)check(NoReturnDtor());
+    }
+  }
+
+  void testTernaryAtEndOfLoop() {
+    int y = 0;
+    while (true) {
+      if (y > 0) {
+        clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
+      }
+      ++y;
+      // Test that the CFG gets hooked up correctly when temporary destructors
+      // are handled after a statically known branch condition.
+      true ? (void)0 : (void)check(NoReturnDtor());
+    }
+  }
+
+  void testNoReturnInComplexCondition() {
+    check(Dtor()) &&
+        (check(NoReturnDtor()) || check(NoReturnDtor())) && check(Dtor());
+    clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
+  }
+
+  void testSequencingOfConditionalTempDtors(bool b) {
+    b || (check(Dtor()), check(NoReturnDtor()));
+    clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
+  }
+
+  void testSequencingOfConditionalTempDtors2(bool b) {
+    (b || check(Dtor())), check(NoReturnDtor());
+    clang_analyzer_warnIfReached();  // no warning, unreachable code
+  }
+
+  void testSequencingOfConditionalTempDtorsWithinBinaryOperators(bool b) {
+    b || (check(Dtor()) + check(NoReturnDtor()));
+    clang_analyzer_warnIfReached();  // expected-warning{{REACHABLE}}
+  }
 #endif // TEMPORARY_DTORS
 }
 
index 531dc23995d27360a83e4d392dca8cf4eef701b2..bafeafcf1cf0c3ac7b3f8d3f66387035b9bdcae0 100644 (file)
@@ -146,6 +146,28 @@ void PR9412_f() {
     PR9412_t<PR9412_Exact>(); // expected-note {{in instantiation of function template specialization 'PR9412_t<0>' requested here}}
 }
 
+struct NoReturn {
+  ~NoReturn() __attribute__((noreturn));
+  operator bool() const;
+};
+struct Return {
+  ~Return();
+  operator bool() const;
+};
+
+int testTernaryUnconditionalNoreturn() {
+  true ? NoReturn() : NoReturn();
+}
+
+int testTernaryConditionalNoreturnTrueBranch(bool value) {
+  value ? (NoReturn() || NoReturn()) : Return();
+} // expected-warning {{control may reach end of non-void function}}
+
+int testTernaryConditionalNoreturnFalseBranch(bool value) {
+  value ? Return() : (NoReturn() || NoReturn());
+} // expected-warning {{control may reach end of non-void function}}
+
+
 #if __cplusplus >= 201103L
 namespace LambdaVsTemporaryDtor {
   struct Y { ~Y(); };