class PrinterHelper;
class LangOptions;
class ASTContext;
+ class CXXRecordDecl;
+ class CXXDeleteExpr;
/// CFGElement - Represents a top-level expression in a basic block.
class CFGElement {
Initializer,
// dtor kind
AutomaticObjectDtor,
+ DeleteDtor,
BaseDtor,
MemberDtor,
TemporaryDtor,
}
};
+/// CFGDeleteDtor - Represents C++ object destructor generated
+/// from a call to delete.
+class CFGDeleteDtor : public CFGImplicitDtor {
+public:
+ CFGDeleteDtor(const CXXRecordDecl *RD, const CXXDeleteExpr *DE)
+ : CFGImplicitDtor(DeleteDtor, RD, DE) {}
+
+ const CXXRecordDecl *getCXXRecordDecl() const {
+ return static_cast<CXXRecordDecl*>(Data1.getPointer());
+ }
+
+ // Get Delete expression which triggered the destructor call.
+ const CXXDeleteExpr *getDeleteExpr() {
+ return static_cast<CXXDeleteExpr *>(Data2.getPointer());
+ }
+
+
+private:
+ friend class CFGElement;
+ CFGDeleteDtor() {}
+ static bool isKind(const CFGElement &elem) {
+ return elem.getKind() == DeleteDtor;
+ }
+};
+
/// CFGBaseDtor - Represents C++ object destructor implicitly generated for
/// base object in destructor.
class CFGBaseDtor : public CFGImplicitDtor {
Elements.push_back(CFGAutomaticObjDtor(VD, S), C);
}
+ void appendDeleteDtor(CXXRecordDecl *RD, CXXDeleteExpr *DE, BumpVectorContext &C) {
+ Elements.push_back(CFGDeleteDtor(RD, DE), C);
+ }
+
// Destructors must be inserted in reversed order. So insertion is in two
// steps. First we prepare space for some number of elements, then we insert
// the elements beginning at the last position in prepared space.
void ProcessAutomaticObjDtor(const CFGAutomaticObjDtor D,
ExplodedNode *Pred, ExplodedNodeSet &Dst);
+ void ProcessDeleteDtor(const CFGDeleteDtor D,
+ ExplodedNode *Pred, ExplodedNodeSet &Dst);
void ProcessBaseDtor(const CFGBaseDtor D,
ExplodedNode *Pred, ExplodedNodeSet &Dst);
void ProcessMemberDtor(const CFGMemberDtor D,
AddStmtChoice asc);
CFGBlock *VisitCXXCatchStmt(CXXCatchStmt *S);
CFGBlock *VisitCXXConstructExpr(CXXConstructExpr *C, AddStmtChoice asc);
+ CFGBlock *VisitCXXDeleteExpr(CXXDeleteExpr *DE, AddStmtChoice asc);
CFGBlock *VisitCXXForRangeStmt(CXXForRangeStmt *S);
CFGBlock *VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E,
AddStmtChoice asc);
B->appendAutomaticObjDtor(VD, S, cfg->getBumpVectorContext());
}
+ void appendDeleteDtor(CFGBlock *B, CXXRecordDecl *RD, CXXDeleteExpr *DE) {
+ B->appendDeleteDtor(RD, DE, cfg->getBumpVectorContext());
+ }
+
void prependAutomaticObjDtorsWithTerminator(CFGBlock *Blk,
LocalScope::const_iterator B, LocalScope::const_iterator E);
case Stmt::CXXConstructExprClass:
return VisitCXXConstructExpr(cast<CXXConstructExpr>(S), asc);
+ case Stmt::CXXDeleteExprClass:
+ return VisitCXXDeleteExpr(cast<CXXDeleteExpr>(S), asc);
+
case Stmt::CXXFunctionalCastExprClass:
return VisitCXXFunctionalCastExpr(cast<CXXFunctionalCastExpr>(S), asc);
return VisitChildren(C);
}
+
+CFGBlock *CFGBuilder::VisitCXXDeleteExpr(CXXDeleteExpr *DE,
+ AddStmtChoice asc) {
+ autoCreateBlock();
+ appendStmt(Block, DE);
+ QualType DTy = DE->getDestroyedType();
+ DTy = DTy.getNonReferenceType();
+ CXXRecordDecl *RD = Context->getBaseElementType(DTy)->getAsCXXRecordDecl();
+ if (RD) {
+ if (!RD->hasTrivialDestructor())
+ appendDeleteDtor(Block, RD, DE);
+ }
+
+ return VisitChildren(DE);
+}
+
CFGBlock *CFGBuilder::VisitCXXFunctionalCastExpr(CXXFunctionalCastExpr *E,
AddStmtChoice asc) {
if (asc.alwaysAdd(*this, E)) {
cast<CXXRecordDecl>(recordType->getDecl());
return classDecl->getDestructor();
}
+ case CFGElement::DeleteDtor: {
+ const CXXDeleteExpr *DE = castAs<CFGDeleteDtor>().getDeleteExpr();
+ QualType DTy = DE->getDestroyedType();
+ DTy = DTy.getNonReferenceType();
+ const CXXRecordDecl *classDecl =
+ astContext.getBaseElementType(DTy)->getAsCXXRecordDecl();
+ return classDecl->getDestructor();
+ }
case CFGElement::TemporaryDtor: {
const CXXBindTemporaryExpr *bindExpr =
castAs<CFGTemporaryDtor>().getBindTemporaryExpr();
OS << ".~" << T->getAsCXXRecordDecl()->getName().str() << "()";
OS << " (Implicit destructor)\n";
+ } else if (Optional<CFGDeleteDtor> DE = E.getAs<CFGDeleteDtor>()) {
+ const CXXRecordDecl *RD = DE->getCXXRecordDecl();
+ if (!RD)
+ return;
+ CXXDeleteExpr *DelExpr =
+ const_cast<CXXDeleteExpr*>(DE->getDeleteExpr());
+ Helper->handledStmt(cast<Stmt>(DelExpr->getArgument()), OS);
+ OS << "->~" << RD->getName().str() << "()";
+ OS << " (Implicit destructor)\n";
} else if (Optional<CFGBaseDtor> BE = E.getAs<CFGBaseDtor>()) {
const CXXBaseSpecifier *BS = BE->getBaseSpecifier();
OS << "~" << BS->getType()->getAsCXXRecordDecl()->getName() << "()";
ProcessInitializer(E.castAs<CFGInitializer>().getInitializer(), Pred);
return;
case CFGElement::AutomaticObjectDtor:
+ case CFGElement::DeleteDtor:
case CFGElement::BaseDtor:
case CFGElement::MemberDtor:
case CFGElement::TemporaryDtor:
case CFGElement::TemporaryDtor:
ProcessTemporaryDtor(D.castAs<CFGTemporaryDtor>(), Pred, Dst);
break;
+ case CFGElement::DeleteDtor:
+ ProcessDeleteDtor(D.castAs<CFGDeleteDtor>(), Pred, Dst);
+ break;
default:
llvm_unreachable("Unexpected dtor kind.");
}
Pred, Dst);
}
+void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ //TODO: Handle DeleteDtor
+}
+
void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D,
ExplodedNode *Pred, ExplodedNodeSet &Dst) {
const LocationContext *LCtx = Pred->getLocationContext();
return PathDiagnosticLocation::createEnd(Dtor.getTriggerStmt(),
SM, CallerCtx);
}
+ case CFGElement::DeleteDtor: {
+ llvm_unreachable("not yet implemented!");
+ }
case CFGElement::BaseDtor:
case CFGElement::MemberDtor: {
const AnalysisDeclContext *CallerInfo = CallerCtx->getAnalysisDeclContext();
extern int *dummy();
(void)__builtin_object_size(dummy(), 0);
}
+
+
+class A {
+public:
+ A() {}
+ ~A() {}
+};
+
+// CHECK: [B2 (ENTRY)]
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B1]
+// CHECK-NEXT: 1: (CXXConstructExpr, class A)
+// CHECK-NEXT: 2: new A([B1.1])
+// CHECK-NEXT: 3: A *a = new A();
+// CHECK-NEXT: 4: a
+// CHECK-NEXT: 5: [B1.4] (ImplicitCastExpr, LValueToRValue, class A *)
+// CHECK-NEXT: 6: [B1.5]->~A() (Implicit destructor)
+// CHECK-NEXT: 7: delete [B1.5]
+// CHECK-NEXT: Preds (1): B2
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT: Preds (1): B1
+void test_deletedtor() {
+ A *a = new A();
+ delete a;
+}
+
+// CHECK: [B2 (ENTRY)]
+// CHECK-NEXT: Succs (1): B1
+// CHECK: [B1]
+// CHECK-NEXT: 1: 5
+// CHECK-NEXT: 2: (CXXConstructExpr, class A)
+// CHECK-NEXT: 3: new A {{\[\[}}B1.1]]
+// CHECK-NEXT: 4: A *a = new A [5];
+// CHECK-NEXT: 5: a
+// CHECK-NEXT: 6: [B1.5] (ImplicitCastExpr, LValueToRValue, class A *)
+// CHECK-NEXT: 7: [B1.6]->~A() (Implicit destructor)
+// CHECK-NEXT: 8: delete [] [B1.6]
+// CHECK-NEXT: Preds (1): B2
+// CHECK-NEXT: Succs (1): B0
+// CHECK: [B0 (EXIT)]
+// CHECK-NEXT: Preds (1): B1
+void test_deleteArraydtor() {
+ A *a = new A[5];
+ delete[] a;
+}