Change how we handle diagnose_if attributes.

This patch changes how we handle argument-dependent `diagnose_if`
attributes. In particular, we now check them in the same place that we
check for things like passing NULL to Nonnull args, etc. This is
basically better in every way than how we were handling them before. :)

This fixes PR31638, PR31639, and PR31640.

Differential Revision: https://reviews.llvm.org/D28889

llvm-svn: 293360
diff --git a/clang/test/SemaCXX/diagnose_if.cpp b/clang/test/SemaCXX/diagnose_if.cpp
index f97b79d..5a9567f 100644
--- a/clang/test/SemaCXX/diagnose_if.cpp
+++ b/clang/test/SemaCXX/diagnose_if.cpp
@@ -2,6 +2,8 @@
 
 #define _diagnose_if(...) __attribute__((diagnose_if(__VA_ARGS__)))
 
+using size_t = unsigned long;
+
 namespace type_dependent {
 template <typename T>
 void neverok() _diagnose_if(!T(), "oh no", "error") {} // expected-note 4{{from 'diagnose_if'}}
@@ -51,14 +53,14 @@
 }
 
 template <typename T>
-void errorIf(T a) _diagnose_if(T() != a, "oh no", "error") {} // expected-note {{candidate disabled: oh no}}
+void errorIf(T a) _diagnose_if(T() != a, "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
 
 template <typename T>
-void warnIf(T a) _diagnose_if(T() != a, "oh no", "warning") {} // expected-note {{from 'diagnose_if'}}
+void warnIf(T a) _diagnose_if(T() != a, "oh no", "warning") {} // expected-note{{from 'diagnose_if'}}
 
 void runIf() {
   errorIf(0);
-  errorIf(1); // expected-error{{call to unavailable function}}
+  errorIf(1); // expected-error{{oh no}}
 
   warnIf(0);
   warnIf(1); // expected-warning{{oh no}}
@@ -114,14 +116,14 @@
 }
 
 template <int N>
-void errorIf(int a) _diagnose_if(N != a, "oh no", "error") {} // expected-note {{candidate disabled: oh no}}
+void errorIf(int a) _diagnose_if(N != a, "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
 
 template <int N>
-void warnIf(int a) _diagnose_if(N != a, "oh no", "warning") {} // expected-note {{from 'diagnose_if'}}
+void warnIf(int a) _diagnose_if(N != a, "oh no", "warning") {} // expected-note{{from 'diagnose_if'}}
 
 void runIf() {
   errorIf<0>(0);
-  errorIf<0>(1); // expected-error{{call to unavailable function}}
+  errorIf<0>(1); // expected-error{{oh no}}
 
   warnIf<0>(0);
   warnIf<0>(1); // expected-warning{{oh no}}
@@ -135,8 +137,8 @@
 void bar(int);
 void bar(short) _diagnose_if(1, "oh no", "error");
 
-void fooArg(int a) _diagnose_if(a, "oh no", "error"); // expected-note{{candidate disabled: oh no}}
-void fooArg(short); // expected-note{{candidate function}}
+void fooArg(int a) _diagnose_if(a, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+void fooArg(short);
 
 void barArg(int);
 void barArg(short a) _diagnose_if(a, "oh no", "error");
@@ -145,7 +147,7 @@
   foo(1); // expected-error{{oh no}}
   bar(1);
 
-  fooArg(1); // expected-error{{call to unavailable function}}
+  fooArg(1); // expected-error{{oh no}}
   barArg(1);
 
   auto p = foo; // expected-error{{incompatible initializer of type '<overloaded function type>'}}
@@ -188,11 +190,11 @@
   void foo(int i) _diagnose_if(i, "bad i", "error"); // expected-note{{from 'diagnose_if'}}
   void bar(int i) _diagnose_if(i != T(), "bad i", "error"); // expected-note{{from 'diagnose_if'}}
 
-  void fooOvl(int i) _diagnose_if(i, "int bad i", "error"); // expected-note 2{{int bad i}}
-  void fooOvl(short i) _diagnose_if(i, "short bad i", "error"); // expected-note 2{{short bad i}}
+  void fooOvl(int i) _diagnose_if(i, "int bad i", "error"); // expected-note{{from 'diagnose_if'}}
+  void fooOvl(short i) _diagnose_if(i, "short bad i", "error"); // expected-note{{from 'diagnose_if'}}
 
-  void barOvl(int i) _diagnose_if(i != T(), "int bad i", "error"); // expected-note 2{{int bad i}}
-  void barOvl(short i) _diagnose_if(i != T(), "short bad i", "error"); // expected-note 2{{short bad i}}
+  void barOvl(int i) _diagnose_if(i != T(), "int bad i", "error"); // expected-note{{from 'diagnose_if'}}
+  void barOvl(short i) _diagnose_if(i != T(), "short bad i", "error"); // expected-note{{from 'diagnose_if'}}
 };
 
 void runErrors() {
@@ -203,14 +205,14 @@
   Errors<int>().bar(1); // expected-error{{bad i}}
 
   Errors<int>().fooOvl(0);
-  Errors<int>().fooOvl(1); // expected-error{{call to unavailable}}
+  Errors<int>().fooOvl(1); // expected-error{{int bad i}}
   Errors<int>().fooOvl(short(0));
-  Errors<int>().fooOvl(short(1)); // expected-error{{call to unavailable}}
+  Errors<int>().fooOvl(short(1)); // expected-error{{short bad i}}
 
   Errors<int>().barOvl(0);
-  Errors<int>().barOvl(1); // expected-error{{call to unavailable}}
+  Errors<int>().barOvl(1); // expected-error{{int bad i}}
   Errors<int>().barOvl(short(0));
-  Errors<int>().barOvl(short(1)); // expected-error{{call to unavailable}}
+  Errors<int>().barOvl(short(1)); // expected-error{{short bad i}}
 }
 
 template <typename T>
@@ -275,8 +277,8 @@
 constexpr int foo();
 constexpr int foo(int a);
 
-void bar() _diagnose_if(foo(), "bad foo", "error"); // expected-note{{from 'diagnose_if'}} expected-note{{not viable: requires 0 arguments}}
-void bar(int a) _diagnose_if(foo(a), "bad foo", "error"); // expected-note{{bad foo}}
+void bar() _diagnose_if(foo(), "bad foo", "error"); // expected-note{{from 'diagnose_if'}}
+void bar(int a) _diagnose_if(foo(a), "bad foo", "error"); // expected-note{{from 'diagnose_if'}}
 
 void early() {
   bar();
@@ -290,7 +292,7 @@
 void late() {
   bar(); // expected-error{{bad foo}}
   bar(0);
-  bar(1); // expected-error{{call to unavailable function}}
+  bar(1); // expected-error{{bad foo}}
 }
 }
 
@@ -301,11 +303,11 @@
   constexpr bool isFooable() const { return i; }
 
   void go() const _diagnose_if(isFooable(), "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
-  operator int() const _diagnose_if(isFooable(), "oh no", "error") { return 1; } // expected-note{{oh no}}
+  operator int() const _diagnose_if(isFooable(), "oh no", "error") { return 1; } // expected-note{{from 'diagnose_if'}}
 
-  void go2() const _diagnose_if(isFooable(), "oh no", "error") // expected-note{{oh no}}
+  void go2() const _diagnose_if(isFooable(), "oh no", "error") // expected-note{{from 'diagnose_if'}}
       __attribute__((enable_if(true, ""))) {}
-  void go2() const _diagnose_if(isFooable(), "oh no", "error") {} // expected-note{{oh no}}
+  void go2() const _diagnose_if(isFooable(), "oh no", "error") {}
 
   constexpr int go3() const _diagnose_if(isFooable(), "oh no", "error")
       __attribute__((enable_if(true, ""))) {
@@ -326,20 +328,20 @@
   }
 };
 
-void go(const Foo &f) _diagnose_if(f.isFooable(), "oh no", "error") {} // expected-note{{oh no}}
+void go(const Foo &f) _diagnose_if(f.isFooable(), "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
 
 void run() {
   Foo(0).go();
   Foo(1).go(); // expected-error{{oh no}}
 
   (void)int(Foo(0));
-  (void)int(Foo(1)); // expected-error{{uses deleted function}}
+  (void)int(Foo(1)); // expected-error{{oh no}}
 
   Foo(0).go2();
-  Foo(1).go2(); // expected-error{{call to unavailable member function}}
+  Foo(1).go2(); // expected-error{{oh no}}
 
   go(Foo(0));
-  go(Foo(1)); // expected-error{{call to unavailable function}}
+  go(Foo(1)); // expected-error{{oh no}}
 }
 }
 
@@ -349,17 +351,17 @@
   constexpr Foo(int i): i(i) {}
   constexpr bool bad() const { return i; }
 
-  template <typename T> T getVal() _diagnose_if(bad(), "oh no", "error") { // expected-note{{oh no}}
+  template <typename T> T getVal() _diagnose_if(bad(), "oh no", "error") { // expected-note{{from 'diagnose_if'}}
     return T();
   }
 
   template <typename T>
-  constexpr T getVal2() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{oh no}}
+  constexpr T getVal2() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{from 'diagnose_if'}}
     return T();
   }
 
   template <typename T>
-  constexpr operator T() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{oh no}}
+  constexpr operator T() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{from 'diagnose_if'}}
     return T();
   }
 
@@ -369,13 +371,13 @@
 
 void run() {
   Foo(0).getVal<int>();
-  Foo(1).getVal<int>(); // expected-error{{call to unavailable member function}}
+  Foo(1).getVal<int>(); // expected-error{{oh no}}
 
   Foo(0).getVal2<int>();
-  Foo(1).getVal2<int>(); // expected-error{{call to unavailable member function}}
+  Foo(1).getVal2<int>(); // expected-error{{oh no}}
 
   (void)int(Foo(0));
-  (void)int(Foo(1)); // expected-error{{uses deleted function}}
+  (void)int(Foo(1)); // expected-error{{oh no}}
 }
 }
 
@@ -385,18 +387,18 @@
   int i;
   constexpr Foo(int i): i(i) {}
   constexpr bool bad() const { return i; }
-  const Bar *operator->() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{oh no}}
+  const Bar *operator->() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{from 'diagnose_if'}}
     return nullptr;
   }
-  void operator()() const _diagnose_if(bad(), "oh no", "error") {} // expected-note{{oh no}}
+  void operator()() const _diagnose_if(bad(), "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
 };
 
 struct ParenOverload {
   int i;
   constexpr ParenOverload(int i): i(i) {}
   constexpr bool bad() const { return i; }
-  void operator()(double) const _diagnose_if(bad(), "oh no", "error") {} // expected-note 2{{oh no}}
-  void operator()(int) const _diagnose_if(bad(), "oh no", "error") {} // expected-note 2{{oh no}}
+  void operator()(double) const _diagnose_if(bad(), "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
+  void operator()(int) const _diagnose_if(bad(), "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
 };
 
 struct ParenTemplate {
@@ -404,33 +406,70 @@
   constexpr ParenTemplate(int i): i(i) {}
   constexpr bool bad() const { return i; }
   template <typename T>
-  void operator()(T) const _diagnose_if(bad(), "oh no", "error") {} // expected-note 2{{oh no}}
+  void operator()(T) const _diagnose_if(bad(), "oh no", "error") {} // expected-note 2{{from 'diagnose_if'}}
 };
 
 void run() {
   (void)Foo(0)->j;
-  (void)Foo(1)->j; // expected-error{{selected unavailable operator '->'}}
+  (void)Foo(1)->j; // expected-error{{oh no}}
 
   Foo(0)();
-  Foo(1)(); // expected-error{{unavailable function call operator}}
+  Foo(1)(); // expected-error{{oh no}}
 
   ParenOverload(0)(1);
   ParenOverload(0)(1.);
 
-  ParenOverload(1)(1); // expected-error{{unavailable function call operator}}
-  ParenOverload(1)(1.); // expected-error{{unavailable function call operator}}
+  ParenOverload(1)(1); // expected-error{{oh no}}
+  ParenOverload(1)(1.); // expected-error{{oh no}}
 
   ParenTemplate(0)(1);
   ParenTemplate(0)(1.);
 
-  ParenTemplate(1)(1); // expected-error{{unavailable function call operator}}
-  ParenTemplate(1)(1.); // expected-error{{unavailable function call operator}}
+  ParenTemplate(1)(1); // expected-error{{oh no}}
+  ParenTemplate(1)(1.); // expected-error{{oh no}}
 }
 
 void runLambda() {
-  auto L1 = [](int i) _diagnose_if(i, "oh no", "error") {}; // expected-note{{oh no}} expected-note{{conversion candidate}}
+  auto L1 = [](int i) _diagnose_if(i, "oh no", "error") {}; // expected-note{{from 'diagnose_if'}}
   L1(0);
-  L1(1); // expected-error{{call to unavailable function call}}
+  L1(1); // expected-error{{oh no}}
+}
+
+struct Brackets {
+  int i;
+  constexpr Brackets(int i): i(i) {}
+  void operator[](int) _diagnose_if(i == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+    _diagnose_if(i == 2, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+};
+
+void runBrackets(int i) {
+  Brackets{0}[i];
+  Brackets{1}[i]; // expected-warning{{oh no}}
+  Brackets{2}[i]; // expected-error{{oh no}}
+}
+
+struct Unary {
+  int i;
+  constexpr Unary(int i): i(i) {}
+  void operator+() _diagnose_if(i == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+    _diagnose_if(i == 2, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+};
+
+void runUnary() {
+  +Unary{0};
+  +Unary{1}; // expected-warning{{oh no}}
+  +Unary{2}; // expected-error{{oh no}}
+}
+
+struct PostInc {
+  void operator++(int i) _diagnose_if(i == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+    _diagnose_if(i == 2, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+};
+
+void runPostInc() {
+  PostInc{}++;
+  PostInc{}.operator++(1); // expected-warning{{oh no}}
+  PostInc{}.operator++(2); // expected-error{{oh no}}
 }
 }
 
@@ -439,22 +478,192 @@
   int I;
   constexpr Foo(int I): I(I) {}
 
-  constexpr const Foo &operator=(const Foo &) const // expected-note 2{{disabled: oh no}}
-      _diagnose_if(I, "oh no", "error") {
+  constexpr const Foo &operator=(const Foo &) const
+      _diagnose_if(I, "oh no", "error") {  // expected-note{{from 'diagnose_if'}}
     return *this;
   }
 
-  constexpr const Foo &operator=(const Foo &&) const // expected-note{{disabled: oh no}} expected-note{{no known conversion}}
-      _diagnose_if(I, "oh no", "error") {
+  constexpr const Foo &operator=(const Foo &&) const
+      _diagnose_if(I, "oh no", "error") { // expected-note{{from 'diagnose_if'}}
     return *this;
   }
 };
 
+struct Bar {
+  int I;
+  constexpr Bar(int I) _diagnose_if(I == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+    _diagnose_if(I == 2, "oh no", "error"): I(I) {} // expected-note{{from 'diagnose_if'}}
+};
+
 void run() {
   constexpr Foo F{0};
   constexpr Foo F2{1};
 
-  F2 = F; // expected-error{{selected unavailable operator}}
-  F2 = Foo{2}; // expected-error{{selected unavailable operator}}
+  F2 = F; // expected-error{{oh no}}
+  F2 = Foo{2}; // expected-error{{oh no}}
+
+  Bar{0};
+  Bar{1}; // expected-warning{{oh no}}
+  Bar{2}; // expected-error{{oh no}}
+}
+}
+
+namespace ref_init {
+struct Bar {};
+struct Baz {};
+struct Foo {
+  int i;
+  constexpr Foo(int i): i(i) {}
+  operator const Bar &() const _diagnose_if(i, "oh no", "warning"); // expected-note{{from 'diagnose_if'}}
+  operator const Baz &() const _diagnose_if(i, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+};
+void fooBar(const Bar &b);
+void fooBaz(const Baz &b);
+
+void run() {
+  fooBar(Foo{0});
+  fooBar(Foo{1}); // expected-warning{{oh no}}
+  fooBaz(Foo{0});
+  fooBaz(Foo{1}); // expected-error{{oh no}}
+}
+}
+
+namespace udl {
+void operator""_fn(char c)_diagnose_if(c == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+    _diagnose_if(c == 2, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+
+void run() {
+  '\0'_fn;
+  '\1'_fn; // expected-warning{{oh no}}
+  '\2'_fn; // expected-error{{oh no}}
+}
+}
+
+namespace PR31638 {
+struct String {
+  String(char const* __s) _diagnose_if(__s == nullptr, "oh no ptr", "warning"); // expected-note{{from 'diagnose_if'}}
+  String(int __s) _diagnose_if(__s != 0, "oh no int", "warning"); // expected-note{{from 'diagnose_if'}}
+};
+
+void run() {
+  String s(nullptr); // expected-warning{{oh no ptr}}
+  String ss(42); // expected-warning{{oh no int}}
+}
+}
+
+namespace PR31639 {
+struct Foo {
+  Foo(int I) __attribute__((diagnose_if(I, "oh no", "error"))); // expected-note{{from 'diagnose_if'}}
+};
+
+void bar() { Foo f(1); } // expected-error{{oh no}}
+}
+
+namespace user_defined_conversion {
+struct Foo {
+  int i;
+  constexpr Foo(int i): i(i) {}
+  operator size_t() const _diagnose_if(i == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+      _diagnose_if(i == 2, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+};
+
+void run() {
+  // `new T[N]`, where N is implicitly convertible to size_t, calls
+  // PerformImplicitConversion directly. This lets us test the diagnostic logic
+  // in PerformImplicitConversion.
+  new int[Foo{0}];
+  new int[Foo{1}]; // expected-warning{{oh no}}
+  new int[Foo{2}]; // expected-error{{oh no}}
+}
+}
+
+namespace std {
+  template <typename T>
+  struct initializer_list {
+    const T *ptr;
+    size_t elems;
+
+    constexpr size_t size() const { return elems; }
+  };
+}
+
+namespace initializer_lists {
+struct Foo {
+  Foo(std::initializer_list<int> l)
+    _diagnose_if(l.size() == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+    _diagnose_if(l.size() == 2, "oh no", "error") {} // expected-note{{from 'diagnose_if'}}
+};
+
+void run() {
+  Foo{std::initializer_list<int>{}};
+  Foo{std::initializer_list<int>{1}}; // expected-warning{{oh no}}
+  Foo{std::initializer_list<int>{1, 2}}; // expected-error{{oh no}}
+  Foo{std::initializer_list<int>{1, 2, 3}};
+}
+}
+
+namespace range_for_loop {
+  namespace adl {
+    struct Foo {
+      int i;
+      constexpr Foo(int i): i(i) {}
+    };
+    void **begin(const Foo &f) _diagnose_if(f.i, "oh no", "warning");
+    void **end(const Foo &f) _diagnose_if(f.i, "oh no", "warning");
+
+    struct Bar {
+      int i;
+      constexpr Bar(int i): i(i) {}
+    };
+    void **begin(const Bar &b) _diagnose_if(b.i, "oh no", "error");
+    void **end(const Bar &b) _diagnose_if(b.i, "oh no", "error");
+  }
+
+  void run() {
+    for (void *p : adl::Foo(0)) {}
+    // FIXME: This should emit diagnostics. It seems that our constexpr
+    // evaluator isn't able to evaluate `adl::Foo(1)` as a constant, though.
+    for (void *p : adl::Foo(1)) {}
+
+    for (void *p : adl::Bar(0)) {}
+    // FIXME: Same thing.
+    for (void *p : adl::Bar(1)) {}
+  }
+}
+
+namespace operator_new {
+struct Foo {
+  int j;
+  static void *operator new(size_t i) _diagnose_if(i, "oh no", "warning");
+};
+
+struct Bar {
+  int j;
+  static void *operator new(size_t i) _diagnose_if(!i, "oh no", "warning");
+};
+
+void run() {
+  // FIXME: This should emit a diagnostic.
+  new Foo();
+  // This is here because we sometimes pass a dummy argument `operator new`. We
+  // should ignore this, rather than complaining about it.
+  new Bar();
+}
+}
+
+namespace contextual_implicit_conv {
+struct Foo {
+  int i;
+  constexpr Foo(int i): i(i) {}
+  constexpr operator int() const _diagnose_if(i == 1, "oh no", "warning") // expected-note{{from 'diagnose_if'}}
+      _diagnose_if(i == 2, "oh no", "error") { // expected-note{{from 'diagnose_if'}}
+    return i;
+  }
+};
+
+void run() {
+  switch (constexpr Foo i = 0) { default: break; }
+  switch (constexpr Foo i = 1) { default: break; } // expected-warning{{oh no}}
+  switch (constexpr Foo i = 2) { default: break; } // expected-error{{oh no}}
 }
 }