<div dir="ltr">This has introduced a new warning:<div><br></div><div><div>include/clang/Serialization/ASTReader.h:1746:8: warning: 'ReadMismatchingDeleteExpressions' overrides a member function but is not marked 'override' [-Winconsistent-missing-override]</div><div> void ReadMismatchingDeleteExpressions(llvm::MapVector<</div><div> ^</div><div>include/clang/Sema/ExternalSemaSource.h:83:16: note: overridden virtual function is here</div><div> virtual void ReadMismatchingDeleteExpressions(llvm::MapVector<</div><div> ^</div><div><br></div><div>Please can you fix this?</div><div class="gmail_extra"><br><div class="gmail_quote">On Mon, May 18, 2015 at 12:59 PM, Ismail Pazarbasi <span dir="ltr"><<a href="mailto:ismail.pazarbasi@gmail.com" target="_blank">ismail.pazarbasi@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex">Author: ismailp<br>
Date: Mon May 18 14:59:11 2015<br>
New Revision: 237608<br>
<br>
URL: <a href="http://llvm.org/viewvc/llvm-project?rev=237608&view=rev" target="_blank">http://llvm.org/viewvc/llvm-project?rev=237608&view=rev</a><br>
Log:<br>
Detect uses of mismatching forms of 'new' and 'delete'<br>
<br>
Emit warning when operand to `delete` is allocated with `new[]` or<br>
operand to `delete[]` is allocated with `new`.<br>
<br>
rev 2 update:<br>
`getNewExprFromInitListOrExpr` should return `dyn_cast_or_null`<br>
instead of `dyn_cast`, since `E` might be null.<br>
<br>
Reviewers: rtrieu, jordan_rose, rsmith<br>
<br>
Subscribers: majnemer, cfe-commits<br>
<br>
Differential Revision: <a href="http://reviews.llvm.org/D4661" target="_blank">http://reviews.llvm.org/D4661</a><br>
<br>
Added:<br>
cfe/trunk/test/SemaCXX/delete-mismatch.h<br>
Modified:<br>
cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td<br>
cfe/trunk/include/clang/Sema/ExternalSemaSource.h<br>
cfe/trunk/include/clang/Sema/MultiplexExternalSemaSource.h<br>
cfe/trunk/include/clang/Sema/Sema.h<br>
cfe/trunk/include/clang/Serialization/ASTBitCodes.h<br>
cfe/trunk/include/clang/Serialization/ASTReader.h<br>
cfe/trunk/lib/Sema/MultiplexExternalSemaSource.cpp<br>
cfe/trunk/lib/Sema/Sema.cpp<br>
cfe/trunk/lib/Sema/SemaExprCXX.cpp<br>
cfe/trunk/lib/Serialization/ASTReader.cpp<br>
cfe/trunk/lib/Serialization/ASTWriter.cpp<br>
cfe/trunk/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp<br>
cfe/trunk/test/Analysis/MismatchedDeallocator-checker-test.mm<br>
cfe/trunk/test/Analysis/MismatchedDeallocator-path-notes.cpp<br>
cfe/trunk/test/CodeGenCXX/new.cpp<br>
cfe/trunk/test/SemaCXX/delete.cpp<br>
<br>
Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=237608&r1=237607&r2=237608&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=237608&r1=237607&r2=237608&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)<br>
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon May 18 14:59:11 2015<br>
@@ -5516,7 +5516,12 @@ def err_delete_explicit_conversion : Err<br>
"conversion function">;<br>
def note_delete_conversion : Note<"conversion to pointer type %0">;<br>
def warn_delete_array_type : Warning<<br>
- "'delete' applied to a pointer-to-array type %0 treated as delete[]">;<br>
+ "'delete' applied to a pointer-to-array type %0 treated as 'delete[]'">;<br>
+def warn_mismatched_delete_new : Warning<<br>
+ "'delete%select{|[]}0' applied to a pointer that was allocated with "<br>
+ "'new%select{[]|}0'; did you mean 'delete%select{[]|}0'?">,<br>
+ InGroup<DiagGroup<"mismatched-new-delete">>;<br>
+def note_allocated_here : Note<"allocated with 'new%select{[]|}0' here">;<br>
def err_no_suitable_delete_member_function_found : Error<<br>
"no suitable member %0 in %1">;<br>
def err_ambiguous_suitable_delete_member_function_found : Error<<br>
<br>
Modified: cfe/trunk/include/clang/Sema/ExternalSemaSource.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/ExternalSemaSource.h?rev=237608&r1=237607&r2=237608&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/ExternalSemaSource.h?rev=237608&r1=237607&r2=237608&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Sema/ExternalSemaSource.h (original)<br>
+++ cfe/trunk/include/clang/Sema/ExternalSemaSource.h Mon May 18 14:59:11 2015<br>
@@ -27,6 +27,7 @@ template <class T, unsigned n> class Sma<br>
namespace clang {<br>
<br>
class CXXConstructorDecl;<br>
+class CXXDeleteExpr;<br>
class CXXRecordDecl;<br>
class DeclaratorDecl;<br>
class LookupResult;<br>
@@ -79,6 +80,9 @@ public:<br>
virtual void ReadUndefinedButUsed(<br>
llvm::DenseMap<NamedDecl*, SourceLocation> &Undefined);<br>
<br>
+ virtual void ReadMismatchingDeleteExpressions(llvm::MapVector<<br>
+ FieldDecl *, llvm::SmallVector<std::pair<SourceLocation, bool>, 4>> &);<br>
+<br>
/// \brief Do last resort, unqualified lookup on a LookupResult that<br>
/// Sema cannot find.<br>
///<br>
<br>
Modified: cfe/trunk/include/clang/Sema/MultiplexExternalSemaSource.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/MultiplexExternalSemaSource.h?rev=237608&r1=237607&r2=237608&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/MultiplexExternalSemaSource.h?rev=237608&r1=237607&r2=237608&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Sema/MultiplexExternalSemaSource.h (original)<br>
+++ cfe/trunk/include/clang/Sema/MultiplexExternalSemaSource.h Mon May 18 14:59:11 2015<br>
@@ -230,6 +230,10 @@ public:<br>
void ReadUndefinedButUsed(<br>
llvm::DenseMap<NamedDecl*, SourceLocation> &Undefined) override;<br>
<br>
+ void ReadMismatchingDeleteExpressions(llvm::MapVector<<br>
+ FieldDecl *, llvm::SmallVector<std::pair<SourceLocation, bool>, 4>> &<br>
+ Exprs) override;<br>
+<br>
/// \brief Do last resort, unqualified lookup on a LookupResult that<br>
/// Sema cannot find.<br>
///<br>
<br>
Modified: cfe/trunk/include/clang/Sema/Sema.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=237608&r1=237607&r2=237608&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=237608&r1=237607&r2=237608&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Sema/Sema.h (original)<br>
+++ cfe/trunk/include/clang/Sema/Sema.h Mon May 18 14:59:11 2015<br>
@@ -78,6 +78,7 @@ namespace clang {<br>
typedef SmallVector<CXXBaseSpecifier*, 4> CXXCastPath;<br>
class CXXConstructorDecl;<br>
class CXXConversionDecl;<br>
+ class CXXDeleteExpr;<br>
class CXXDestructorDecl;<br>
class CXXFieldCollector;<br>
class CXXMemberCallExpr;<br>
@@ -404,6 +405,15 @@ public:<br>
llvm::SmallSetVector<const TypedefNameDecl *, 4><br>
UnusedLocalTypedefNameCandidates;<br>
<br>
+ /// \brief Delete-expressions to be analyzed at the end of translation unit<br>
+ ///<br>
+ /// This list contains class members, and locations of delete-expressions<br>
+ /// that could not be proven as to whether they mismatch with new-expression<br>
+ /// used in initializer of the field.<br>
+ typedef std::pair<SourceLocation, bool> DeleteExprLoc;<br>
+ typedef llvm::SmallVector<DeleteExprLoc, 4> DeleteLocs;<br>
+ llvm::MapVector<FieldDecl *, DeleteLocs> DeleteExprs;<br>
+<br>
typedef llvm::SmallPtrSet<const CXXRecordDecl*, 8> RecordDeclSetTy;<br>
<br>
/// PureVirtualClassDiagSet - a set of class declarations which we have<br>
@@ -888,6 +898,11 @@ public:<br>
void getUndefinedButUsed(<br>
SmallVectorImpl<std::pair<NamedDecl *, SourceLocation> > &Undefined);<br>
<br>
+ /// Retrieves list of suspicious delete-expressions that will be checked at<br>
+ /// the end of translation unit.<br>
+ const llvm::MapVector<FieldDecl *, DeleteLocs> &<br>
+ getMismatchingDeleteExpressions() const;<br>
+<br>
typedef std::pair<ObjCMethodList, ObjCMethodList> GlobalMethods;<br>
typedef llvm::DenseMap<Selector, GlobalMethods> GlobalMethodPool;<br>
<br>
@@ -8663,6 +8678,9 @@ private:<br>
/// attempts to add itself into the container<br>
void CheckObjCCircularContainer(ObjCMessageExpr *Message);<br>
<br>
+ void AnalyzeDeleteExprMismatch(const CXXDeleteExpr *DE);<br>
+ void AnalyzeDeleteExprMismatch(FieldDecl *Field, SourceLocation DeleteLoc,<br>
+ bool DeleteWasArrayForm);<br>
public:<br>
/// \brief Register a magic integral constant to be used as a type tag.<br>
void RegisterTypeTagForDatatype(const IdentifierInfo *ArgumentKind,<br>
<br>
Modified: cfe/trunk/include/clang/Serialization/ASTBitCodes.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Serialization/ASTBitCodes.h?rev=237608&r1=237607&r2=237608&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Serialization/ASTBitCodes.h?rev=237608&r1=237607&r2=237608&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Serialization/ASTBitCodes.h (original)<br>
+++ cfe/trunk/include/clang/Serialization/ASTBitCodes.h Mon May 18 14:59:11 2015<br>
@@ -561,6 +561,9 @@ namespace clang {<br>
/// \brief Record code for the table of offsets to CXXCtorInitializers<br>
/// lists.<br>
CXX_CTOR_INITIALIZERS_OFFSETS = 53,<br>
+<br>
+ /// \brief Delete expressions that will be analyzed later.<br>
+ DELETE_EXPRS_TO_ANALYZE = 54<br>
};<br>
<br>
/// \brief Record types used within a source manager block.<br>
<br>
Modified: cfe/trunk/include/clang/Serialization/ASTReader.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Serialization/ASTReader.h?rev=237608&r1=237607&r2=237608&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Serialization/ASTReader.h?rev=237608&r1=237607&r2=237608&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/include/clang/Serialization/ASTReader.h (original)<br>
+++ cfe/trunk/include/clang/Serialization/ASTReader.h Mon May 18 14:59:11 2015<br>
@@ -760,6 +760,9 @@ private:<br>
/// SourceLocation of a matching ODR-use.<br>
SmallVector<uint64_t, 8> UndefinedButUsed;<br>
<br>
+ /// \brief Delete expressions to analyze at the end of translation unit.<br>
+ SmallVector<uint64_t, 8> DelayedDeleteExprs;<br>
+<br>
// \brief A list of late parsed template function data.<br>
SmallVector<uint64_t, 1> LateParsedTemplates;<br>
<br>
@@ -1740,6 +1743,10 @@ public:<br>
void ReadUndefinedButUsed(<br>
llvm::DenseMap<NamedDecl *, SourceLocation> &Undefined) override;<br>
<br>
+ void ReadMismatchingDeleteExpressions(llvm::MapVector<<br>
+ FieldDecl *, llvm::SmallVector<std::pair<SourceLocation, bool>, 4>> &<br>
+ Exprs);<br>
+<br>
void ReadTentativeDefinitions(<br>
SmallVectorImpl<VarDecl *> &TentativeDefs) override;<br>
<br>
<br>
Modified: cfe/trunk/lib/Sema/MultiplexExternalSemaSource.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/MultiplexExternalSemaSource.cpp?rev=237608&r1=237607&r2=237608&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/MultiplexExternalSemaSource.cpp?rev=237608&r1=237607&r2=237608&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Sema/MultiplexExternalSemaSource.cpp (original)<br>
+++ cfe/trunk/lib/Sema/MultiplexExternalSemaSource.cpp Mon May 18 14:59:11 2015<br>
@@ -212,7 +212,15 @@ void MultiplexExternalSemaSource::ReadUn<br>
for(size_t i = 0; i < Sources.size(); ++i)<br>
Sources[i]->ReadUndefinedButUsed(Undefined);<br>
}<br>
-<br>
+<br>
+void MultiplexExternalSemaSource::ReadMismatchingDeleteExpressions(<br>
+ llvm::MapVector<FieldDecl *,<br>
+ llvm::SmallVector<std::pair<SourceLocation, bool>, 4>> &<br>
+ Exprs) {<br>
+ for (auto &Source : Sources)<br>
+ Source->ReadMismatchingDeleteExpressions(Exprs);<br>
+}<br>
+<br>
bool MultiplexExternalSemaSource::LookupUnqualified(LookupResult &R, Scope *S){<br>
for(size_t i = 0; i < Sources.size(); ++i)<br>
Sources[i]->LookupUnqualified(R, S);<br>
<br>
Modified: cfe/trunk/lib/Sema/Sema.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.cpp?rev=237608&r1=237607&r2=237608&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.cpp?rev=237608&r1=237607&r2=237608&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Sema/Sema.cpp (original)<br>
+++ cfe/trunk/lib/Sema/Sema.cpp Mon May 18 14:59:11 2015<br>
@@ -861,6 +861,17 @@ void Sema::ActOnEndOfTranslationUnit() {<br>
}<br>
}<br>
<br>
+ if (!Diags.isIgnored(diag::warn_mismatched_delete_new, SourceLocation())) {<br>
+ if (ExternalSource)<br>
+ ExternalSource->ReadMismatchingDeleteExpressions(DeleteExprs);<br>
+ for (const auto &DeletedFieldInfo : DeleteExprs) {<br>
+ for (const auto &DeleteExprLoc : DeletedFieldInfo.second) {<br>
+ AnalyzeDeleteExprMismatch(DeletedFieldInfo.first, DeleteExprLoc.first,<br>
+ DeleteExprLoc.second);<br>
+ }<br>
+ }<br>
+ }<br>
+<br>
// Check we've noticed that we're no longer parsing the initializer for every<br>
// variable. If we miss cases, then at best we have a performance issue and<br>
// at worst a rejects-valid bug.<br>
@@ -1220,6 +1231,9 @@ void ExternalSemaSource::ReadUndefinedBu<br>
llvm::DenseMap<NamedDecl *, SourceLocation> &Undefined) {<br>
}<br>
<br>
+void ExternalSemaSource::ReadMismatchingDeleteExpressions(llvm::MapVector<<br>
+ FieldDecl *, llvm::SmallVector<std::pair<SourceLocation, bool>, 4>> &) {}<br>
+<br>
void PrettyDeclStackTraceEntry::print(raw_ostream &OS) const {<br>
SourceLocation Loc = this->Loc;<br>
if (!Loc.isValid() && TheDecl) Loc = TheDecl->getLocation();<br>
@@ -1468,3 +1482,8 @@ CapturedRegionScopeInfo *Sema::getCurCap<br>
<br>
return dyn_cast<CapturedRegionScopeInfo>(FunctionScopes.back());<br>
}<br>
+<br>
+const llvm::MapVector<FieldDecl *, Sema::DeleteLocs> &<br>
+Sema::getMismatchingDeleteExpressions() const {<br>
+ return DeleteExprs;<br>
+}<br>
<br>
Modified: cfe/trunk/lib/Sema/SemaExprCXX.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=237608&r1=237607&r2=237608&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExprCXX.cpp?rev=237608&r1=237607&r2=237608&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Sema/SemaExprCXX.cpp (original)<br>
+++ cfe/trunk/lib/Sema/SemaExprCXX.cpp Mon May 18 14:59:11 2015<br>
@@ -2339,6 +2339,261 @@ bool Sema::FindDeallocationFunction(Sour<br>
return false;<br>
}<br>
<br>
+namespace {<br>
+/// \brief Checks whether delete-expression, and new-expression used for<br>
+/// initializing deletee have the same array form.<br>
+class MismatchingNewDeleteDetector {<br>
+public:<br>
+ enum MismatchResult {<br>
+ /// Indicates that there is no mismatch or a mismatch cannot be proven.<br>
+ NoMismatch,<br>
+ /// Indicates that variable is initialized with mismatching form of \a new.<br>
+ VarInitMismatches,<br>
+ /// Indicates that member is initialized with mismatching form of \a new.<br>
+ MemberInitMismatches,<br>
+ /// Indicates that 1 or more constructors' definitions could not been<br>
+ /// analyzed, and they will be checked again at the end of translation unit.<br>
+ AnalyzeLater<br>
+ };<br>
+<br>
+ /// \param EndOfTU True, if this is the final analysis at the end of<br>
+ /// translation unit. False, if this is the initial analysis at the point<br>
+ /// delete-expression was encountered.<br>
+ explicit MismatchingNewDeleteDetector(bool EndOfTU)<br>
+ : IsArrayForm(false), Field(nullptr), EndOfTU(EndOfTU),<br>
+ HasUndefinedConstructors(false) {}<br>
+<br>
+ /// \brief Checks whether pointee of a delete-expression is initialized with<br>
+ /// matching form of new-expression.<br>
+ ///<br>
+ /// If return value is \c VarInitMismatches or \c MemberInitMismatches at the<br>
+ /// point where delete-expression is encountered, then a warning will be<br>
+ /// issued immediately. If return value is \c AnalyzeLater at the point where<br>
+ /// delete-expression is seen, then member will be analyzed at the end of<br>
+ /// translation unit. \c AnalyzeLater is returned iff at least one constructor<br>
+ /// couldn't be analyzed. If at least one constructor initializes the member<br>
+ /// with matching type of new, the return value is \c NoMismatch.<br>
+ MismatchResult analyzeDeleteExpr(const CXXDeleteExpr *DE);<br>
+ /// \brief Analyzes a class member.<br>
+ /// \param Field Class member to analyze.<br>
+ /// \param DeleteWasArrayForm Array form-ness of the delete-expression used<br>
+ /// for deleting the \p Field.<br>
+ MismatchResult analyzeField(FieldDecl *Field, bool DeleteWasArrayForm);<br>
+ /// List of mismatching new-expressions used for initialization of the pointee<br>
+ llvm::SmallVector<const CXXNewExpr *, 4> NewExprs;<br>
+ /// Indicates whether delete-expression was in array form.<br>
+ bool IsArrayForm;<br>
+ FieldDecl *Field;<br>
+<br>
+private:<br>
+ const bool EndOfTU;<br>
+ /// \brief Indicates that there is at least one constructor without body.<br>
+ bool HasUndefinedConstructors;<br>
+ /// \brief Returns \c CXXNewExpr from given initialization expression.<br>
+ /// \param E Expression used for initializing pointee in delete-expression.<br>
+ /// \param E can be a single-element \c InitListExpr consisting of<br>
+ /// \param E new-expression.<br>
+ const CXXNewExpr *getNewExprFromInitListOrExpr(const Expr *E);<br>
+ /// \brief Returns whether member is initialized with mismatching form of<br>
+ /// \c new either by the member initializer or in-class initialization.<br>
+ ///<br>
+ /// If bodies of all constructors are not visible at the end of translation<br>
+ /// unit or at least one constructor initializes member with the matching<br>
+ /// form of \c new, mismatch cannot be proven, and this function will return<br>
+ /// \c NoMismatch.<br>
+ MismatchResult analyzeMemberExpr(const MemberExpr *ME);<br>
+ /// \brief Returns whether variable is initialized with mismatching form of<br>
+ /// \c new.<br>
+ ///<br>
+ /// If variable is initialized with matching form of \c new or variable is not<br>
+ /// initialized with a \c new expression, this function will return true.<br>
+ /// If variable is initialized with mismatching form of \c new, returns false.<br>
+ /// \param D Variable to analyze.<br>
+ bool hasMatchingVarInit(const DeclRefExpr *D);<br>
+ /// \brief Checks whether the constructor initializes pointee with mismatching<br>
+ /// form of \c new.<br>
+ ///<br>
+ /// Returns true, if member is initialized with matching form of \c new in<br>
+ /// member initializer list. Returns false, if member is initialized with the<br>
+ /// matching form of \c new in this constructor's initializer or given<br>
+ /// constructor isn't defined at the point where delete-expression is seen, or<br>
+ /// member isn't initialized by the constructor.<br>
+ bool hasMatchingNewInCtor(const CXXConstructorDecl *CD);<br>
+ /// \brief Checks whether member is initialized with matching form of<br>
+ /// \c new in member initializer list.<br>
+ bool hasMatchingNewInCtorInit(const CXXCtorInitializer *CI);<br>
+ /// Checks whether member is initialized with mismatching form of \c new by<br>
+ /// in-class initializer.<br>
+ MismatchResult analyzeInClassInitializer();<br>
+};<br>
+}<br>
+<br>
+MismatchingNewDeleteDetector::MismatchResult<br>
+MismatchingNewDeleteDetector::analyzeDeleteExpr(const CXXDeleteExpr *DE) {<br>
+ NewExprs.clear();<br>
+ assert(DE && "Expected delete-expression");<br>
+ IsArrayForm = DE->isArrayForm();<br>
+ const Expr *E = DE->getArgument()->IgnoreParenImpCasts();<br>
+ if (const MemberExpr *ME = dyn_cast<const MemberExpr>(E)) {<br>
+ return analyzeMemberExpr(ME);<br>
+ } else if (const DeclRefExpr *D = dyn_cast<const DeclRefExpr>(E)) {<br>
+ if (!hasMatchingVarInit(D))<br>
+ return VarInitMismatches;<br>
+ }<br>
+ return NoMismatch;<br>
+}<br>
+<br>
+const CXXNewExpr *<br>
+MismatchingNewDeleteDetector::getNewExprFromInitListOrExpr(const Expr *E) {<br>
+ assert(E != nullptr && "Expected a valid initializer expression");<br>
+ E = E->IgnoreParenImpCasts();<br>
+ if (const InitListExpr *ILE = dyn_cast<const InitListExpr>(E)) {<br>
+ if (ILE->getNumInits() == 1)<br>
+ E = dyn_cast<const CXXNewExpr>(ILE->getInit(0)->IgnoreParenImpCasts());<br>
+ }<br>
+<br>
+ return dyn_cast_or_null<const CXXNewExpr>(E);<br>
+}<br>
+<br>
+bool MismatchingNewDeleteDetector::hasMatchingNewInCtorInit(<br>
+ const CXXCtorInitializer *CI) {<br>
+ const CXXNewExpr *NE = nullptr;<br>
+ if (Field == CI->getMember() &&<br>
+ (NE = getNewExprFromInitListOrExpr(CI->getInit()))) {<br>
+ if (NE->isArray() == IsArrayForm)<br>
+ return true;<br>
+ else<br>
+ NewExprs.push_back(NE);<br>
+ }<br>
+ return false;<br>
+}<br>
+<br>
+bool MismatchingNewDeleteDetector::hasMatchingNewInCtor(<br>
+ const CXXConstructorDecl *CD) {<br>
+ if (CD->isImplicit())<br>
+ return false;<br>
+ const FunctionDecl *Definition = CD;<br>
+ if (!CD->isThisDeclarationADefinition() && !CD->isDefined(Definition)) {<br>
+ HasUndefinedConstructors = true;<br>
+ return EndOfTU;<br>
+ }<br>
+ for (const auto *CI : cast<const CXXConstructorDecl>(Definition)->inits()) {<br>
+ if (hasMatchingNewInCtorInit(CI))<br>
+ return true;<br>
+ }<br>
+ return false;<br>
+}<br>
+<br>
+MismatchingNewDeleteDetector::MismatchResult<br>
+MismatchingNewDeleteDetector::analyzeInClassInitializer() {<br>
+ assert(Field != nullptr && "This should be called only for members");<br>
+ if (const CXXNewExpr *NE =<br>
+ getNewExprFromInitListOrExpr(Field->getInClassInitializer())) {<br>
+ if (NE->isArray() != IsArrayForm) {<br>
+ NewExprs.push_back(NE);<br>
+ return MemberInitMismatches;<br>
+ }<br>
+ }<br>
+ return NoMismatch;<br>
+}<br>
+<br>
+MismatchingNewDeleteDetector::MismatchResult<br>
+MismatchingNewDeleteDetector::analyzeField(FieldDecl *Field,<br>
+ bool DeleteWasArrayForm) {<br>
+ assert(Field != nullptr && "Analysis requires a valid class member.");<br>
+ this->Field = Field;<br>
+ IsArrayForm = DeleteWasArrayForm;<br>
+ const CXXRecordDecl *RD = cast<const CXXRecordDecl>(Field->getParent());<br>
+ for (const auto *CD : RD->ctors()) {<br>
+ if (hasMatchingNewInCtor(CD))<br>
+ return NoMismatch;<br>
+ }<br>
+ if (HasUndefinedConstructors)<br>
+ return EndOfTU ? NoMismatch : AnalyzeLater;<br>
+ if (!NewExprs.empty())<br>
+ return MemberInitMismatches;<br>
+ return Field->hasInClassInitializer() ? analyzeInClassInitializer()<br>
+ : NoMismatch;<br>
+}<br>
+<br>
+MismatchingNewDeleteDetector::MismatchResult<br>
+MismatchingNewDeleteDetector::analyzeMemberExpr(const MemberExpr *ME) {<br>
+ assert(ME != nullptr && "Expected a member expression");<br>
+ if (FieldDecl *F = dyn_cast<FieldDecl>(ME->getMemberDecl()))<br>
+ return analyzeField(F, IsArrayForm);<br>
+ return NoMismatch;<br>
+}<br>
+<br>
+bool MismatchingNewDeleteDetector::hasMatchingVarInit(const DeclRefExpr *D) {<br>
+ const CXXNewExpr *NE = nullptr;<br>
+ if (const VarDecl *VD = dyn_cast<const VarDecl>(D->getDecl())) {<br>
+ if (VD->hasInit() && (NE = getNewExprFromInitListOrExpr(VD->getInit())) &&<br>
+ NE->isArray() != IsArrayForm) {<br>
+ NewExprs.push_back(NE);<br>
+ }<br>
+ }<br>
+ return NewExprs.empty();<br>
+}<br>
+<br>
+static void<br>
+DiagnoseMismatchedNewDelete(Sema &SemaRef, SourceLocation DeleteLoc,<br>
+ const MismatchingNewDeleteDetector &Detector) {<br>
+ SourceLocation EndOfDelete = SemaRef.getLocForEndOfToken(DeleteLoc);<br>
+ FixItHint H;<br>
+ if (!Detector.IsArrayForm)<br>
+ H = FixItHint::CreateInsertion(EndOfDelete, "[]");<br>
+ else {<br>
+ SourceLocation RSquare = Lexer::findLocationAfterToken(<br>
+ DeleteLoc, tok::l_square, SemaRef.getSourceManager(),<br>
+ SemaRef.getLangOpts(), true);<br>
+ if (RSquare.isValid())<br>
+ H = FixItHint::CreateRemoval(SourceRange(EndOfDelete, RSquare));<br>
+ }<br>
+ SemaRef.Diag(DeleteLoc, diag::warn_mismatched_delete_new)<br>
+ << Detector.IsArrayForm << H;<br>
+<br>
+ for (const auto *NE : Detector.NewExprs)<br>
+ SemaRef.Diag(NE->getExprLoc(), diag::note_allocated_here)<br>
+ << Detector.IsArrayForm;<br>
+}<br>
+<br>
+void Sema::AnalyzeDeleteExprMismatch(const CXXDeleteExpr *DE) {<br>
+ if (Diags.isIgnored(diag::warn_mismatched_delete_new, SourceLocation()))<br>
+ return;<br>
+ MismatchingNewDeleteDetector Detector(/*EndOfTU=*/false);<br>
+ switch (Detector.analyzeDeleteExpr(DE)) {<br>
+ case MismatchingNewDeleteDetector::VarInitMismatches:<br>
+ case MismatchingNewDeleteDetector::MemberInitMismatches: {<br>
+ DiagnoseMismatchedNewDelete(*this, DE->getLocStart(), Detector);<br>
+ break;<br>
+ }<br>
+ case MismatchingNewDeleteDetector::AnalyzeLater: {<br>
+ DeleteExprs[Detector.Field].push_back(<br>
+ std::make_pair(DE->getLocStart(), DE->isArrayForm()));<br>
+ break;<br>
+ }<br>
+ case MismatchingNewDeleteDetector::NoMismatch:<br>
+ break;<br>
+ }<br>
+}<br>
+<br>
+void Sema::AnalyzeDeleteExprMismatch(FieldDecl *Field, SourceLocation DeleteLoc,<br>
+ bool DeleteWasArrayForm) {<br>
+ MismatchingNewDeleteDetector Detector(/*EndOfTU=*/true);<br>
+ switch (Detector.analyzeField(Field, DeleteWasArrayForm)) {<br>
+ case MismatchingNewDeleteDetector::VarInitMismatches:<br>
+ llvm_unreachable("This analysis should have been done for class members.");<br>
+ case MismatchingNewDeleteDetector::AnalyzeLater:<br>
+ llvm_unreachable("Analysis cannot be postponed any point beyond end of "<br>
+ "translation unit.");<br>
+ case MismatchingNewDeleteDetector::MemberInitMismatches:<br>
+ DiagnoseMismatchedNewDelete(*this, DeleteLoc, Detector);<br>
+ break;<br>
+ case MismatchingNewDeleteDetector::NoMismatch:<br>
+ break;<br>
+ }<br>
+}<br>
+<br>
/// ActOnCXXDelete - Parsed a C++ 'delete' expression (C++ 5.3.5), as in:<br>
/// @code ::delete ptr; @endcode<br>
/// or<br>
@@ -2454,12 +2709,6 @@ Sema::ActOnCXXDelete(SourceLocation Star<br>
}<br>
}<br>
<br>
- // C++ [expr.delete]p2:<br>
- // [Note: a pointer to a const type can be the operand of a<br>
- // delete-expression; it is not necessary to cast away the constness<br>
- // (5.2.11) of the pointer expression before it is used as the operand<br>
- // of the delete-expression. ]<br>
-<br>
if (Pointee->isArrayType() && !ArrayForm) {<br>
Diag(StartLoc, diag::warn_delete_array_type)<br>
<< Type << Ex.get()->getSourceRange()<br>
@@ -2534,7 +2783,7 @@ Sema::ActOnCXXDelete(SourceLocation Star<br>
DeleteName);<br>
<br>
MarkFunctionReferenced(StartLoc, OperatorDelete);<br>
-<br>
+<br>
// Check access and ambiguity of operator delete and destructor.<br>
if (PointeeRD) {<br>
if (CXXDestructorDecl *Dtor = LookupDestructor(PointeeRD)) {<br>
@@ -2544,9 +2793,11 @@ Sema::ActOnCXXDelete(SourceLocation Star<br>
}<br>
}<br>
<br>
- return new (Context) CXXDeleteExpr(<br>
+ CXXDeleteExpr *Result = new (Context) CXXDeleteExpr(<br>
Context.VoidTy, UseGlobal, ArrayForm, ArrayFormAsWritten,<br>
UsualArrayDeleteWantsSize, OperatorDelete, Ex.get(), StartLoc);<br>
+ AnalyzeDeleteExprMismatch(Result);<br>
+ return Result;<br>
}<br>
<br>
/// \brief Check the use of the given variable as a C++ condition in an if,<br>
<br>
Modified: cfe/trunk/lib/Serialization/ASTReader.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReader.cpp?rev=237608&r1=237607&r2=237608&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTReader.cpp?rev=237608&r1=237607&r2=237608&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Serialization/ASTReader.cpp (original)<br>
+++ cfe/trunk/lib/Serialization/ASTReader.cpp Mon May 18 14:59:11 2015<br>
@@ -3021,6 +3021,18 @@ ASTReader::ReadASTBlock(ModuleFile &F, u<br>
ReadSourceLocation(F, Record, I).getRawEncoding());<br>
}<br>
break;<br>
+ case DELETE_EXPRS_TO_ANALYZE:<br>
+ for (unsigned I = 0, N = Record.size(); I != N;) {<br>
+ DelayedDeleteExprs.push_back(getGlobalDeclID(F, Record[I++]));<br>
+ const uint64_t Count = Record[I++];<br>
+ DelayedDeleteExprs.push_back(Count);<br>
+ for (uint64_t C = 0; C < Count; ++C) {<br>
+ DelayedDeleteExprs.push_back(ReadSourceLocation(F, Record, I).getRawEncoding());<br>
+ bool IsArrayForm = Record[I++] == 1;<br>
+ DelayedDeleteExprs.push_back(IsArrayForm);<br>
+ }<br>
+ }<br>
+ break;<br>
<br>
case IMPORTED_MODULES: {<br>
if (F.Kind != MK_ImplicitModule && F.Kind != MK_ExplicitModule) {<br>
@@ -7016,6 +7028,21 @@ void ASTReader::ReadUndefinedButUsed(<br>
}<br>
}<br>
<br>
+void ASTReader::ReadMismatchingDeleteExpressions(llvm::MapVector<<br>
+ FieldDecl *, llvm::SmallVector<std::pair<SourceLocation, bool>, 4>> &<br>
+ Exprs) {<br>
+ for (unsigned Idx = 0, N = DelayedDeleteExprs.size(); Idx != N;) {<br>
+ FieldDecl *FD = cast<FieldDecl>(GetDecl(DelayedDeleteExprs[Idx++]));<br>
+ uint64_t Count = DelayedDeleteExprs[Idx++];<br>
+ for (uint64_t C = 0; C < Count; ++C) {<br>
+ SourceLocation DeleteLoc =<br>
+ SourceLocation::getFromRawEncoding(DelayedDeleteExprs[Idx++]);<br>
+ const bool IsArrayForm = DelayedDeleteExprs[Idx++];<br>
+ Exprs[FD].push_back(std::make_pair(DeleteLoc, IsArrayForm));<br>
+ }<br>
+ }<br>
+}<br>
+<br>
void ASTReader::ReadTentativeDefinitions(<br>
SmallVectorImpl<VarDecl *> &TentativeDefs) {<br>
for (unsigned I = 0, N = TentativeDefinitions.size(); I != N; ++I) {<br>
<br>
Modified: cfe/trunk/lib/Serialization/ASTWriter.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriter.cpp?rev=237608&r1=237607&r2=237608&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Serialization/ASTWriter.cpp?rev=237608&r1=237607&r2=237608&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/lib/Serialization/ASTWriter.cpp (original)<br>
+++ cfe/trunk/lib/Serialization/ASTWriter.cpp Mon May 18 14:59:11 2015<br>
@@ -4155,6 +4155,20 @@ void ASTWriter::WriteASTCore(Sema &SemaR<br>
AddSourceLocation(I->second, UndefinedButUsed);<br>
}<br>
<br>
+ // Build a record containing all delete-expressions that we would like to<br>
+ // analyze later in AST.<br>
+ RecordData DeleteExprsToAnalyze;<br>
+<br>
+ for (const auto &DeleteExprsInfo :<br>
+ SemaRef.getMismatchingDeleteExpressions()) {<br>
+ AddDeclRef(DeleteExprsInfo.first, DeleteExprsToAnalyze);<br>
+ DeleteExprsToAnalyze.push_back(DeleteExprsInfo.second.size());<br>
+ for (const auto &DeleteLoc : DeleteExprsInfo.second) {<br>
+ AddSourceLocation(DeleteLoc.first, DeleteExprsToAnalyze);<br>
+ DeleteExprsToAnalyze.push_back(DeleteLoc.second);<br>
+ }<br>
+ }<br>
+<br>
// Write the control block<br>
WriteControlBlock(PP, Context, isysroot, OutputFile);<br>
<br>
@@ -4424,7 +4438,10 @@ void ASTWriter::WriteASTCore(Sema &SemaR<br>
// Write the undefined internal functions and variables, and inline functions.<br>
if (!UndefinedButUsed.empty())<br>
Stream.EmitRecord(UNDEFINED_BUT_USED, UndefinedButUsed);<br>
-<br>
+<br>
+ if (!DeleteExprsToAnalyze.empty())<br>
+ Stream.EmitRecord(DELETE_EXPRS_TO_ANALYZE, DeleteExprsToAnalyze);<br>
+<br>
// Write the visible updates to DeclContexts.<br>
for (auto *DC : UpdatedDeclContexts)<br>
WriteDeclContextVisibleUpdate(DC);<br>
<br>
Modified: cfe/trunk/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/Malloc%2BMismatchedDeallocator%2BNewDelete.cpp?rev=237608&r1=237607&r2=237608&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/Malloc%2BMismatchedDeallocator%2BNewDelete.cpp?rev=237608&r1=237607&r2=237608&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp (original)<br>
+++ cfe/trunk/test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp Mon May 18 14:59:11 2015<br>
@@ -97,9 +97,11 @@ void testShouldReportDoubleFreeNotMismat<br>
free(p);<br>
delete globalPtr; // expected-warning {{Attempt to free released memory}}<br>
}<br>
-<br>
+int *allocIntArray(unsigned c) {<br>
+ return new int[c];<br>
+}<br>
void testMismatchedChangePointeeThroughAssignment() {<br>
- int *arr = new int[4];<br>
+ int *arr = allocIntArray(4);<br>
globalPtr = arr;<br>
delete arr; // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'}}<br>
-}<br>
\ No newline at end of file<br>
+}<br>
<br>
Modified: cfe/trunk/test/Analysis/MismatchedDeallocator-checker-test.mm<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/MismatchedDeallocator-checker-test.mm?rev=237608&r1=237607&r2=237608&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/MismatchedDeallocator-checker-test.mm?rev=237608&r1=237607&r2=237608&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/Analysis/MismatchedDeallocator-checker-test.mm (original)<br>
+++ cfe/trunk/test/Analysis/MismatchedDeallocator-checker-test.mm Mon May 18 14:59:11 2015<br>
@@ -95,8 +95,11 @@ void testNew6() {<br>
realloc(p, sizeof(long)); // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not realloc()}}<br>
}<br>
<br>
+int *allocInt() {<br>
+ return new int;<br>
+}<br>
void testNew7() {<br>
- int *p = new int;<br>
+ int *p = allocInt();<br>
delete[] p; // expected-warning{{Memory allocated by 'new' should be deallocated by 'delete', not 'delete[]'}}<br>
}<br>
<br>
@@ -105,8 +108,12 @@ void testNew8() {<br>
delete[] p; // expected-warning{{Memory allocated by operator new should be deallocated by 'delete', not 'delete[]'}}<br>
}<br>
<br>
+int *allocIntArray(unsigned c) {<br>
+ return new int[c];<br>
+}<br>
+<br>
void testNew9() {<br>
- int *p = new int[1];<br>
+ int *p = allocIntArray(1);<br>
delete p; // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'}}<br>
}<br>
<br>
<br>
Modified: cfe/trunk/test/Analysis/MismatchedDeallocator-path-notes.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/MismatchedDeallocator-path-notes.cpp?rev=237608&r1=237607&r2=237608&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Analysis/MismatchedDeallocator-path-notes.cpp?rev=237608&r1=237607&r2=237608&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/Analysis/MismatchedDeallocator-path-notes.cpp (original)<br>
+++ cfe/trunk/test/Analysis/MismatchedDeallocator-path-notes.cpp Mon May 18 14:59:11 2015<br>
@@ -3,9 +3,12 @@<br>
// RUN: FileCheck --input-file=%t.plist %s<br>
<br>
void changePointee(int *p);<br>
+int *allocIntArray(unsigned c) {<br>
+ return new int[c]; // expected-note {{Memory is allocated}}<br>
+}<br>
void test() {<br>
- int *p = new int[1];<br>
- // expected-note@-1 {{Memory is allocated}}<br>
+ int *p = allocIntArray(1); // expected-note {{Calling 'allocIntArray'}}<br>
+ // expected-note@-1 {{Returned allocated memory}}<br>
changePointee(p);<br>
delete p; // expected-warning {{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'}}<br>
// expected-note@-1 {{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'}}<br>
@@ -24,13 +27,124 @@ void test() {<br>
// CHECK-NEXT: <key>start</key><br>
// CHECK-NEXT: <array><br>
// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>line</key><integer>10</integer><br>
+// CHECK-NEXT: <key>col</key><integer>3</integer><br>
+// CHECK-NEXT: <key>file</key><integer>0</integer><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>line</key><integer>10</integer><br>
+// CHECK-NEXT: <key>col</key><integer>5</integer><br>
+// CHECK-NEXT: <key>file</key><integer>0</integer><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: </array><br>
+// CHECK-NEXT: <key>end</key><br>
+// CHECK-NEXT: <array><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>line</key><integer>10</integer><br>
+// CHECK-NEXT: <key>col</key><integer>12</integer><br>
+// CHECK-NEXT: <key>file</key><integer>0</integer><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>line</key><integer>10</integer><br>
+// CHECK-NEXT: <key>col</key><integer>24</integer><br>
+// CHECK-NEXT: <key>file</key><integer>0</integer><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: </array><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: </array><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>kind</key><string>event</string><br>
+// CHECK-NEXT: <key>location</key><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>line</key><integer>10</integer><br>
+// CHECK-NEXT: <key>col</key><integer>12</integer><br>
+// CHECK-NEXT: <key>file</key><integer>0</integer><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: <key>ranges</key><br>
+// CHECK-NEXT: <array><br>
+// CHECK-NEXT: <array><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>line</key><integer>10</integer><br>
+// CHECK-NEXT: <key>col</key><integer>12</integer><br>
+// CHECK-NEXT: <key>file</key><integer>0</integer><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>line</key><integer>10</integer><br>
+// CHECK-NEXT: <key>col</key><integer>27</integer><br>
+// CHECK-NEXT: <key>file</key><integer>0</integer><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: </array><br>
+// CHECK-NEXT: </array><br>
+// CHECK-NEXT: <key>depth</key><integer>0</integer><br>
+// CHECK-NEXT: <key>extended_message</key><br>
+// CHECK-NEXT: <string>Calling 'allocIntArray'</string><br>
+// CHECK-NEXT: <key>message</key><br>
+// CHECK-NEXT: <string>Calling 'allocIntArray'</string><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>kind</key><string>event</string><br>
+// CHECK-NEXT: <key>location</key><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>line</key><integer>6</integer><br>
+// CHECK-NEXT: <key>col</key><integer>1</integer><br>
+// CHECK-NEXT: <key>file</key><integer>0</integer><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: <key>depth</key><integer>1</integer><br>
+// CHECK-NEXT: <key>extended_message</key><br>
+// CHECK-NEXT: <string>Entered call from 'test'</string><br>
+// CHECK-NEXT: <key>message</key><br>
+// CHECK-NEXT: <string>Entered call from 'test'</string><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>kind</key><string>control</string><br>
+// CHECK-NEXT: <key>edges</key><br>
+// CHECK-NEXT: <array><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>start</key><br>
+// CHECK-NEXT: <array><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>line</key><integer>6</integer><br>
+// CHECK-NEXT: <key>col</key><integer>1</integer><br>
+// CHECK-NEXT: <key>file</key><integer>0</integer><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>line</key><integer>6</integer><br>
+// CHECK-NEXT: <key>col</key><integer>3</integer><br>
+// CHECK-NEXT: <key>file</key><integer>0</integer><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: </array><br>
+// CHECK-NEXT: <key>end</key><br>
+// CHECK-NEXT: <array><br>
+// CHECK-NEXT: <dict><br>
// CHECK-NEXT: <key>line</key><integer>7</integer><br>
// CHECK-NEXT: <key>col</key><integer>3</integer><br>
// CHECK-NEXT: <key>file</key><integer>0</integer><br>
// CHECK-NEXT: </dict><br>
// CHECK-NEXT: <dict><br>
// CHECK-NEXT: <key>line</key><integer>7</integer><br>
-// CHECK-NEXT: <key>col</key><integer>5</integer><br>
+// CHECK-NEXT: <key>col</key><integer>8</integer><br>
+// CHECK-NEXT: <key>file</key><integer>0</integer><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: </array><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: </array><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>kind</key><string>control</string><br>
+// CHECK-NEXT: <key>edges</key><br>
+// CHECK-NEXT: <array><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>start</key><br>
+// CHECK-NEXT: <array><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>line</key><integer>7</integer><br>
+// CHECK-NEXT: <key>col</key><integer>3</integer><br>
+// CHECK-NEXT: <key>file</key><integer>0</integer><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>line</key><integer>7</integer><br>
+// CHECK-NEXT: <key>col</key><integer>8</integer><br>
// CHECK-NEXT: <key>file</key><integer>0</integer><br>
// CHECK-NEXT: </dict><br>
// CHECK-NEXT: </array><br>
@@ -38,12 +152,12 @@ void test() {<br>
// CHECK-NEXT: <array><br>
// CHECK-NEXT: <dict><br>
// CHECK-NEXT: <key>line</key><integer>7</integer><br>
-// CHECK-NEXT: <key>col</key><integer>12</integer><br>
+// CHECK-NEXT: <key>col</key><integer>10</integer><br>
// CHECK-NEXT: <key>file</key><integer>0</integer><br>
// CHECK-NEXT: </dict><br>
// CHECK-NEXT: <dict><br>
// CHECK-NEXT: <key>line</key><integer>7</integer><br>
-// CHECK-NEXT: <key>col</key><integer>14</integer><br>
+// CHECK-NEXT: <key>col</key><integer>12</integer><br>
// CHECK-NEXT: <key>file</key><integer>0</integer><br>
// CHECK-NEXT: </dict><br>
// CHECK-NEXT: </array><br>
@@ -55,7 +169,7 @@ void test() {<br>
// CHECK-NEXT: <key>location</key><br>
// CHECK-NEXT: <dict><br>
// CHECK-NEXT: <key>line</key><integer>7</integer><br>
-// CHECK-NEXT: <key>col</key><integer>12</integer><br>
+// CHECK-NEXT: <key>col</key><integer>10</integer><br>
// CHECK-NEXT: <key>file</key><integer>0</integer><br>
// CHECK-NEXT: </dict><br>
// CHECK-NEXT: <key>ranges</key><br>
@@ -63,23 +177,52 @@ void test() {<br>
// CHECK-NEXT: <array><br>
// CHECK-NEXT: <dict><br>
// CHECK-NEXT: <key>line</key><integer>7</integer><br>
-// CHECK-NEXT: <key>col</key><integer>12</integer><br>
+// CHECK-NEXT: <key>col</key><integer>10</integer><br>
// CHECK-NEXT: <key>file</key><integer>0</integer><br>
// CHECK-NEXT: </dict><br>
// CHECK-NEXT: <dict><br>
// CHECK-NEXT: <key>line</key><integer>7</integer><br>
-// CHECK-NEXT: <key>col</key><integer>21</integer><br>
+// CHECK-NEXT: <key>col</key><integer>19</integer><br>
// CHECK-NEXT: <key>file</key><integer>0</integer><br>
// CHECK-NEXT: </dict><br>
// CHECK-NEXT: </array><br>
// CHECK-NEXT: </array><br>
-// CHECK-NEXT: <key>depth</key><integer>0</integer><br>
+// CHECK-NEXT: <key>depth</key><integer>1</integer><br>
// CHECK-NEXT: <key>extended_message</key><br>
// CHECK-NEXT: <string>Memory is allocated</string><br>
// CHECK-NEXT: <key>message</key><br>
// CHECK-NEXT: <string>Memory is allocated</string><br>
// CHECK-NEXT: </dict><br>
// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>kind</key><string>event</string><br>
+// CHECK-NEXT: <key>location</key><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>line</key><integer>10</integer><br>
+// CHECK-NEXT: <key>col</key><integer>12</integer><br>
+// CHECK-NEXT: <key>file</key><integer>0</integer><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: <key>ranges</key><br>
+// CHECK-NEXT: <array><br>
+// CHECK-NEXT: <array><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>line</key><integer>10</integer><br>
+// CHECK-NEXT: <key>col</key><integer>12</integer><br>
+// CHECK-NEXT: <key>file</key><integer>0</integer><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: <dict><br>
+// CHECK-NEXT: <key>line</key><integer>10</integer><br>
+// CHECK-NEXT: <key>col</key><integer>27</integer><br>
+// CHECK-NEXT: <key>file</key><integer>0</integer><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: </array><br>
+// CHECK-NEXT: </array><br>
+// CHECK-NEXT: <key>depth</key><integer>0</integer><br>
+// CHECK-NEXT: <key>extended_message</key><br>
+// CHECK-NEXT: <string>Returned allocated memory</string><br>
+// CHECK-NEXT: <key>message</key><br>
+// CHECK-NEXT: <string>Returned allocated memory</string><br>
+// CHECK-NEXT: </dict><br>
+// CHECK-NEXT: <dict><br>
// CHECK-NEXT: <key>kind</key><string>control</string><br>
// CHECK-NEXT: <key>edges</key><br>
// CHECK-NEXT: <array><br>
@@ -87,25 +230,25 @@ void test() {<br>
// CHECK-NEXT: <key>start</key><br>
// CHECK-NEXT: <array><br>
// CHECK-NEXT: <dict><br>
-// CHECK-NEXT: <key>line</key><integer>7</integer><br>
+// CHECK-NEXT: <key>line</key><integer>10</integer><br>
// CHECK-NEXT: <key>col</key><integer>12</integer><br>
// CHECK-NEXT: <key>file</key><integer>0</integer><br>
// CHECK-NEXT: </dict><br>
// CHECK-NEXT: <dict><br>
-// CHECK-NEXT: <key>line</key><integer>7</integer><br>
-// CHECK-NEXT: <key>col</key><integer>14</integer><br>
+// CHECK-NEXT: <key>line</key><integer>10</integer><br>
+// CHECK-NEXT: <key>col</key><integer>24</integer><br>
// CHECK-NEXT: <key>file</key><integer>0</integer><br>
// CHECK-NEXT: </dict><br>
// CHECK-NEXT: </array><br>
// CHECK-NEXT: <key>end</key><br>
// CHECK-NEXT: <array><br>
// CHECK-NEXT: <dict><br>
-// CHECK-NEXT: <key>line</key><integer>10</integer><br>
+// CHECK-NEXT: <key>line</key><integer>13</integer><br>
// CHECK-NEXT: <key>col</key><integer>3</integer><br>
// CHECK-NEXT: <key>file</key><integer>0</integer><br>
// CHECK-NEXT: </dict><br>
// CHECK-NEXT: <dict><br>
-// CHECK-NEXT: <key>line</key><integer>10</integer><br>
+// CHECK-NEXT: <key>line</key><integer>13</integer><br>
// CHECK-NEXT: <key>col</key><integer>8</integer><br>
// CHECK-NEXT: <key>file</key><integer>0</integer><br>
// CHECK-NEXT: </dict><br>
@@ -117,7 +260,7 @@ void test() {<br>
// CHECK-NEXT: <key>kind</key><string>event</string><br>
// CHECK-NEXT: <key>location</key><br>
// CHECK-NEXT: <dict><br>
-// CHECK-NEXT: <key>line</key><integer>10</integer><br>
+// CHECK-NEXT: <key>line</key><integer>13</integer><br>
// CHECK-NEXT: <key>col</key><integer>3</integer><br>
// CHECK-NEXT: <key>file</key><integer>0</integer><br>
// CHECK-NEXT: </dict><br>
@@ -125,12 +268,12 @@ void test() {<br>
// CHECK-NEXT: <array><br>
// CHECK-NEXT: <array><br>
// CHECK-NEXT: <dict><br>
-// CHECK-NEXT: <key>line</key><integer>10</integer><br>
+// CHECK-NEXT: <key>line</key><integer>13</integer><br>
// CHECK-NEXT: <key>col</key><integer>10</integer><br>
// CHECK-NEXT: <key>file</key><integer>0</integer><br>
// CHECK-NEXT: </dict><br>
// CHECK-NEXT: <dict><br>
-// CHECK-NEXT: <key>line</key><integer>10</integer><br>
+// CHECK-NEXT: <key>line</key><integer>13</integer><br>
// CHECK-NEXT: <key>col</key><integer>10</integer><br>
// CHECK-NEXT: <key>file</key><integer>0</integer><br>
// CHECK-NEXT: </dict><br>
@@ -152,7 +295,7 @@ void test() {<br>
// CHECK-NEXT: <key>issue_hash</key><string>4</string><br>
// CHECK-NEXT: <key>location</key><br>
// CHECK-NEXT: <dict><br>
-// CHECK-NEXT: <key>line</key><integer>10</integer><br>
+// CHECK-NEXT: <key>line</key><integer>13</integer><br>
// CHECK-NEXT: <key>col</key><integer>3</integer><br>
// CHECK-NEXT: <key>file</key><integer>0</integer><br>
// CHECK-NEXT: </dict><br>
<br>
Modified: cfe/trunk/test/CodeGenCXX/new.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/new.cpp?rev=237608&r1=237607&r2=237608&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/new.cpp?rev=237608&r1=237607&r2=237608&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/CodeGenCXX/new.cpp (original)<br>
+++ cfe/trunk/test/CodeGenCXX/new.cpp Mon May 18 14:59:11 2015<br>
@@ -321,14 +321,14 @@ namespace N3664 {<br>
// CHECK-LABEL: define void @_ZN5N36641fEv<br>
void f() {<br>
// CHECK: call noalias i8* @_Znwm(i64 4) [[ATTR_BUILTIN_NEW:#[^ ]*]]<br>
- int *p = new int;<br>
+ int *p = new int; // expected-note {{allocated with 'new' here}}<br>
// CHECK: call void @_ZdlPv({{.*}}) [[ATTR_BUILTIN_DELETE:#[^ ]*]]<br>
delete p;<br>
<br>
// CHECK: call noalias i8* @_Znam(i64 12) [[ATTR_BUILTIN_NEW]]<br>
int *q = new int[3];<br>
// CHECK: call void @_ZdaPv({{.*}}) [[ATTR_BUILTIN_DELETE]]<br>
- delete [] p;<br>
+ delete[] p; // expected-warning {{'delete[]' applied to a pointer that was allocated with 'new'; did you mean 'delete'?}}<br>
<br>
// CHECK: call i8* @_ZnamRKSt9nothrow_t(i64 3, {{.*}}) [[ATTR_BUILTIN_NOTHROW_NEW:#[^ ]*]]<br>
(void) new (nothrow) S[3];<br>
<br>
Added: cfe/trunk/test/SemaCXX/delete-mismatch.h<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/delete-mismatch.h?rev=237608&view=auto" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/delete-mismatch.h?rev=237608&view=auto</a><br>
==============================================================================<br>
--- cfe/trunk/test/SemaCXX/delete-mismatch.h (added)<br>
+++ cfe/trunk/test/SemaCXX/delete-mismatch.h Mon May 18 14:59:11 2015<br>
@@ -0,0 +1,15 @@<br>
+// Header for PCH test delete.cpp<br>
+namespace pch_test {<br>
+struct X {<br>
+ int *a;<br>
+ X();<br>
+ X(int);<br>
+ X(bool)<br>
+ : a(new int[1]) { } // expected-note{{allocated with 'new[]' here}}<br>
+ ~X()<br>
+ {<br>
+ delete a; // expected-warning{{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}<br>
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:9-[[@LINE-1]]:9}:"[]"<br>
+ }<br>
+};<br>
+}<br>
<br>
Modified: cfe/trunk/test/SemaCXX/delete.cpp<br>
URL: <a href="http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/delete.cpp?rev=237608&r1=237607&r2=237608&view=diff" target="_blank">http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/delete.cpp?rev=237608&r1=237607&r2=237608&view=diff</a><br>
==============================================================================<br>
--- cfe/trunk/test/SemaCXX/delete.cpp (original)<br>
+++ cfe/trunk/test/SemaCXX/delete.cpp Mon May 18 14:59:11 2015<br>
@@ -1,9 +1,130 @@<br>
-// RUN: %clang_cc1 -fsyntax-only -verify %s<br>
-// RUN: cp %s %t<br>
-// RUN: %clang_cc1 -fixit -x c++ %t<br>
-// RUN: %clang_cc1 -E -o - %t | FileCheck %s<br>
+// Test without PCH<br>
+// RUN: %clang_cc1 -fsyntax-only -include %S/delete-mismatch.h -fdiagnostics-parseable-fixits -std=c++11 %s 2>&1 | FileCheck %s<br>
+<br>
+// Test with PCH<br>
+// RUN: %clang_cc1 -x c++-header -std=c++11 -emit-pch -o %t %S/delete-mismatch.h<br>
+// RUN: %clang_cc1 -std=c++11 -include-pch %t -DWITH_PCH -fsyntax-only -verify %s -ast-dump<br>
<br>
void f(int a[10][20]) {<br>
- // CHECK: delete[] a;<br>
delete a; // expected-warning {{'delete' applied to a pointer-to-array type}}<br>
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:9-[[@LINE-1]]:9}:"[]"<br>
+}<br>
+namespace MemberCheck {<br>
+struct S {<br>
+ int *a = new int[5]; // expected-note4 {{allocated with 'new[]' here}}<br>
+ int *b;<br>
+ int *c;<br>
+ static int *d;<br>
+ S();<br>
+ S(int);<br>
+ ~S() {<br>
+ delete a; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}<br>
+ delete b; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}<br>
+ delete[] c; // expected-warning {{'delete[]' applied to a pointer that was allocated with 'new'; did you mean 'delete'?}}<br>
+ }<br>
+ void f();<br>
+};<br>
+<br>
+void S::f()<br>
+{<br>
+ delete a; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}<br>
+ delete b; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}<br>
+}<br>
+<br>
+S::S()<br>
+: b(new int[1]), c(new int) {} // expected-note3 {{allocated with 'new[]' here}}<br>
+// expected-note@-1 {{allocated with 'new' here}}<br>
+<br>
+S::S(int i)<br>
+: b(new int[i]), c(new int) {} // expected-note3 {{allocated with 'new[]' here}}<br>
+// expected-note@-1 {{allocated with 'new' here}}<br>
+<br>
+struct S2 : S {<br>
+ ~S2() {<br>
+ delete a; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}<br>
+ }<br>
+};<br>
+int *S::d = new int[42]; // expected-note {{allocated with 'new[]' here}}<br>
+void f(S *s) {<br>
+ int *a = new int[1]; // expected-note {{allocated with 'new[]' here}}<br>
+ delete a; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}<br>
+ delete s->a; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}<br>
+ delete s->b; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}<br>
+ delete s->c;<br>
+ delete s->d;<br>
+ delete S::d; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}<br>
+}<br>
+<br>
+// At least one constructor initializes field with matching form of 'new'.<br>
+struct MatchingNewIsOK {<br>
+ int *p;<br>
+ bool is_array_;<br>
+ MatchingNewIsOK() : p{new int}, is_array_(false) {}<br>
+ explicit MatchingNewIsOK(unsigned c) : p{new int[c]}, is_array_(true) {}<br>
+ ~MatchingNewIsOK() {<br>
+ if (is_array_)<br>
+ delete[] p;<br>
+ else<br>
+ delete p;<br>
+ }<br>
+};<br>
+<br>
+// At least one constructor's body is missing; no proof of mismatch.<br>
+struct CantProve_MissingCtorDefinition {<br>
+ int *p;<br>
+ CantProve_MissingCtorDefinition();<br>
+ CantProve_MissingCtorDefinition(int);<br>
+ ~CantProve_MissingCtorDefinition();<br>
+};<br>
+<br>
+CantProve_MissingCtorDefinition::CantProve_MissingCtorDefinition()<br>
+ : p(new int)<br>
+{ }<br>
+<br>
+CantProve_MissingCtorDefinition::~CantProve_MissingCtorDefinition()<br>
+{<br>
+ delete[] p;<br>
+}<br>
+<br>
+struct base {};<br>
+struct derived : base {};<br>
+struct InitList {<br>
+ base *p, *p2 = nullptr, *p3{nullptr}, *p4;<br>
+ InitList(unsigned c) : p(new derived[c]), p4(nullptr) {} // expected-note {{allocated with 'new[]' here}}<br>
+ InitList(unsigned c, unsigned) : p{new derived[c]}, p4{nullptr} {} // expected-note {{allocated with 'new[]' here}}<br>
+ ~InitList() {<br>
+ delete p; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}<br>
+ delete [] p;<br>
+ delete p2;<br>
+ delete [] p3;<br>
+ delete p4;<br>
+ }<br>
+};<br>
+}<br>
+<br>
+namespace NonMemberCheck {<br>
+#define DELETE_ARRAY(x) delete[] (x)<br>
+#define DELETE(x) delete (x)<br>
+void f() {<br>
+ int *a = new int(5); // expected-note2 {{allocated with 'new' here}}<br>
+ delete[] a; // expected-warning {{'delete[]' applied to a pointer that was allocated with 'new'; did you mean 'delete'?}}<br>
+ int *b = new int;<br>
+ delete b;<br>
+ int *c{new int}; // expected-note {{allocated with 'new' here}}<br>
+ int *d{new int[1]}; // expected-note2 {{allocated with 'new[]' here}}<br>
+ delete [ ] c; // expected-warning {{'delete[]' applied to a pointer that was allocated with 'new'; did you mean 'delete'?}}<br>
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:9-[[@LINE-1]]:17}:""<br>
+ delete d; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}<br>
+ // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:9-[[@LINE-1]]:9}:"[]"<br>
+ DELETE_ARRAY(a); // expected-warning {{'delete[]' applied to a pointer that was allocated with 'new'; did you mean 'delete'?}}<br>
+ DELETE(d); // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}<br>
+}<br>
}<br>
+#ifndef WITH_PCH<br>
+pch_test::X::X()<br>
+ : a(new int[1]) // expected-note{{allocated with 'new[]' here}}<br>
+{ }<br>
+pch_test::X::X(int i)<br>
+ : a(new int[i]) // expected-note{{allocated with 'new[]' here}}<br>
+{ }<br>
+#endif<br>
<br>
<br>
_______________________________________________<br>
cfe-commits mailing list<br>
<a href="mailto:cfe-commits@cs.uiuc.edu">cfe-commits@cs.uiuc.edu</a><br>
<a href="http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits" target="_blank">http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits</a><br>
</blockquote></div><br></div></div></div>