Index: lib/StaticAnalyzer/Checkers/MallocChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -15,6 +15,8 @@ #include "ClangSACheckers.h" #include "InterCheckerAPI.h" #include "clang/AST/Attr.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/DeclFriend.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" @@ -753,6 +755,107 @@ C.addTransition(State); } +static QualType getDeepPointeeType(QualType T) { + QualType Result = T, PointeeType = T->getPointeeType(); + while (!PointeeType.isNull()) { + Result = PointeeType; + PointeeType = PointeeType->getPointeeType(); + } + return Result; +} + +static bool UnusedNewCanEscape(const CXXNewExpr *NE) { + // There are cases when the result of a call to nonplacement operator new is + // not used, but nevertheless, the memory may not be leaked. The memory can + // escape in the constructor of the constructed object. For example: + // + // void f(B* b) { + // new A(b); + // } + // + // 'A's constructor: + // A(B *b) { + // b->escapeA(this); // memory escapes! + // } + // + // (PR19102 is devoted to the problem and contain a full example). + // To reduce the number of false-positives we assume the unused operator new + // as not a leak if the following requirement is met: + // * The invoked constructor has a parameter - pointer or reference to a + // record that has a public method (any access level , if a constructed + // record is a friend of parameter record), that accepts a pointer to the + // type of the constructed record or one of its bases. + + const CXXConstructExpr* ConstructE = NE->getConstructExpr(); + if (!ConstructE) + return false; + + QualType ConstructedTy = NE->getAllocatedType().getCanonicalType(); + const CXXRecordDecl *ConstructedRD = ConstructedTy->getAsCXXRecordDecl(); + if (!ConstructedRD) + return false; + + CXXConstructorDecl* CtorD = ConstructE->getConstructor(); + + // Iterate over the constructor parameters. + for (const auto *CtorParam : CtorD->params()) { + + QualType CtorParamPointeeT = CtorParam->getType()->getPointeeType(); + if (CtorParamPointeeT.isNull()) + continue; + + CtorParamPointeeT = getDeepPointeeType(CtorParamPointeeT). + getCanonicalType().getUnqualifiedType(); + + if (const CXXRecordDecl *RD = CtorParamPointeeT->getAsCXXRecordDecl()) { + // Found a parameter of the pointer-to-a-record type. Look if a + // constructed record is a friend of a parameter record. + bool IsFriendOfCtorParam = false; + + for (const auto *FriendOfRD : RD->friends()) { + if (TypeSourceInfo *FriendType = FriendOfRD->getFriendType()) { + if (FriendType->getType().getCanonicalType() == ConstructedTy) { + IsFriendOfCtorParam = true; + break; + } + } + } + + // Iterate over records methods. + for (const auto *Method : RD->methods()) { + + if (!IsFriendOfCtorParam && Method->getAccess() != AS_public) + // A method is not accessible from a constructed record. + continue; + + // Iterate over method parameters, try to find one of a type of a + // constructed record or one of its bases. + for (const auto *IMethodParams : Method->params()) { + QualType MPPointeeT = IMethodParams->getType()->getPointeeType(); + if (MPPointeeT.isNull()) + continue; + + MPPointeeT = getDeepPointeeType(MPPointeeT).getCanonicalType(); + if (!isa(MPPointeeT)) + continue; + + if (MPPointeeT == ConstructedTy) + return true; + + // Iterate over bases of a constructed class. + for (const auto &ConstructedBases : ConstructedRD->bases()) { + QualType CBTy = ConstructedBases.getType(); + if (!CBTy.isNull() && CBTy.getUnqualifiedType() == MPPointeeT) + return true; + } + } + } + } + } + + return false; +} + void MallocChecker::checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const { @@ -763,6 +866,10 @@ checkUseAfterFree(Sym, C, *I); if (!isStandardNewDelete(NE->getOperatorNew(), C.getASTContext())) + return; + + ParentMap &PM = C.getLocationContext()->getParentMap(); + if (!PM.isConsumedExpr(NE) && UnusedNewCanEscape(NE)) return; ProgramStateRef State = C.getState(); Index: test/Analysis/NewDeleteLeaks-PR19102.cpp =================================================================== --- test/Analysis/NewDeleteLeaks-PR19102.cpp +++ test/Analysis/NewDeleteLeaks-PR19102.cpp @@ -0,0 +1,73 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDeleteLeaks -std=c++11 -verify %s + +class B1; + +class A0 {}; + +class A1 { +public: + void SetB1(B1* b1); +}; + +class A2 { + void SetB1(B1* b1); +}; + +class A3 { + friend class B1; + void SetB1(B1* b1); +}; + +class A4 { + friend class B1; + void SetB1(int, B1* b1); +}; + +class A5 { +public: + void SetB1(B1* &b1); +}; + +class B1 { +public: + B1(int); + B1(A0 &a0); + B1(A1 a1); + B1(A1* a1); + B1(int, A1& a1); + B1(A2 &a2); + B1(A3 &a3); + B1(A4 &a4); + B1(A5 &a5); + B1(A5 **a5); +}; + +class B2: public B1 { +public: + B2(A1 &a1); +}; + +void f() { + A0 a0; + new B1(a0); // expected-warning@+2 {{Potential memory leak}} + + A1 a1; + new B1(1); // expected-warning@+1 {{Potential memory leak}} + new B1(a1); // expected-warning@+1 {{Potential memory leak}} + new B1(&a1); // no warning + new B1(1, a1); // no warning + new B2(a1); // no warning + + A2 a2; + new B1(a2); // expected-warning@+2 {{Potential memory leak}} + + A3 a3; + new B1(a3); // no warning + + A4 a4; + new B1(a4); // no warning + + A5 a5, *a5p = &a5; + new B1(a5); // no warning + new B1(&a5p); // no warning +}