Add the diagnose_if attribute to clang.

`diagnose_if` can be used to have clang emit either warnings or errors
for function calls that meet user-specified conditions. For example:

```
constexpr int foo(int a)
  __attribute__((diagnose_if(a > 10, "configurations with a > 10 are "
                                      "expensive.", "warning")));

int f1 = foo(9);
int f2 = foo(10); // warning: configuration with a > 10 are expensive.
int f3 = foo(f2);
```

It currently only emits diagnostics in cases where the condition is
guaranteed to always be true. So, the following code will emit no
warnings:

```
constexpr int bar(int a) {
  foo(a);
  return 0;
}

constexpr int i = bar(10);
```

We hope to support optionally emitting diagnostics for cases like that
(and emitting runtime checks) in the future.

Release notes will appear shortly. :)

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

llvm-svn: 291418
diff --git a/clang/test/SemaCXX/diagnose_if.cpp b/clang/test/SemaCXX/diagnose_if.cpp
new file mode 100644
index 0000000..f97b79d
--- /dev/null
+++ b/clang/test/SemaCXX/diagnose_if.cpp
@@ -0,0 +1,460 @@
+// RUN: %clang_cc1 %s -verify -fno-builtin -std=c++14
+
+#define _diagnose_if(...) __attribute__((diagnose_if(__VA_ARGS__)))
+
+namespace type_dependent {
+template <typename T>
+void neverok() _diagnose_if(!T(), "oh no", "error") {} // expected-note 4{{from 'diagnose_if'}}
+
+template <typename T>
+void alwaysok() _diagnose_if(T(), "oh no", "error") {}
+
+template <typename T>
+void alwayswarn() _diagnose_if(!T(), "oh no", "warning") {} // expected-note 4{{from 'diagnose_if'}}
+
+template <typename T>
+void neverwarn() _diagnose_if(T(), "oh no", "warning") {}
+
+void runAll() {
+  alwaysok<int>();
+  alwaysok<int>();
+
+  {
+    void (*pok)() = alwaysok<int>;
+    pok = &alwaysok<int>;
+  }
+
+  neverok<int>(); // expected-error{{oh no}}
+  neverok<short>(); // expected-error{{oh no}}
+
+  {
+    void (*pok)() = neverok<int>; // expected-error{{oh no}}
+  }
+  {
+    void (*pok)();
+    pok = &neverok<int>; // expected-error{{oh no}}
+  }
+
+  alwayswarn<int>(); // expected-warning{{oh no}}
+  alwayswarn<short>(); // expected-warning{{oh no}}
+  {
+    void (*pok)() = alwayswarn<int>; // expected-warning{{oh no}}
+    pok = &alwayswarn<int>; // expected-warning{{oh no}}
+  }
+
+  neverwarn<int>();
+  neverwarn<short>();
+  {
+    void (*pok)() = neverwarn<int>;
+    pok = &neverwarn<int>;
+  }
+}
+
+template <typename T>
+void errorIf(T a) _diagnose_if(T() != a, "oh no", "error") {} // expected-note {{candidate disabled: oh no}}
+
+template <typename T>
+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}}
+
+  warnIf(0);
+  warnIf(1); // expected-warning{{oh no}}
+}
+}
+
+namespace value_dependent {
+template <int N>
+void neverok() _diagnose_if(N == 0 || N != 0, "oh no", "error") {} // expected-note 4{{from 'diagnose_if'}}
+
+template <int N>
+void alwaysok() _diagnose_if(N == 0 && N != 0, "oh no", "error") {}
+
+template <int N>
+void alwayswarn() _diagnose_if(N == 0 || N != 0, "oh no", "warning") {} // expected-note 4{{from 'diagnose_if'}}
+
+template <int N>
+void neverwarn() _diagnose_if(N == 0 && N != 0, "oh no", "warning") {}
+
+void runAll() {
+  alwaysok<0>();
+  alwaysok<1>();
+
+  {
+    void (*pok)() = alwaysok<0>;
+    pok = &alwaysok<0>;
+  }
+
+  neverok<0>(); // expected-error{{oh no}}
+  neverok<1>(); // expected-error{{oh no}}
+
+  {
+    void (*pok)() = neverok<0>; // expected-error{{oh no}}
+  }
+  {
+    void (*pok)();
+    pok = &neverok<0>; // expected-error{{oh no}}
+  }
+
+  alwayswarn<0>(); // expected-warning{{oh no}}
+  alwayswarn<1>(); // expected-warning{{oh no}}
+  {
+    void (*pok)() = alwayswarn<0>; // expected-warning{{oh no}}
+    pok = &alwayswarn<0>; // expected-warning{{oh no}}
+  }
+
+  neverwarn<0>();
+  neverwarn<1>();
+  {
+    void (*pok)() = neverwarn<0>;
+    pok = &neverwarn<0>;
+  }
+}
+
+template <int N>
+void errorIf(int a) _diagnose_if(N != a, "oh no", "error") {} // expected-note {{candidate disabled: oh no}}
+
+template <int N>
+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}}
+
+  warnIf<0>(0);
+  warnIf<0>(1); // expected-warning{{oh no}}
+}
+}
+
+namespace no_overload_interaction {
+void foo(int) _diagnose_if(1, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+void foo(short);
+
+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 barArg(int);
+void barArg(short a) _diagnose_if(a, "oh no", "error");
+
+void runAll() {
+  foo(1); // expected-error{{oh no}}
+  bar(1);
+
+  fooArg(1); // expected-error{{call to unavailable function}}
+  barArg(1);
+
+  auto p = foo; // expected-error{{incompatible initializer of type '<overloaded function type>'}}
+}
+}
+
+namespace with_default_args {
+void foo(int a = 0) _diagnose_if(a, "oh no", "warning"); // expected-note 1{{from 'diagnose_if'}}
+void bar(int a = 1) _diagnose_if(a, "oh no", "warning"); // expected-note 2{{from 'diagnose_if'}}
+
+void runAll() {
+  foo();
+  foo(0);
+  foo(1); // expected-warning{{oh no}}
+
+  bar(); // expected-warning{{oh no}}
+  bar(0);
+  bar(1); // expected-warning{{oh no}}
+}
+}
+
+namespace naked_mem_expr {
+struct Foo {
+  void foo(int a) _diagnose_if(a, "should warn", "warning"); // expected-note{{from 'diagnose_if'}}
+  void bar(int a) _diagnose_if(a, "oh no", "error"); // expected-note{{from 'diagnose_if'}}
+};
+
+void runFoo() {
+  Foo().foo(0);
+  Foo().foo(1); // expected-warning{{should warn}}
+
+  Foo().bar(0);
+  Foo().bar(1); // expected-error{{oh no}}
+}
+}
+
+namespace class_template {
+template <typename T>
+struct Errors {
+  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 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 runErrors() {
+  Errors<int>().foo(0);
+  Errors<int>().foo(1); // expected-error{{bad i}}
+
+  Errors<int>().bar(0);
+  Errors<int>().bar(1); // expected-error{{bad i}}
+
+  Errors<int>().fooOvl(0);
+  Errors<int>().fooOvl(1); // expected-error{{call to unavailable}}
+  Errors<int>().fooOvl(short(0));
+  Errors<int>().fooOvl(short(1)); // expected-error{{call to unavailable}}
+
+  Errors<int>().barOvl(0);
+  Errors<int>().barOvl(1); // expected-error{{call to unavailable}}
+  Errors<int>().barOvl(short(0));
+  Errors<int>().barOvl(short(1)); // expected-error{{call to unavailable}}
+}
+
+template <typename T>
+struct Warnings {
+  void foo(int i) _diagnose_if(i, "bad i", "warning"); // expected-note{{from 'diagnose_if'}}
+  void bar(int i) _diagnose_if(i != T(), "bad i", "warning"); // expected-note{{from 'diagnose_if'}}
+
+  void fooOvl(int i) _diagnose_if(i, "int bad i", "warning"); // expected-note{{from 'diagnose_if'}}
+  void fooOvl(short i) _diagnose_if(i, "short bad i", "warning"); // expected-note{{from 'diagnose_if'}}
+
+  void barOvl(int i) _diagnose_if(i != T(), "int bad i", "warning"); // expected-note{{from 'diagnose_if'}}
+  void barOvl(short i) _diagnose_if(i != T(), "short bad i", "warning"); // expected-note{{from 'diagnose_if'}}
+};
+
+void runWarnings() {
+  Warnings<int>().foo(0);
+  Warnings<int>().foo(1); // expected-warning{{bad i}}
+
+  Warnings<int>().bar(0);
+  Warnings<int>().bar(1); // expected-warning{{bad i}}
+
+  Warnings<int>().fooOvl(0);
+  Warnings<int>().fooOvl(1); // expected-warning{{int bad i}}
+  Warnings<int>().fooOvl(short(0));
+  Warnings<int>().fooOvl(short(1)); // expected-warning{{short bad i}}
+
+  Warnings<int>().barOvl(0);
+  Warnings<int>().barOvl(1); // expected-warning{{int bad i}}
+  Warnings<int>().barOvl(short(0));
+  Warnings<int>().barOvl(short(1)); // expected-warning{{short bad i}}
+}
+}
+
+namespace template_specialization {
+template <typename T>
+struct Foo {
+  void foo() _diagnose_if(1, "override me", "error"); // expected-note{{from 'diagnose_if'}}
+  void bar(int i) _diagnose_if(i, "bad i", "error"); // expected-note{{from 'diagnose_if'}}
+  void baz(int i);
+};
+
+template <>
+struct Foo<int> {
+  void foo();
+  void bar(int i);
+  void baz(int i) _diagnose_if(i, "bad i", "error"); // expected-note{{from 'diagnose_if'}}
+};
+
+void runAll() {
+  Foo<double>().foo(); // expected-error{{override me}}
+  Foo<int>().foo();
+
+  Foo<double>().bar(1); // expected-error{{bad i}}
+  Foo<int>().bar(1);
+
+  Foo<double>().baz(1);
+  Foo<int>().baz(1); // expected-error{{bad i}}
+}
+}
+
+namespace late_constexpr {
+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 early() {
+  bar();
+  bar(0);
+  bar(1);
+}
+
+constexpr int foo() { return 1; }
+constexpr int foo(int a) { return a; }
+
+void late() {
+  bar(); // expected-error{{bad foo}}
+  bar(0);
+  bar(1); // expected-error{{call to unavailable function}}
+}
+}
+
+namespace late_parsed {
+struct Foo {
+  int i;
+  constexpr Foo(int i): i(i) {}
+  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}}
+
+  void go2() const _diagnose_if(isFooable(), "oh no", "error") // expected-note{{oh no}}
+      __attribute__((enable_if(true, ""))) {}
+  void go2() const _diagnose_if(isFooable(), "oh no", "error") {} // expected-note{{oh no}}
+
+  constexpr int go3() const _diagnose_if(isFooable(), "oh no", "error")
+      __attribute__((enable_if(true, ""))) {
+    return 1;
+  }
+
+  constexpr int go4() const _diagnose_if(isFooable(), "oh no", "error") {
+    return 1;
+  }
+  constexpr int go4() const _diagnose_if(isFooable(), "oh no", "error")
+      __attribute__((enable_if(true, ""))) {
+    return 1;
+  }
+
+  // We hope to support emitting these errors in the future. For now, though...
+  constexpr int runGo() const {
+    return go3() + go4();
+  }
+};
+
+void go(const Foo &f) _diagnose_if(f.isFooable(), "oh no", "error") {} // expected-note{{oh no}}
+
+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}}
+
+  Foo(0).go2();
+  Foo(1).go2(); // expected-error{{call to unavailable member function}}
+
+  go(Foo(0));
+  go(Foo(1)); // expected-error{{call to unavailable function}}
+}
+}
+
+namespace member_templates {
+struct Foo {
+  int i;
+  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}}
+    return T();
+  }
+
+  template <typename T>
+  constexpr T getVal2() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{oh no}}
+    return T();
+  }
+
+  template <typename T>
+  constexpr operator T() const _diagnose_if(bad(), "oh no", "error") { // expected-note{{oh no}}
+    return T();
+  }
+
+  // We hope to support emitting these errors in the future.
+  int run() { return getVal<int>() + getVal2<int>() + int(*this); }
+};
+
+void run() {
+  Foo(0).getVal<int>();
+  Foo(1).getVal<int>(); // expected-error{{call to unavailable member function}}
+
+  Foo(0).getVal2<int>();
+  Foo(1).getVal2<int>(); // expected-error{{call to unavailable member function}}
+
+  (void)int(Foo(0));
+  (void)int(Foo(1)); // expected-error{{uses deleted function}}
+}
+}
+
+namespace special_member_operators {
+struct Bar { int j; };
+struct Foo {
+  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}}
+    return nullptr;
+  }
+  void operator()() const _diagnose_if(bad(), "oh no", "error") {} // expected-note{{oh no}}
+};
+
+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}}
+};
+
+struct ParenTemplate {
+  int i;
+  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 run() {
+  (void)Foo(0)->j;
+  (void)Foo(1)->j; // expected-error{{selected unavailable operator '->'}}
+
+  Foo(0)();
+  Foo(1)(); // expected-error{{unavailable function call operator}}
+
+  ParenOverload(0)(1);
+  ParenOverload(0)(1.);
+
+  ParenOverload(1)(1); // expected-error{{unavailable function call operator}}
+  ParenOverload(1)(1.); // expected-error{{unavailable function call operator}}
+
+  ParenTemplate(0)(1);
+  ParenTemplate(0)(1.);
+
+  ParenTemplate(1)(1); // expected-error{{unavailable function call operator}}
+  ParenTemplate(1)(1.); // expected-error{{unavailable function call operator}}
+}
+
+void runLambda() {
+  auto L1 = [](int i) _diagnose_if(i, "oh no", "error") {}; // expected-note{{oh no}} expected-note{{conversion candidate}}
+  L1(0);
+  L1(1); // expected-error{{call to unavailable function call}}
+}
+}
+
+namespace ctors {
+struct Foo {
+  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") {
+    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") {
+    return *this;
+  }
+};
+
+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}}
+}
+}