From: Anton Yartsev Date: Tue, 5 Aug 2014 18:26:05 +0000 (+0000) Subject: [Analyzer] fix for PR19102 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=2e61d2893934f7b91ca9e2f889a4e4cc9939b05c;p=clang [Analyzer] fix for PR19102 Newly-created unconsumed instance is now assumed escaped if an invoked constructor has an argument of a pointer-to-record type. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@214909 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index a03fa25900..c7a64fb06d 100644 --- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -15,6 +15,7 @@ #include "ClangSACheckers.h" #include "InterCheckerAPI.h" #include "clang/AST/Attr.h" +#include "clang/AST/ParentMap.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" @@ -753,6 +754,42 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { 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 treatUnusedNewEscaped(const CXXNewExpr *NE) { + + const CXXConstructExpr *ConstructE = NE->getConstructExpr(); + if (!ConstructE) + return false; + + if (!NE->getAllocatedType()->getAsCXXRecordDecl()) + return false; + + const 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); + + if (CtorParamPointeeT->getAsCXXRecordDecl()) + return true; + } + + return false; +} + void MallocChecker::checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const { @@ -765,6 +802,10 @@ void MallocChecker::checkPostStmt(const CXXNewExpr *NE, if (!isStandardNewDelete(NE->getOperatorNew(), C.getASTContext())) return; + ParentMap &PM = C.getLocationContext()->getParentMap(); + if (!PM.isConsumedExpr(NE) && treatUnusedNewEscaped(NE)) + return; + ProgramStateRef State = C.getState(); // The return value from operator new is bound to a specified initialization // value (if any) and we don't want to loose this value. So we call diff --git a/test/Analysis/NewDeleteLeaks-PR19102.cpp b/test/Analysis/NewDeleteLeaks-PR19102.cpp new file mode 100644 index 0000000000..559c2d6f0e --- /dev/null +++ b/test/Analysis/NewDeleteLeaks-PR19102.cpp @@ -0,0 +1,43 @@ +// RUN: %clang_cc1 -analyze -analyzer-checker=core,alpha.cplusplus.NewDeleteLeaks -verify %s + +class A0 {}; + +class A1 { +public: + A1(int); +}; + +struct S{ + int i; +}; + +class A2 { +public: + A2(); + A2(S); + A2(int*); + A2(S*); + A2(S&, int); + A2(int, S**); +}; + +void test() { + new int; // expected-warning@+1 {{Potential memory leak}} + new A0; // expected-warning@+1 {{Potential memory leak}} + new A1(0); // expected-warning@+1 {{Potential memory leak}} + new A2; // expected-warning@+1 {{Potential memory leak}} + S s; + s.i = 1; + S* ps = new S; + new A2(s); // expected-warning@+1 {{Potential memory leak}} + new A2(&(s.i)); // expected-warning@+1 {{Potential memory leak}} + new A2(ps); // no warning + new A2(*ps, 1); // no warning + new A2(1, &ps); // no warning + + // Tests to ensure that leaks are reported for consumed news no matter what the arguments are. + A2 *a2p1 = new A2; // expected-warning@+1 {{Potential leak of memory}} + A2 *a2p2 = new A2(ps); // expected-warning@+1 {{Potential leak of memory}} + A2 *a2p3 = new A2(*ps, 1); // expected-warning@+1 {{Potential leak of memory}} + A2 *a2p4 = new A2(1, &ps); // expected-warning@+1 {{Potential leak of memory}} +}