Detect uses of mismatching forms of 'new' and 'delete'

Emit warning when operand to `delete` is allocated with `new[]` or
operand to `delete[]` is allocated with `new`.

rev 2 update:
`getNewExprFromInitListOrExpr` should return `dyn_cast_or_null`
instead of `dyn_cast`, since `E` might be null.

Reviewers: rtrieu, jordan_rose, rsmith

Subscribers: majnemer, cfe-commits

Differential Revision: http://reviews.llvm.org/D4661

llvm-svn: 237608
diff --git a/clang/test/SemaCXX/delete.cpp b/clang/test/SemaCXX/delete.cpp
index 5824fac..f94a863 100644
--- a/clang/test/SemaCXX/delete.cpp
+++ b/clang/test/SemaCXX/delete.cpp
@@ -1,9 +1,130 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
-// RUN: cp %s %t
-// RUN: %clang_cc1 -fixit -x c++ %t
-// RUN: %clang_cc1 -E -o - %t | FileCheck %s
+// Test without PCH
+// RUN: %clang_cc1 -fsyntax-only -include %S/delete-mismatch.h -fdiagnostics-parseable-fixits -std=c++11 %s 2>&1 | FileCheck %s
+
+// Test with PCH
+// RUN: %clang_cc1 -x c++-header -std=c++11 -emit-pch -o %t %S/delete-mismatch.h
+// RUN: %clang_cc1 -std=c++11 -include-pch %t -DWITH_PCH -fsyntax-only -verify %s -ast-dump
 
 void f(int a[10][20]) {
-  // CHECK: delete[] a;
   delete a; // expected-warning {{'delete' applied to a pointer-to-array type}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:9-[[@LINE-1]]:9}:"[]"
 }
+namespace MemberCheck {
+struct S {
+  int *a = new int[5]; // expected-note4 {{allocated with 'new[]' here}}
+  int *b;
+  int *c;
+  static int *d;
+  S();
+  S(int);
+  ~S() {
+    delete a; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}
+    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'?}}
+  }
+  void f();
+};
+
+void S::f()
+{
+  delete a; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}
+  delete b; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}
+}
+
+S::S()
+: b(new int[1]), c(new int) {} // expected-note3 {{allocated with 'new[]' here}}
+// expected-note@-1 {{allocated with 'new' here}}
+
+S::S(int i)
+: b(new int[i]), c(new int) {} // expected-note3 {{allocated with 'new[]' here}}
+// expected-note@-1 {{allocated with 'new' here}}
+
+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}}
+  delete a; // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}
+  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[]'?}}
+}
+
+// At least one constructor initializes field with matching form of 'new'.
+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;
+  }
+};
+
+// At least one constructor's body is missing; no proof of mismatch.
+struct CantProve_MissingCtorDefinition {
+  int *p;
+  CantProve_MissingCtorDefinition();
+  CantProve_MissingCtorDefinition(int);
+  ~CantProve_MissingCtorDefinition();
+};
+
+CantProve_MissingCtorDefinition::CantProve_MissingCtorDefinition()
+  : p(new int)
+{ }
+
+CantProve_MissingCtorDefinition::~CantProve_MissingCtorDefinition()
+{
+  delete[] p;
+}
+
+struct base {};
+struct derived : base {};
+struct InitList {
+  base *p, *p2 = nullptr, *p3{nullptr}, *p4;
+  InitList(unsigned c) : p(new derived[c]), p4(nullptr) {}  // expected-note {{allocated with 'new[]' here}}
+  InitList(unsigned c, unsigned) : p{new derived[c]}, p4{nullptr} {} // 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;
+    delete p2;
+    delete [] p3;
+    delete p4;
+  }
+};
+}
+
+namespace NonMemberCheck {
+#define DELETE_ARRAY(x) delete[] (x)
+#define DELETE(x) delete (x)
+void f() {
+  int *a = new int(5); // expected-note2 {{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-note2 {{allocated with 'new[]' here}}
+  delete  [    ] c;   // expected-warning {{'delete[]' applied to a pointer that was allocated with 'new'; did you mean 'delete'?}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:9-[[@LINE-1]]:17}:""
+  delete d;           // expected-warning {{'delete' applied to a pointer that was allocated with 'new[]'; did you mean 'delete[]'?}}
+  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:9-[[@LINE-1]]:9}:"[]"
+  DELETE_ARRAY(a);    // 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[]'?}}
+}
+}
+#ifndef WITH_PCH
+pch_test::X::X()
+  : a(new int[1])  // expected-note{{allocated with 'new[]' here}}
+{ }
+pch_test::X::X(int i)
+  : a(new int[i])  // expected-note{{allocated with 'new[]' here}}
+{ }
+#endif