[cfe-commits] r160806 - in /cfe/trunk: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h lib/StaticAnalyzer/Core/ExprEngine.cpp lib/StaticAnalyzer/Core/ExprEngineCXX.cpp lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp lib/StaticAnalyzer/Core/PathDiagnostic.cpp

Jordan Rose jordan_rose at apple.com
Thu Jul 26 13:04:13 PDT 2012


Author: jrose
Date: Thu Jul 26 15:04:13 2012
New Revision: 160806

URL: http://llvm.org/viewvc/llvm-project?rev=160806&view=rev
Log:
[analyzer] Handle base class initializers and destructors.

Most of the logic here is fairly simple; the interesting thing is that
we now distinguish complete constructors from base or delegate constructors.
We also make sure to cast to the base class before evaluating a constructor
or destructor, since non-virtual base classes may behave differently.

This includes some refactoring of VisitCXXConstructExpr and VisitCXXDestructor
in order to keep ExprEngine.cpp as clean as possible (leaving the details for
ExprEngineCXX.cpp).

Modified:
    cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
    cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp

Modified: cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h?rev=160806&r1=160805&r2=160806&view=diff
==============================================================================
--- cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h (original)
+++ cfe/trunk/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h Thu Jul 26 15:04:13 2012
@@ -378,10 +378,10 @@
   void VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred, 
                         ExplodedNodeSet & Dst);
 
-  void VisitCXXConstructExpr(const CXXConstructExpr *E, const MemRegion *Dest,
-                             ExplodedNode *Pred, ExplodedNodeSet &Dst);
+  void VisitCXXConstructExpr(const CXXConstructExpr *E, ExplodedNode *Pred,
+                             ExplodedNodeSet &Dst);
 
-  void VisitCXXDestructor(const CXXDestructorDecl *DD,
+  void VisitCXXDestructor(QualType ObjectType,
                           const MemRegion *Dest, const Stmt *S,
                           ExplodedNode *Pred, ExplodedNodeSet &Dst);
 

Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp?rev=160806&r1=160805&r2=160806&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngine.cpp Thu Jul 26 15:04:13 2012
@@ -24,7 +24,6 @@
 #include "clang/AST/ParentMap.h"
 #include "clang/AST/StmtObjC.h"
 #include "clang/AST/StmtCXX.h"
-#include "clang/AST/DeclCXX.h"
 #include "clang/Basic/Builtins.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/PrettyStackTrace.h"
@@ -357,6 +356,7 @@
 void ExprEngine::ProcessInitializer(const CFGInitializer Init,
                                     ExplodedNode *Pred) {
   ExplodedNodeSet Dst;
+  NodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
 
   // We don't set EntryNode and currentStmt. And we don't clean up state.
   const CXXCtorInitializer *BMI = Init.getInitializer();
@@ -369,8 +369,6 @@
 
   if (BMI->isAnyMemberInitializer()) {
     // Evaluate the initializer.
-
-    StmtNodeBuilder Bldr(Pred, Dst, *currentBuilderContext);
     ProgramStateRef state = Pred->getState();
 
     const FieldDecl *FD = BMI->getAnyMember();
@@ -384,20 +382,17 @@
     PostInitializer PP(BMI, stackFrame);
     // Builder automatically add the generated node to the deferred set,
     // which are processed in the builder's dtor.
-    Bldr.generateNode(PP, Pred, state);
+    Bldr.generateNode(PP, state, Pred);
   } else {
     assert(BMI->isBaseInitializer());
 
-    // Get the base class declaration.
-    const CXXConstructExpr *ctorExpr = cast<CXXConstructExpr>(BMI->getInit());
-
-    // Create the base object region.
-    SVal baseVal =
-        getStoreManager().evalDerivedToBase(thisVal, ctorExpr->getType());
-    const MemRegion *baseReg = baseVal.getAsRegion();
-    assert(baseReg);
-
-    VisitCXXConstructExpr(ctorExpr, baseReg, Pred, Dst);
+    // We already did all the work when visiting the CXXConstructExpr.
+    // Just construct a PostInitializer node so that the diagnostics don't get
+    // confused.
+    PostInitializer PP(BMI, stackFrame);
+    // Builder automatically add the generated node to the deferred set,
+    // which are processed in the builder's dtor.
+    Bldr.generateNode(PP, Pred->getState(), Pred);
   }
 
   // Enqueue the new nodes onto the work list.
@@ -439,18 +434,29 @@
   if (const ReferenceType *refType = varType->getAs<ReferenceType>())
     varType = refType->getPointeeType();
 
-  const CXXRecordDecl *recordDecl = varType->getAsCXXRecordDecl();
-  assert(recordDecl && "get CXXRecordDecl fail");
-  const CXXDestructorDecl *dtorDecl = recordDecl->getDestructor();
-
   Loc dest = state->getLValue(varDecl, Pred->getLocationContext());
 
-  VisitCXXDestructor(dtorDecl, cast<loc::MemRegionVal>(dest).getRegion(),
+  VisitCXXDestructor(varType, cast<loc::MemRegionVal>(dest).getRegion(),
                      Dtor.getTriggerStmt(), Pred, Dst);
 }
 
 void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D,
-                                 ExplodedNode *Pred, ExplodedNodeSet &Dst) {}
+                                 ExplodedNode *Pred, ExplodedNodeSet &Dst) {
+  const LocationContext *LCtx = Pred->getLocationContext();
+  ProgramStateRef State = Pred->getState();
+
+  const CXXDestructorDecl *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl());
+  Loc ThisPtr = getSValBuilder().getCXXThis(CurDtor,
+                                            LCtx->getCurrentStackFrame());
+  SVal ThisVal = Pred->getState()->getSVal(ThisPtr);
+
+  // Create the base object region.
+  QualType BaseTy = D.getBaseSpecifier()->getType();
+  SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy);
+
+  VisitCXXDestructor(BaseTy, cast<loc::MemRegionVal>(BaseVal).getRegion(),
+                     CurDtor->getBody(), Pred, Dst);
+}
 
 void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
                                    ExplodedNode *Pred, ExplodedNodeSet &Dst) {}
@@ -459,26 +465,6 @@
                                       ExplodedNode *Pred,
                                       ExplodedNodeSet &Dst) {}
 
-static const VarDecl *findDirectConstruction(const DeclStmt *DS,
-                                             const Expr *Init) {
-  for (DeclStmt::const_decl_iterator I = DS->decl_begin(), E = DS->decl_end();
-       I != E; ++I) {
-    const VarDecl *Var = dyn_cast<VarDecl>(*I);
-    if (!Var)
-      continue;
-    if (Var->getInit() != Init)
-      continue;
-    // FIXME: We need to decide how copy-elision should work here.
-    if (!Var->isDirectInit())
-      break;
-    if (Var->getType()->isReferenceType())
-      break;
-    return Var;
-  }
-
-  return 0;
-}
-
 void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
                        ExplodedNodeSet &DstTop) {
   PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
@@ -739,20 +725,9 @@
     }
 
     case Stmt::CXXTemporaryObjectExprClass:
-    case Stmt::CXXConstructExprClass: {
-      const CXXConstructExpr *C = cast<CXXConstructExpr>(S);
-      const MemRegion *Target = 0;
-
-      const LocationContext *LCtx = Pred->getLocationContext();
-      const ParentMap &PM = LCtx->getParentMap();
-      if (const DeclStmt *DS = dyn_cast_or_null<DeclStmt>(PM.getParent(C)))
-        if (const VarDecl *Var = findDirectConstruction(DS, C))
-          Target = Pred->getState()->getLValue(Var, LCtx).getAsRegion();
-      // If we don't have a destination region, VisitCXXConstructExpr() will
-      // create one.
-      
+    case Stmt::CXXConstructExprClass: {      
       Bldr.takeNodes(Pred);
-      VisitCXXConstructExpr(C, Target, Pred, Dst);
+      VisitCXXConstructExpr(cast<CXXConstructExpr>(S), Pred, Dst);
       Bldr.addNodes(Dst);
       break;
     }

Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp?rev=160806&r1=160805&r2=160806&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp Thu Jul 26 15:04:13 2012
@@ -17,6 +17,7 @@
 #include "clang/StaticAnalyzer/Core/PathSensitive/Calls.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/StmtCXX.h"
+#include "clang/AST/ParentMap.h"
 
 using namespace clang;
 using namespace ento;
@@ -40,11 +41,68 @@
   Bldr.generateNode(ME, Pred, state->BindExpr(ME, LCtx, loc::MemRegionVal(R)));
 }
 
+static const VarDecl *findDirectConstruction(const DeclStmt *DS,
+                                             const Expr *Init) {
+  for (DeclStmt::const_decl_iterator I = DS->decl_begin(), E = DS->decl_end();
+       I != E; ++I) {
+    const VarDecl *Var = dyn_cast<VarDecl>(*I);
+    if (!Var)
+      continue;
+    if (Var->getInit() != Init)
+      continue;
+    // FIXME: We need to decide how copy-elision should work here.
+    if (!Var->isDirectInit())
+      break;
+    if (Var->getType()->isReferenceType())
+      break;
+    return Var;
+  }
+
+  return 0;
+}
+
 void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
-                                       const MemRegion *Dest,
                                        ExplodedNode *Pred,
                                        ExplodedNodeSet &destNodes) {
-  CXXConstructorCall Call(CE, Dest, Pred->getState(),
+  const LocationContext *LCtx = Pred->getLocationContext();
+  const MemRegion *Target = 0;
+
+  switch (CE->getConstructionKind()) {
+  case CXXConstructExpr::CK_Complete: {
+    // See if we're constructing an existing region.
+    // FIXME: This is inefficient. Not only are we getting the parent of the
+    // CXXConstructExpr, but we also then have to crawl through it if it's a
+    // DeclStmt to find out which variable is being constructed. (The CFG has
+    // one (fake) DeclStmt per Decl, but ParentMap uses the original AST.)
+    const ParentMap &PM = LCtx->getParentMap();
+    if (const DeclStmt *DS = dyn_cast_or_null<DeclStmt>(PM.getParent(CE)))
+      if (const VarDecl *Var = findDirectConstruction(DS, CE))
+        Target = Pred->getState()->getLValue(Var, LCtx).getAsRegion();
+    // If we can't find an existing region to construct into, we'll just
+    // generate a symbolic region, which is fine.
+    break;
+  }
+  case CXXConstructExpr::CK_NonVirtualBase:
+  case CXXConstructExpr::CK_VirtualBase:
+  case CXXConstructExpr::CK_Delegating: {
+    const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
+    Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor,
+                                              LCtx->getCurrentStackFrame());
+    SVal ThisVal = Pred->getState()->getSVal(ThisPtr);
+
+    if (CE->getConstructionKind() == CXXConstructExpr::CK_Delegating) {
+      Target = ThisVal.getAsRegion();
+    } else {
+      // Cast to the base type.
+      QualType BaseTy = CE->getType();
+      SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy);
+      Target = cast<loc::MemRegionVal>(BaseVal).getRegion();
+    }
+    break;
+  }
+  }
+
+  CXXConstructorCall Call(CE, Target, Pred->getState(),
                           Pred->getLocationContext());
 
   ExplodedNodeSet DstPreVisit;
@@ -65,12 +123,16 @@
   getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this);
 }
 
-void ExprEngine::VisitCXXDestructor(const CXXDestructorDecl *DD,
-                                      const MemRegion *Dest,
-                                      const Stmt *S,
-                                      ExplodedNode *Pred, 
-                                      ExplodedNodeSet &Dst) {
-  CXXDestructorCall Call(DD, S, Dest, Pred->getState(),
+void ExprEngine::VisitCXXDestructor(QualType ObjectType,
+                                    const MemRegion *Dest,
+                                    const Stmt *S,
+                                    ExplodedNode *Pred, 
+                                    ExplodedNodeSet &Dst) {
+  const CXXRecordDecl *RecordDecl = ObjectType->getAsCXXRecordDecl();
+  assert(RecordDecl && "Only CXXRecordDecls should have destructors");
+  const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor();
+
+  CXXDestructorCall Call(DtorDecl, S, Dest, Pred->getState(),
                          Pred->getLocationContext());
 
   ExplodedNodeSet DstPreCall;

Modified: cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp?rev=160806&r1=160805&r2=160806&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp Thu Jul 26 15:04:13 2012
@@ -70,37 +70,49 @@
 static std::pair<const Stmt*,
                  const CFGBlock*> getLastStmt(const ExplodedNode *Node) {
   const Stmt *S = 0;
-  const CFGBlock *Blk = 0;
   const StackFrameContext *SF =
           Node->getLocation().getLocationContext()->getCurrentStackFrame();
+
+  // Back up through the ExplodedGraph until we reach a statement node.
   while (Node) {
     const ProgramPoint &PP = Node->getLocation();
-    // Skip any BlockEdges, empty blocks, and the CallExitBegin node.
-    if (isa<BlockEdge>(PP) || isa<CallExitBegin>(PP) || isa<BlockEntrance>(PP)){
-      assert(Node->pred_size() == 1);
-      Node = *Node->pred_begin();
-      continue;
-    }
-    // If we reached the CallEnter, the function has no statements.
-    if (isa<CallEnter>(PP))
-      break;
+
     if (const StmtPoint *SP = dyn_cast<StmtPoint>(&PP)) {
       S = SP->getStmt();
-      // Now, get the enclosing basic block.
-      while (Node && Node->pred_size() >=1 ) {
-        const ProgramPoint &PP = Node->getLocation();
-        if (isa<BlockEdge>(PP) &&
-            (PP.getLocationContext()->getCurrentStackFrame() == SF)) {
-          BlockEdge &EPP = cast<BlockEdge>(PP);
-          Blk = EPP.getDst();
-          break;
-        }
-        Node = *Node->pred_begin();
-      }
       break;
+    } else if (const CallExitEnd *CEE = dyn_cast<CallExitEnd>(&PP)) {
+      S = CEE->getCalleeContext()->getCallSite();
+      if (S)
+        break;
+      // If we have an implicit call, we'll probably end up with a
+      // StmtPoint inside the callee, which is acceptable.
+      // (It's possible a function ONLY contains implicit calls -- such as an
+      // implicitly-generated destructor -- so we shouldn't just skip back to
+      // the CallEnter node and keep going.)
+    } else if (const CallEnter *CE = dyn_cast<CallEnter>(&PP)) {
+      // If we reached the CallEnter for this function, it has no statements.
+      if (CE->getCalleeContext() == SF)
+        break;
     }
-    break;
+
+    Node = *Node->pred_begin();
   }
+
+  const CFGBlock *Blk = 0;
+  if (S) {
+    // Now, get the enclosing basic block.
+    while (Node && Node->pred_size() >=1 ) {
+      const ProgramPoint &PP = Node->getLocation();
+      if (isa<BlockEdge>(PP) &&
+          (PP.getLocationContext()->getCurrentStackFrame() == SF)) {
+        BlockEdge &EPP = cast<BlockEdge>(PP);
+        Blk = EPP.getDst();
+        break;
+      }
+      Node = *Node->pred_begin();
+    }
+  }
+
   return std::pair<const Stmt*, const CFGBlock*>(S, Blk);
 }
 
@@ -293,11 +305,14 @@
     if (!ADC->getCFGBuildOptions().AddImplicitDtors ||
         !ADC->getCFGBuildOptions().AddInitializers)
       return false;
-    // FIXME: This is a hack. We only process VarDecl destructors right now,
-    // so we should only inline VarDecl constructors.
-    if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call))
-      if (!isa<VarRegion>(Ctor->getCXXThisVal().getAsRegion()))
-        return false;
+    // FIXME: This is a hack. We don't handle member or temporary constructors
+    // right now, so we shouldn't inline their constructors.
+    if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
+      const CXXConstructExpr *CtorExpr = Ctor->getOriginExpr();
+      if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete)
+        if (!isa<VarRegion>(Ctor->getCXXThisVal().getAsRegion()))
+          return false;
+    }
     break;
   }
   case CE_CXXAllocator:

Modified: cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp?rev=160806&r1=160805&r2=160806&view=diff
==============================================================================
--- cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp (original)
+++ cfe/trunk/lib/StaticAnalyzer/Core/PathDiagnostic.cpp Thu Jul 26 15:04:13 2012
@@ -532,7 +532,12 @@
                                              SM, CallerCtx);
   }
   case CFGElement::BaseDtor:
-  case CFGElement::MemberDtor:
+  case CFGElement::MemberDtor: {
+    const AnalysisDeclContext *CallerInfo = CallerCtx->getAnalysisDeclContext();
+    if (const Stmt *CallerBody = CallerInfo->getBody())
+      return PathDiagnosticLocation::createEnd(CallerBody, SM, CallerCtx);
+    return PathDiagnosticLocation::create(CallerInfo->getDecl(), SM);
+  }
   case CFGElement::TemporaryDtor:
     llvm_unreachable("not yet implemented!");
   }





More information about the cfe-commits mailing list