Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -5224,7 +5224,12 @@ "conversion function">; def note_delete_conversion : Note<"conversion to pointer type %0">; def warn_delete_array_type : Warning< - "'delete' applied to a pointer-to-array type %0 treated as delete[]">; + "'delete' applied to a pointer-to-array type %0 treated as 'delete[]'">; +def warn_mismatched_delete_new : Warning< + "'delete%select{|[]}0' applied to a pointer that was allocated with " + "'new%select{[]|}0' treated as 'delete%select{[]|}0'">, + InGroup>; +def note_allocated_here : Note<"allocated with 'new%select{[]|}0' here">; def err_no_suitable_delete_member_function_found : Error< "no suitable member %0 in %1">; def err_ambiguous_suitable_delete_member_function_found : Error< Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -8397,6 +8397,8 @@ /// \brief Check if the given expression contains 'break' or 'continue' /// statement that produces control flow different from GCC. void CheckBreakContinueBinding(Expr *E); + bool DiagnoseMismatchingDeleteArrayForm(QualType Pointee, bool ArrayForm, + Expr *E, SourceLocation &NewLoc); public: /// \brief Register a magic integral constant to be used as a type tag. Index: lib/Sema/SemaExprCXX.cpp =================================================================== --- lib/Sema/SemaExprCXX.cpp +++ lib/Sema/SemaExprCXX.cpp @@ -2236,6 +2236,39 @@ return false; } +bool Sema::DiagnoseMismatchingDeleteArrayForm(QualType Pointee, bool ArrayForm, + Expr *E, SourceLocation &NewLoc) { + if (Diags.isIgnored(diag::warn_mismatched_delete_new, SourceLocation())) + return false; + + const CXXNewExpr *NE = nullptr; + if (const MemberExpr *ME = dyn_cast(E)) { + if (const FieldDecl *FD = dyn_cast(ME->getMemberDecl())) { + const CXXRecordDecl *RD = cast(FD->getParent()); + for (const auto *CD : RD->ctors()) { + if (NE) + break; + for (const auto *CI : CD->inits()) { + if (FD == CI->getMember()) { + NE = dyn_cast(CI->getInit()); + break; + } + } + } + // No ctor-initializer initializes this field, check in-class initializer + if (!NE && FD->hasInClassInitializer()) + NE = dyn_cast(FD->getInClassInitializer()); + } + } else if (const DeclRefExpr *D = dyn_cast(E)) { + if (const VarDecl *VD = dyn_cast(D->getDecl())) + NE = dyn_cast_or_null(VD->getInit()); + } + if (NE && (NE->isArray() ^ ArrayForm)) { + NewLoc = NE->getLocStart(); + return true; + } + return false; +} /// ActOnCXXDelete - Parsed a C++ 'delete' expression (C++ 5.3.5), as in: /// @code ::delete ptr; @endcode /// or @@ -2351,18 +2384,26 @@ } } - // C++ [expr.delete]p2: - // [Note: a pointer to a const type can be the operand of a - // delete-expression; it is not necessary to cast away the constness - // (5.2.11) of the pointer expression before it is used as the operand - // of the delete-expression. ] - if (Pointee->isArrayType() && !ArrayForm) { Diag(StartLoc, diag::warn_delete_array_type) << Type << Ex.get()->getSourceRange() << FixItHint::CreateInsertion(PP.getLocForEndOfToken(StartLoc), "[]"); ArrayForm = true; } + SourceLocation NewLoc; + if (DiagnoseMismatchingDeleteArrayForm( + Pointee, ArrayForm, Ex.get()->IgnoreImpCasts(), NewLoc)) { + PartialDiagnostic PD = PDiag(diag::warn_mismatched_delete_new) + << ArrayForm << Ex.get()->getSourceRange(); + if (!ArrayForm) + PD << FixItHint::CreateInsertion(PP.getLocForEndOfToken(StartLoc), + "[]"); + // FIXME: Find a way to suggest fix-it for removal of '[]' without storing + // locations of [ and ]. + Diag(StartLoc, PD); + Diag(NewLoc, diag::note_allocated_here) << ArrayForm; + ArrayForm = !ArrayForm; + } DeclarationName DeleteName = Context.DeclarationNames.getCXXOperatorName( ArrayForm ? OO_Array_Delete : OO_Delete); Index: test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp =================================================================== --- test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp +++ test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp @@ -97,9 +97,11 @@ free(p); delete globalPtr; // expected-warning {{Attempt to free released memory}} } - +int *allocIntArray(unsigned c) { + return new int[c]; +} void testMismatchedChangePointeeThroughAssignment() { - int *arr = new int[4]; + int *arr = allocIntArray(4); globalPtr = arr; delete arr; // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'}} -} \ No newline at end of file +} Index: test/Analysis/MismatchedDeallocator-checker-test.mm =================================================================== --- test/Analysis/MismatchedDeallocator-checker-test.mm +++ test/Analysis/MismatchedDeallocator-checker-test.mm @@ -90,8 +90,11 @@ realloc(p, sizeof(long)); // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not realloc()}} } +int *allocInt() { + return new int; +} void testNew7() { - int *p = new int; + int *p = allocInt(); delete[] p; // expected-warning{{Memory allocated by 'new' should be deallocated by 'delete', not 'delete[]'}} } @@ -100,8 +103,12 @@ delete[] p; // expected-warning{{Memory allocated by operator new should be deallocated by 'delete', not 'delete[]'}} } +int *allocIntArray(unsigned c) { + return new int[c]; +} + void testNew9() { - int *p = new int[1]; + int *p = allocIntArray(1); delete p; // expected-warning{{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'}} } Index: test/Analysis/MismatchedDeallocator-path-notes.cpp =================================================================== --- test/Analysis/MismatchedDeallocator-path-notes.cpp +++ test/Analysis/MismatchedDeallocator-path-notes.cpp @@ -3,9 +3,12 @@ // RUN: FileCheck --input-file=%t.plist %s void changePointee(int *p); +int *allocIntArray(unsigned c) { + return new int[c]; // expected-note {{Memory is allocated}} +} void test() { - int *p = new int[1]; - // expected-note@-1 {{Memory is allocated}} + int *p = allocIntArray(1); // expected-note {{Calling 'allocIntArray'}} + // expected-note@-1 {{Returned allocated memory}} changePointee(p); delete p; // expected-warning {{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'}} // expected-note@-1 {{Memory allocated by 'new[]' should be deallocated by 'delete[]', not 'delete'}} @@ -24,12 +27,12 @@ // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line7 +// CHECK-NEXT: line10 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line7 +// CHECK-NEXT: line10 // CHECK-NEXT: col5 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -37,13 +40,124 @@ // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line7 +// CHECK-NEXT: line10 // CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: +// CHECK-NEXT: line10 +// CHECK-NEXT: col24 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line10 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line10 +// CHECK-NEXT: col12 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line10 +// CHECK-NEXT: col27 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth0 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Calling 'allocIntArray' +// CHECK-NEXT: message +// CHECK-NEXT: Calling 'allocIntArray' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line6 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Entered call from 'test' +// CHECK-NEXT: message +// CHECK-NEXT: Entered call from 'test' +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line6 +// CHECK-NEXT: col1 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line6 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line7 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: // CHECK-NEXT: line7 -// CHECK-NEXT: col14 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindcontrol +// CHECK-NEXT: edges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: start +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line7 +// CHECK-NEXT: col3 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line7 +// CHECK-NEXT: col8 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: end +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line7 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line7 +// CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: @@ -55,6 +169,35 @@ // CHECK-NEXT: location // CHECK-NEXT: // CHECK-NEXT: line7 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: ranges +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line7 +// CHECK-NEXT: col10 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: line7 +// CHECK-NEXT: col19 +// CHECK-NEXT: file0 +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: depth1 +// CHECK-NEXT: extended_message +// CHECK-NEXT: Memory is allocated +// CHECK-NEXT: message +// CHECK-NEXT: Memory is allocated +// CHECK-NEXT: +// CHECK-NEXT: +// CHECK-NEXT: kindevent +// CHECK-NEXT: location +// CHECK-NEXT: +// CHECK-NEXT: line10 // CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -62,22 +205,22 @@ // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line7 +// CHECK-NEXT: line10 // CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line7 -// CHECK-NEXT: col21 +// CHECK-NEXT: line10 +// CHECK-NEXT: col27 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: depth0 // CHECK-NEXT: extended_message -// CHECK-NEXT: Memory is allocated +// CHECK-NEXT: Returned allocated memory // CHECK-NEXT: message -// CHECK-NEXT: Memory is allocated +// CHECK-NEXT: Returned allocated memory // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: kindcontrol @@ -87,25 +230,25 @@ // CHECK-NEXT: start // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line7 +// CHECK-NEXT: line10 // CHECK-NEXT: col12 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line7 -// CHECK-NEXT: col14 +// CHECK-NEXT: line10 +// CHECK-NEXT: col24 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: end // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line10 +// CHECK-NEXT: line13 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line10 +// CHECK-NEXT: line13 // CHECK-NEXT: col8 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -117,7 +260,7 @@ // CHECK-NEXT: kindevent // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line10 +// CHECK-NEXT: line13 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -125,12 +268,12 @@ // CHECK-NEXT: // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line10 +// CHECK-NEXT: line13 // CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: // CHECK-NEXT: -// CHECK-NEXT: line10 +// CHECK-NEXT: line13 // CHECK-NEXT: col10 // CHECK-NEXT: file0 // CHECK-NEXT: @@ -151,7 +294,7 @@ // CHECK-NEXT: issue_hash4 // CHECK-NEXT: location // CHECK-NEXT: -// CHECK-NEXT: line10 +// CHECK-NEXT: line13 // CHECK-NEXT: col3 // CHECK-NEXT: file0 // CHECK-NEXT: Index: test/CodeGenCXX/new.cpp =================================================================== --- test/CodeGenCXX/new.cpp +++ test/CodeGenCXX/new.cpp @@ -290,14 +290,14 @@ // CHECK-LABEL: define void @_ZN5N36641fEv void f() { // CHECK: call noalias i8* @_Znwm(i64 4) [[ATTR_BUILTIN_NEW:#[^ ]*]] - int *p = new int; + int *p = new int; // expected-note {{allocated with 'new' here}} // CHECK: call void @_ZdlPv({{.*}}) [[ATTR_BUILTIN_DELETE:#[^ ]*]] delete p; // CHECK: call noalias i8* @_Znam(i64 12) [[ATTR_BUILTIN_NEW]] int *q = new int[3]; - // CHECK: call void @_ZdaPv({{.*}}) [[ATTR_BUILTIN_DELETE]] - delete [] p; + // CHECK: call void @_ZdlPv({{.*}}) [[ATTR_BUILTIN_DELETE]] + delete[] p; // expected-warning {{'delete[]' applied to a pointer that was allocated with 'new' treated as 'delete'}} // CHECK: call i8* @_ZnamRKSt9nothrow_t(i64 3, {{.*}}) [[ATTR_BUILTIN_NOTHROW_NEW:#[^ ]*]] (void) new (nothrow) S[3]; Index: test/SemaCXX/delete.cpp =================================================================== --- test/SemaCXX/delete.cpp +++ test/SemaCXX/delete.cpp @@ -1,9 +1,52 @@ -// RUN: %clang_cc1 -fsyntax-only -verify %s +// RUN: %clang_cc1 -fsyntax-only -std=c++11 -verify %s // RUN: cp %s %t -// RUN: %clang_cc1 -fixit -x c++ %t +// RUN: %clang_cc1 -fixit -x c++ -std=c++11 %t // RUN: %clang_cc1 -E -o - %t | FileCheck %s void f(int a[10][20]) { // CHECK: delete[] a; delete a; // expected-warning {{'delete' applied to a pointer-to-array type}} } +namespace MemberCheck { +struct S { + int *a = new int[5]; // expected-note {{allocated with 'new[]' here}} + // expected-note@-1 {{allocated with 'new[]' here}} + // expected-note@-2 {{allocated with 'new[]' here}} + int *b; + int *c; + static int *d; + S() : b(new int[5]), c(new int) {} // expected-note {{allocated with 'new[]' here}} + // expected-note@-1 {{allocated with 'new[]' here}} + // expected-note@-2 {{allocated with 'new' here}} + ~S() { + // CHECK: delete[] a; + delete a; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]' treated as 'delete[]'}} + // CHECK: delete[] b; + delete b; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]' treated as 'delete[]'}} + delete [] c; // expected-warning {{'delete[]' applied to a pointer that was allocated with 'new' treated as 'delete'}} + } +}; +struct S2 : S { + ~S2() { + delete a; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]' treated as 'delete[]'}} + } +}; +int *S::d = new int[42]; // expected-note {{allocated with 'new[]' here}} +void f(S *s) { + int *a = new int[1]; // expected-note {{allocated with 'new[]' here}} + //CHECK: delete[] a; + delete a; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]' treated as 'delete[]'}} + // CHECK: delete[] s->a; + delete s->a; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]' treated as 'delete[]'}} + delete s->b; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]' treated as 'delete[]'}} + delete s->c; + delete s->d; + delete S::d; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]' treated as 'delete[]'}} +} +void f2() { + int *a = new int(5); // expected-note {{allocated with 'new' here}} + delete[] a; // expected-warning {{'delete[]' applied to a pointer that was allocated with 'new' treated as 'delete'}} + int *b = new int; + delete b; +} +}