Index: clang/include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- clang/include/clang/StaticAnalyzer/Checkers/Checkers.td +++ clang/include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -1268,6 +1268,30 @@ "false", Released, Hide>, + CmdLineOption, + CmdLineOption, + CmdLineOption, + CmdLineOption, CmdLineOption, + CmdLineOption, CmdLineOption(AnyFunctionCall::getOriginExpr()); + } + + const FunctionDecl *getDecl() const override { + return getOriginExpr()->getOperatorDelete(); + } + + unsigned getNumArgs() const override { return getDecl()->getNumParams(); } + + const Expr *getArgExpr(unsigned Index = 0) const override { + return getOriginExpr()->getArgument(); + } + + Kind getKind() const override { return CE_CXXDeallocator; } + + static bool classof(const CallEvent *CE) { + return CE->getKind() == CE_CXXDeallocator; + } +}; + /// Represents the ways an Objective-C message send can occur. // // Note to maintainers: OCM_Message should always be last, since it does not @@ -1244,6 +1283,12 @@ const LocationContext *LCtx) { return create(E, State, LCtx); } + + CallEventRef + getCXXDeallocatorCall(const CXXDeleteExpr *E, ProgramStateRef State, + const LocationContext *LCtx) { + return create(E, State, LCtx); + } }; template Index: clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp @@ -33,18 +33,23 @@ check::PostStmt, check::PreStmt, check::PostStmt, + check::PreStmt, + check::PostStmt, + check::PreStmt, + check::PostStmt, check::PreStmt, check::PostStmt, check::PreCall, check::PostCall, check::EndFunction, + check::EndAnalysis, check::NewAllocator, check::Bind, check::PointerEscape, check::RegionChanges, check::LiveSymbols> { - bool isCallbackEnabled(AnalyzerOptions &Opts, StringRef CallbackName) const { + bool isCallbackEnabled(const AnalyzerOptions &Opts, StringRef CallbackName) const { return Opts.getCheckerBooleanOption(this, "*") || Opts.getCheckerBooleanOption(this, CallbackName); } @@ -95,6 +100,26 @@ llvm::errs() << "PostStmt\n"; } + void checkPreStmt(const CXXDeleteExpr *NE, CheckerContext &C) const { + if (isCallbackEnabled(C, "PreStmtCXXDeleteExpr")) + llvm::errs() << "PreStmt\n"; + } + + void checkPostStmt(const CXXDeleteExpr *NE, CheckerContext &C) const { + if (isCallbackEnabled(C, "PostStmtCXXDeleteExpr")) + llvm::errs() << "PostStmt\n"; + } + + void checkPreStmt(const CXXConstructExpr *NE, CheckerContext &C) const { + if (isCallbackEnabled(C, "PreStmtCXXConstructExpr")) + llvm::errs() << "PreStmt\n"; + } + + void checkPostStmt(const CXXConstructExpr *NE, CheckerContext &C) const { + if (isCallbackEnabled(C, "PostStmtCXXConstructExpr")) + llvm::errs() << "PostStmt\n"; + } + void checkPreStmt(const OffsetOfExpr *OOE, CheckerContext &C) const { if (isCallbackEnabled(C, "PreStmtOffsetOfExpr")) llvm::errs() << "PreStmt\n"; @@ -140,6 +165,13 @@ } } + void checkEndAnalysis(ExplodedGraph &G, + BugReporter &BR, + ExprEngine &Eng) const { + if (isCallbackEnabled(BR.getAnalyzerOptions(), "EndAnalysis")) + llvm::errs() << "EndAnalysis\n"; + } + void checkNewAllocator(const CXXNewExpr *CNE, SVal Target, CheckerContext &C) const { if (isCallbackEnabled(C, "NewAllocator")) Index: clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -281,8 +281,8 @@ check::ConstPointerEscape, check::PreStmt, check::EndFunction, check::PreCall, check::PostCall, check::PostStmt, check::NewAllocator, - check::PreStmt, check::PostStmt, - check::PostObjCMessage, check::Location, eval::Assume> { + check::PostStmt, check::PostObjCMessage, + check::Location, eval::Assume> { public: /// In pessimistic mode, the checker assumes that it does not know which /// functions might free the memory. @@ -314,7 +314,6 @@ void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const; void checkNewAllocator(const CXXNewExpr *NE, SVal Target, CheckerContext &C) const; - void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const; void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; @@ -1378,25 +1377,6 @@ return State; } -void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE, - CheckerContext &C) const { - - if (!ChecksEnabled[CK_NewDeleteChecker]) - if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol()) - checkUseAfterFree(Sym, C, DE->getArgument()); - - if (!isStandardNewDelete(DE->getOperatorDelete())) - return; - - ProgramStateRef State = C.getState(); - bool IsKnownToBeAllocated; - State = FreeMemAux(C, DE->getArgument(), DE, State, - /*Hold*/ false, IsKnownToBeAllocated, - (DE->isArrayForm() ? AF_CXXNewArray : AF_CXXNew)); - - C.addTransition(State); -} - static bool isKnownDeallocObjCMethodName(const ObjCMethodCall &Call) { // If the first selector piece is one of the names below, assume that the // object takes ownership of the memory, promising to eventually deallocate it @@ -2580,7 +2560,27 @@ void MallocChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { - if (const CXXDestructorCall *DC = dyn_cast(&Call)) { + if (const auto *DC = dyn_cast(&Call)) { + const CXXDeleteExpr *DE = DC->getOriginExpr(); + + if (!ChecksEnabled[CK_NewDeleteChecker]) + if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol()) + checkUseAfterFree(Sym, C, DE->getArgument()); + + if (!isStandardNewDelete(DC->getDecl())) + return; + + ProgramStateRef State = C.getState(); + bool IsKnownToBeAllocated; + State = FreeMemAux(C, DE->getArgument(), DE, State, + /*Hold*/ false, IsKnownToBeAllocated, + (DE->isArrayForm() ? AF_CXXNewArray : AF_CXXNew)); + + C.addTransition(State); + return; + } + + if (const auto *DC = dyn_cast(&Call)) { SymbolRef Sym = DC->getCXXThisVal().getAsSymbol(); if (!Sym || checkDoubleDelete(Sym, C)) return; Index: clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -846,9 +846,11 @@ void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, ExplodedNode *Pred, ExplodedNodeSet &Dst) { - StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); - ProgramStateRef state = Pred->getState(); - Bldr.generateNode(CDE, Pred, state); + + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef Call = CEMgr.getCXXDeallocatorCall( + CDE, Pred->getState(), Pred->getLocationContext()); + getCheckerManager().runCheckersForPreCall(Dst, Pred, *Call, *this); } void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS, Index: clang/test/Analysis/analyzer-config.c =================================================================== --- clang/test/Analysis/analyzer-config.c +++ clang/test/Analysis/analyzer-config.c @@ -36,17 +36,22 @@ // CHECK-NEXT: deadcode.DeadStores:WarnForDeadNestedAssignments = true // CHECK-NEXT: debug.AnalysisOrder:* = false // CHECK-NEXT: debug.AnalysisOrder:Bind = false +// CHECK-NEXT: debug.AnalysisOrder:EndAnalysis = false // CHECK-NEXT: debug.AnalysisOrder:EndFunction = false // CHECK-NEXT: debug.AnalysisOrder:LiveSymbols = false // CHECK-NEXT: debug.AnalysisOrder:NewAllocator = false // CHECK-NEXT: debug.AnalysisOrder:PointerEscape = false // CHECK-NEXT: debug.AnalysisOrder:PostCall = false // CHECK-NEXT: debug.AnalysisOrder:PostStmtArraySubscriptExpr = false +// CHECK-NEXT: debug.AnalysisOrder:PostStmtCXXConstructExpr = false +// CHECK-NEXT: debug.AnalysisOrder:PostStmtCXXDeleteExpr = false // CHECK-NEXT: debug.AnalysisOrder:PostStmtCXXNewExpr = false // CHECK-NEXT: debug.AnalysisOrder:PostStmtCastExpr = false // CHECK-NEXT: debug.AnalysisOrder:PostStmtOffsetOfExpr = false // CHECK-NEXT: debug.AnalysisOrder:PreCall = false // CHECK-NEXT: debug.AnalysisOrder:PreStmtArraySubscriptExpr = false +// CHECK-NEXT: debug.AnalysisOrder:PreStmtCXXConstructExpr = false +// CHECK-NEXT: debug.AnalysisOrder:PreStmtCXXDeleteExpr = false // CHECK-NEXT: debug.AnalysisOrder:PreStmtCXXNewExpr = false // CHECK-NEXT: debug.AnalysisOrder:PreStmtCastExpr = false // CHECK-NEXT: debug.AnalysisOrder:PreStmtOffsetOfExpr = false @@ -100,4 +105,4 @@ // CHECK-NEXT: unroll-loops = false // CHECK-NEXT: widen-loops = false // CHECK-NEXT: [stats] -// CHECK-NEXT: num-entries = 97 +// CHECK-NEXT: num-entries = 102 Index: clang/test/Analysis/operator-delete-analysis-order.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/operator-delete-analysis-order.cpp @@ -0,0 +1,67 @@ +// RUN: %clang_analyze_cc1 -std=c++11 -fblocks -verify %s \ +// RUN: -analyzer-checker=core \ +// RUN: -analyzer-checker=debug.AnalysisOrder \ +// RUN: -analyzer-config debug.AnalysisOrder:PreStmtCXXDeleteExpr=true \ +// RUN: -analyzer-config debug.AnalysisOrder:PostStmtCXXDeleteExpr=true \ +// RUN: -analyzer-config debug.AnalysisOrder:PreCall=true \ +// RUN: -analyzer-config debug.AnalysisOrder:PostCall=true \ +// RUN: 2>&1 | FileCheck %s + +// expected-no-diagnostics + +namespace fundamental_dealloc { +void f() { + int *p = new int; + delete p; +} +} // namespace fundamental_dealloc + +namespace record_dealloc { +struct S { + int x, y; +}; + +void f() { + S *s = new S; + delete s; +} +} // namespace record_dealloc + +namespace nontrivial_destructor { +struct NontrivialDtor { + int x, y; + + ~NontrivialDtor() { + // Casually mine bitcoin. + } +}; + +void f() { + NontrivialDtor *s = new NontrivialDtor; + delete s; +} +} // namespace nontrivial_destructor + +// Mind that the results here are in reverse order compared how functions are +// defined in this file. + +// CHECK: PreCall (operator new) +// CHECK-NEXT: PostCall (operator new) +// CHECK-NEXT: PreCall (nontrivial_destructor::NontrivialDtor::NontrivialDtor) +// CHECK-NEXT: PostCall (nontrivial_destructor::NontrivialDtor::NontrivialDtor) +// CHECK-NEXT: PreCall (nontrivial_destructor::NontrivialDtor::~NontrivialDtor) +// CHECK-NEXT: PostCall (nontrivial_destructor::NontrivialDtor::~NontrivialDtor) +// CHECK-NEXT: PreStmt +// CHECK-NEXT: PreCall (operator delete) + +// CHECK: PreCall (operator new) +// CHECK-NEXT: PostCall (operator new) +// CHECK-NEXT: PreCall (record_dealloc::S::S) +// CHECK-NEXT: PostCall (record_dealloc::S::S) +// CHECK-NEXT: PreStmt +// CHECK-NEXT: PreCall (operator delete) + +// CHECK: PreCall (operator new) +// CHECK-NEXT: PostCall (operator new) +// CHECK-NEXT: PreStmt +// CHECK-NEXT: PreCall (operator delete)