diff --git a/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp b/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp --- a/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp +++ b/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp @@ -19,6 +19,10 @@ using namespace ento; const FunctionDecl *CheckerContext::getCalleeDecl(const CallExpr *CE) const { + const FunctionDecl *D = CE->getDirectCallee(); + if (D) + return D; + const Expr *Callee = CE->getCallee(); SVal L = Pred->getSVal(Callee); return L.getAsFunctionDecl(); diff --git a/clang/test/Analysis/NewDelete-checker-test.cpp b/clang/test/Analysis/NewDelete-checker-test.cpp --- a/clang/test/Analysis/NewDelete-checker-test.cpp +++ b/clang/test/Analysis/NewDelete-checker-test.cpp @@ -421,3 +421,36 @@ Derived *p = (Derived *)allocate(); delete p; } + +template +void* allocate_via_nttp(size_t n) { + return allocate_fn(n); +} + +template +void deallocate_via_nttp(void* ptr) { + deallocate_fn(ptr); +} + +void testNTTPNewNTTPDelete() { + void* p = allocate_via_nttp<::operator new>(10); + deallocate_via_nttp<::operator delete>(p); +} // no warn + +void testNTTPNewDirectDelete() { + void* p = allocate_via_nttp<::operator new>(10); + ::operator delete(p); +} // no warn + +void testDirectNewNTTPDelete() { + void* p = ::operator new(10); + deallocate_via_nttp<::operator delete>(p); +} + +void not_free(void*) { +} + +void testLeakBecauseNTTPIsNotDeallocation() { + void* p = ::operator new(10); + deallocate_via_nttp(p); +} // leak-warning{{Potential leak of memory pointed to by 'p'}}