diff options
| -rw-r--r-- | clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp | 41 | ||||
| -rw-r--r-- | clang/test/Analysis/NewDeleteLeaks-PR19102.cpp | 43 |
2 files changed, 84 insertions, 0 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp index a03fa259005..c7a64fb06db 100644 --- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp +++ b/clang/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/clang/test/Analysis/NewDeleteLeaks-PR19102.cpp b/clang/test/Analysis/NewDeleteLeaks-PR19102.cpp new file mode 100644 index 00000000000..559c2d6f0ed --- /dev/null +++ b/clang/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}} +} |

