Updated patch to address following issues:
* Delay checking delete expressions until the end of translation unit
* Collect a list of new-expressions that mismatch with delete-expression
* If at least one new-expression matches with delete-expression, no warning
will be emitted
* Do not attempt to recover
* Collect ctor-initializers from //definitions// of constructors
* Added `InitListExprs` support
http://reviews.llvm.org/D4661
Files:
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/Sema.h
lib/Sema/Sema.cpp
lib/Sema/SemaExprCXX.cpp
test/Analysis/Malloc+MismatchedDeallocator+NewDelete.cpp
test/Analysis/MismatchedDeallocator-checker-test.mm
test/Analysis/MismatchedDeallocator-path-notes.cpp
test/CodeGenCXX/new.cpp
test/SemaCXX/delete.cpp
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -5241,7 +5241,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'; did you mean 'delete%select{[]|}0'?">,
+ InGroup<DiagGroup<"mismatched-new-delete">>;
+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
@@ -78,10 +78,12 @@
typedef SmallVector<CXXBaseSpecifier*, 4> CXXCastPath;
class CXXConstructorDecl;
class CXXConversionDecl;
+ class CXXDeleteExpr;
class CXXDestructorDecl;
class CXXFieldCollector;
class CXXMemberCallExpr;
class CXXMethodDecl;
+ class CXXNewExpr;
class CXXScopeSpec;
class CXXTemporary;
class CXXTryStmt;
@@ -390,6 +392,9 @@
/// \brief Set containing all declared private fields that are not used.
NamedDeclSetType UnusedPrivateFields;
+ /// \brief All delete-expressions within the translation unit.
+ llvm::SmallPtrSet<const CXXDeleteExpr *, 4> DeleteExprs;
+
typedef llvm::SmallPtrSet<const CXXRecordDecl*, 8> RecordDeclSetTy;
/// PureVirtualClassDiagSet - a set of class declarations which we have
@@ -8400,6 +8405,17 @@
/// \brief Check if the given expression contains 'break' or 'continue'
/// statement that produces control flow different from GCC.
void CheckBreakContinueBinding(Expr *E);
+ /// \brief Check whether given delete-expression mismatches with all new-
+ /// expressions used for initializing pointee.
+ ///
+ /// If pointee is initialized with array form of 'new', it should be deleted
+ /// with array form delete. If at least one \a matching new-expression is
+ /// found, the function will return false.
+ /// \param DE delete-expression to analyze
+ /// \param NewExprs new-expressions used for initialization of pointee
+ bool DiagnoseMismatchedNewDelete(
+ const CXXDeleteExpr *DE,
+ llvm::SmallVector<const CXXNewExpr *, 4> &NewExprs);
public:
/// \brief Register a magic integral constant to be used as a type tag.
Index: lib/Sema/Sema.cpp
===================================================================
--- lib/Sema/Sema.cpp
+++ lib/Sema/Sema.cpp
@@ -841,6 +841,24 @@
}
}
}
+ if (!Diags.isIgnored(diag::warn_mismatched_delete_new, SourceLocation())) {
+ for (const auto &DE : DeleteExprs) {
+ llvm::SmallVector<const CXXNewExpr *, 4> NewExprs;
+ if (!DiagnoseMismatchedNewDelete(DE, NewExprs))
+ continue;
+ PartialDiagnostic PD = PDiag(diag::warn_mismatched_delete_new)
+ << DE->isArrayForm()
+ << DE->getArgument()->getSourceRange();
+ if (!DE->isArrayForm())
+ PD << FixItHint::CreateInsertion(
+ PP.getLocForEndOfToken(DE->getLocStart()), "[]");
+ // FIXME: Find a way to suggest fix-it for removal of '[]'.
+ Diag(DE->getLocStart(), PD);
+ for (const auto *NE : NewExprs) {
+ Diag(NE->getExprLoc(), diag::note_allocated_here) << DE->isArrayForm();
+ }
+ }
+ }
// Check we've noticed that we're no longer parsing the initializer for every
// variable. If we miss cases, then at best we have a performance issue and
Index: lib/Sema/SemaExprCXX.cpp
===================================================================
--- lib/Sema/SemaExprCXX.cpp
+++ lib/Sema/SemaExprCXX.cpp
@@ -2226,6 +2226,79 @@
return false;
}
+// Returns new-expression or null for given initializer.
+static const CXXNewExpr *getNewExprInit(const Expr *E) {
+ assert(E != nullptr && "Expected a valid initializer expression");
+ E = E->IgnoreImpCasts();
+ const CXXNewExpr *NE = dyn_cast<const CXXNewExpr>(E);
+ if (NE)
+ return NE;
+ if (const InitListExpr *ILE = dyn_cast<const InitListExpr>(E))
+ if (ILE->getNumInits() == 1)
+ NE = dyn_cast<const CXXNewExpr>(ILE->getInit(0)->IgnoreParenImpCasts());
+ return NE;
+}
+
+// Returns list of new-expressions used for initializing given class member
+static bool mismatchedMemberInitsNewExprs(
+ const MemberExpr *ME, bool IsArrayForm,
+ llvm::SmallVector<const CXXNewExpr *, 4> &NewExprs) {
+ assert(ME != nullptr && "Expected a member expression");
+ const CXXNewExpr *NE = nullptr;
+ const FieldDecl *FD = dyn_cast<const FieldDecl>(ME->getMemberDecl());
+ if (!FD)
+ return false;
+ const CXXRecordDecl *RD = cast<const CXXRecordDecl>(FD->getParent());
+ for (const auto *CD : RD->ctors()) {
+ if (!CD->isThisDeclarationADefinition())
+ continue;
+ for (const auto *CI : CD->inits()) {
+ if (FD == CI->getMember() && (NE = getNewExprInit(CI->getInit()))) {
+ if (NE->isArray() == IsArrayForm)
+ return false;
+ NewExprs.push_back(NE);
+ }
+ }
+ }
+ // No ctor-initializer initializes this field, check in-class initializer
+ if (!NE && FD->hasInClassInitializer() &&
+ (NE = getNewExprInit(FD->getInClassInitializer()))) {
+ if (NE->isArray() == IsArrayForm)
+ return false;
+ NewExprs.push_back(NE);
+ }
+ return !NewExprs.empty();
+}
+
+// Returns list of new-expressions used for initializing given variable
+static bool
+mismatchedVarInitsNewExprs(const DeclRefExpr *D, bool IsArrayForm,
+ llvm::SmallVector<const CXXNewExpr *, 4> &NewExprs) {
+ const CXXNewExpr *NE = nullptr;
+ if (const VarDecl *VD = dyn_cast<const VarDecl>(D->getDecl()))
+ if (VD->hasInit() && (NE = getNewExprInit(VD->getInit()))) {
+ if (NE->isArray() == IsArrayForm)
+ return false;
+ NewExprs.push_back(NE);
+ }
+ return !NewExprs.empty();
+}
+
+bool Sema::DiagnoseMismatchedNewDelete(
+ const CXXDeleteExpr *DE,
+ llvm::SmallVector<const CXXNewExpr *, 4> &NewExprs) {
+ const Expr *E = DE->getArgument()->IgnoreCasts();
+ NewExprs.clear();
+
+ if (const MemberExpr *ME = dyn_cast<const MemberExpr>(E)) {
+ if (!mismatchedMemberInitsNewExprs(ME, DE->isArrayForm(), NewExprs))
+ return false;
+ } else if (const DeclRefExpr *D = dyn_cast<const DeclRefExpr>(E)) {
+ if (!mismatchedVarInitsNewExprs(D, DE->isArrayForm(), NewExprs))
+ return false;
+ }
+ return !NewExprs.empty();
+}
/// ActOnCXXDelete - Parsed a C++ 'delete' expression (C++ 5.3.5), as in:
/// @code ::delete ptr; @endcode
/// or
@@ -2341,12 +2414,6 @@
}
}
- // 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()
@@ -2431,9 +2498,11 @@
}
}
- return new (Context) CXXDeleteExpr(
+ CXXDeleteExpr *Result = new (Context) CXXDeleteExpr(
Context.VoidTy, UseGlobal, ArrayForm, ArrayFormAsWritten,
UsualArrayDeleteWantsSize, OperatorDelete, Ex.get(), StartLoc);
+ DeleteExprs.insert(Result);
+ return Result;
}
/// \brief Check the use of the given variable as a C++ condition in an if,
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,18 +90,25 @@
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[]'}}
}
void testNew8() {
int *p = (int *)operator new(0);
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,26 +27,137 @@
// CHECK-NEXT: <key>start</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>7</integer>
+// CHECK-NEXT: <key>line</key><integer>10</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>7</integer>
+// CHECK-NEXT: <key>line</key><integer>10</integer>
// CHECK-NEXT: <key>col</key><integer>5</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>7</integer>
+// CHECK-NEXT: <key>line</key><integer>10</integer>
// CHECK-NEXT: <key>col</key><integer>12</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>10</integer>
+// CHECK-NEXT: <key>col</key><integer>24</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: </array>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: </array>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>kind</key><string>event</string>
+// CHECK-NEXT: <key>location</key>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>10</integer>
+// CHECK-NEXT: <key>col</key><integer>12</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <key>ranges</key>
+// CHECK-NEXT: <array>
+// CHECK-NEXT: <array>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>10</integer>
+// CHECK-NEXT: <key>col</key><integer>12</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>10</integer>
+// CHECK-NEXT: <key>col</key><integer>27</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: </array>
+// CHECK-NEXT: </array>
+// CHECK-NEXT: <key>depth</key><integer>0</integer>
+// CHECK-NEXT: <key>extended_message</key>
+// CHECK-NEXT: <string>Calling 'allocIntArray'</string>
+// CHECK-NEXT: <key>message</key>
+// CHECK-NEXT: <string>Calling 'allocIntArray'</string>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>kind</key><string>event</string>
+// CHECK-NEXT: <key>location</key>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>6</integer>
+// CHECK-NEXT: <key>col</key><integer>1</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <key>depth</key><integer>1</integer>
+// CHECK-NEXT: <key>extended_message</key>
+// CHECK-NEXT: <string>Entered call from 'test'</string>
+// CHECK-NEXT: <key>message</key>
+// CHECK-NEXT: <string>Entered call from 'test'</string>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>kind</key><string>control</string>
+// CHECK-NEXT: <key>edges</key>
+// CHECK-NEXT: <array>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>start</key>
+// CHECK-NEXT: <array>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>6</integer>
+// CHECK-NEXT: <key>col</key><integer>1</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>6</integer>
+// CHECK-NEXT: <key>col</key><integer>3</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: </array>
+// CHECK-NEXT: <key>end</key>
+// CHECK-NEXT: <array>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>7</integer>
+// CHECK-NEXT: <key>col</key><integer>3</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>7</integer>
-// CHECK-NEXT: <key>col</key><integer>14</integer>
+// CHECK-NEXT: <key>col</key><integer>8</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: </array>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: </array>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>kind</key><string>control</string>
+// CHECK-NEXT: <key>edges</key>
+// CHECK-NEXT: <array>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>start</key>
+// CHECK-NEXT: <array>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>7</integer>
+// CHECK-NEXT: <key>col</key><integer>3</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>7</integer>
+// CHECK-NEXT: <key>col</key><integer>8</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: </array>
+// CHECK-NEXT: <key>end</key>
+// CHECK-NEXT: <array>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>7</integer>
+// CHECK-NEXT: <key>col</key><integer>10</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>7</integer>
+// CHECK-NEXT: <key>col</key><integer>12</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
@@ -55,29 +169,58 @@
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>line</key><integer>7</integer>
+// CHECK-NEXT: <key>col</key><integer>10</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <key>ranges</key>
+// CHECK-NEXT: <array>
+// CHECK-NEXT: <array>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>7</integer>
+// CHECK-NEXT: <key>col</key><integer>10</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>7</integer>
+// CHECK-NEXT: <key>col</key><integer>19</integer>
+// CHECK-NEXT: <key>file</key><integer>0</integer>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: </array>
+// CHECK-NEXT: </array>
+// CHECK-NEXT: <key>depth</key><integer>1</integer>
+// CHECK-NEXT: <key>extended_message</key>
+// CHECK-NEXT: <string>Memory is allocated</string>
+// CHECK-NEXT: <key>message</key>
+// CHECK-NEXT: <string>Memory is allocated</string>
+// CHECK-NEXT: </dict>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>kind</key><string>event</string>
+// CHECK-NEXT: <key>location</key>
+// CHECK-NEXT: <dict>
+// CHECK-NEXT: <key>line</key><integer>10</integer>
// CHECK-NEXT: <key>col</key><integer>12</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>ranges</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>7</integer>
+// CHECK-NEXT: <key>line</key><integer>10</integer>
// CHECK-NEXT: <key>col</key><integer>12</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>7</integer>
-// CHECK-NEXT: <key>col</key><integer>21</integer>
+// CHECK-NEXT: <key>line</key><integer>10</integer>
+// CHECK-NEXT: <key>col</key><integer>27</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>depth</key><integer>0</integer>
// CHECK-NEXT: <key>extended_message</key>
-// CHECK-NEXT: <string>Memory is allocated</string>
+// CHECK-NEXT: <string>Returned allocated memory</string>
// CHECK-NEXT: <key>message</key>
-// CHECK-NEXT: <string>Memory is allocated</string>
+// CHECK-NEXT: <string>Returned allocated memory</string>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
// CHECK-NEXT: <key>kind</key><string>control</string>
@@ -87,25 +230,25 @@
// CHECK-NEXT: <key>start</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>7</integer>
+// CHECK-NEXT: <key>line</key><integer>10</integer>
// CHECK-NEXT: <key>col</key><integer>12</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>7</integer>
-// CHECK-NEXT: <key>col</key><integer>14</integer>
+// CHECK-NEXT: <key>line</key><integer>10</integer>
+// CHECK-NEXT: <key>col</key><integer>24</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: </array>
// CHECK-NEXT: <key>end</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>10</integer>
+// CHECK-NEXT: <key>line</key><integer>13</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>10</integer>
+// CHECK-NEXT: <key>line</key><integer>13</integer>
// CHECK-NEXT: <key>col</key><integer>8</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
@@ -117,20 +260,20 @@
// CHECK-NEXT: <key>kind</key><string>event</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>10</integer>
+// CHECK-NEXT: <key>line</key><integer>13</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <key>ranges</key>
// CHECK-NEXT: <array>
// CHECK-NEXT: <array>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>10</integer>
+// CHECK-NEXT: <key>line</key><integer>13</integer>
// CHECK-NEXT: <key>col</key><integer>10</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>10</integer>
+// CHECK-NEXT: <key>line</key><integer>13</integer>
// CHECK-NEXT: <key>col</key><integer>10</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
@@ -151,7 +294,7 @@
// CHECK-NEXT: <key>issue_hash</key><string>4</string>
// CHECK-NEXT: <key>location</key>
// CHECK-NEXT: <dict>
-// CHECK-NEXT: <key>line</key><integer>10</integer>
+// CHECK-NEXT: <key>line</key><integer>13</integer>
// CHECK-NEXT: <key>col</key><integer>3</integer>
// CHECK-NEXT: <key>file</key><integer>0</integer>
// CHECK-NEXT: </dict>
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;
+ delete[] p; // expected-warning {{'delete[]' applied to a pointer that was allocated with 'new'; did you mean '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,86 @@
-// 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[1]), 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[]'; did you mean 'delete[]'?}}
+ // CHECK: delete[] b;
+ delete b; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}
+ delete[] c; // expected-warning {{'delete[]' applied to a pointer that was allocated with 'new'; did you mean 'delete'?}}
+ }
+};
+struct S2 : S {
+ ~S2() {
+ delete a; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean '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[]'; did you mean 'delete[]'?}}
+ // CHECK: delete[] s->a;
+ delete s->a; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}
+ delete s->b; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}
+ delete s->c;
+ delete s->d;
+ delete S::d; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}
+}
+
+struct MatchingNewIsOK {
+ int *p;
+ bool is_array_;
+ MatchingNewIsOK() : p{new int}, is_array_(false) {}
+ explicit MatchingNewIsOK(unsigned c) : p{new int[c]}, is_array_(true) {}
+ ~MatchingNewIsOK() {
+ if (is_array_)
+ delete[] p;
+ else
+ delete p;
+ }
+};
+
+struct base {};
+struct derived : base {};
+struct InitList {
+ base *p;
+ InitList() : p{new derived[1]} {} // expected-note {{allocated with 'new[]' here}}
+ explicit InitList(unsigned c) : p(new derived[c]) {} // expected-note {{allocated with 'new[]' here}}
+ InitList(unsigned c, unsigned) : p{new derived[c]} {} // expected-note {{allocated with 'new[]' here}}
+ InitList(const char *) : p{new derived[1]} {} // expected-note {{allocated with 'new[]' here}}
+ ~InitList() {
+ delete p; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}
+ delete[] p;
+ }
+};
+}
+
+namespace NonMemberCheck {
+void f() {
+ 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'; did you mean 'delete'?}}
+ int *b = new int;
+ delete b;
+ int *c{new int}; // expected-note {{allocated with 'new' here}}
+ int *d{new int[1]}; // expected-note {{allocated with 'new[]' here}}
+ delete[] c; // expected-warning {{'delete[]' applied to a pointer that was allocated with 'new'; did you mean 'delete'?}}
+ delete d; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}
+}
+}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits