// RUN: %clang_cc1 %s -std=c++17 -fsyntax-only -verify
// RUN: %clang_cc1 %s -DPEDANTIC -pedantic -fsyntax-only -verify

#if PEDANTIC
void g() {
  if (true)
    [[likely]] {} // expected-warning {{use of the 'likely' attribute is a C++20 extension}}
  else
    [[unlikely]] {} // expected-warning {{use of the 'unlikely' attribute is a C++20 extension}}
}
#else
void a() {
  if (true)
    [[likely]]; // expected-warning {{conflicting attributes 'likely' are ignored}}
  else
    [[likely]]; // expected-note {{conflicting attribute is here}}
}

void b() {
  if (true)
    [[unlikely]]; // expected-warning {{conflicting attributes 'unlikely' are ignored}}
  else
    [[unlikely]]; // expected-note {{conflicting attribute is here}}
}

void c() {
  if (true)
    [[likely]];
}

void d() {
  if (true)
    [[unlikely]];
}

void g() {
  if (true)
    [[likely]] {}
  else
    [[unlikely]] {}
}

void h() {
  if (true)
    [[likely]] {}
  else {
  }
}

void i() {
  if (true)
    [[unlikely]] {}
  else {
  }
}

void j() {
  if (true) {
  } else
    [[likely]] {}
}

void k() {
  if (true) {
  } else
    [[likely]] {}
}

void l() {
  if (true)
    [[likely]] {}
  else
    [[unlikely]] if (false) [[likely]] {}
}

void m() {
  [[likely]] int x = 42; // expected-error {{'likely' attribute cannot be applied to a declaration}}

  if (x)
    [[unlikely]] {}
  if (x) {
    [[unlikely]];
  }
  switch (x) {
  case 1:
    [[likely]] {}
    break;
    [[likely]] case 2 : case 3 : {}
    break;
  }

  do {
    [[unlikely]];
  } while (x);
  do
    [[unlikely]] {}
  while (x);
  do { // expected-note {{to match this 'do'}}
  }
  [[unlikely]] while (x); // expected-error {{expected 'while' in do/while loop}}
  for (;;)
    [[unlikely]] {}
  for (;;) {
    [[unlikely]];
  }
  while (x)
    [[unlikely]] {}
  while (x) {
    [[unlikely]];
  }

  switch (x)
    [[unlikely]] {}

  if (x)
    goto lbl;

  // FIXME: allow the attribute on the label
  [[unlikely]] lbl : // expected-error {{'unlikely' attribute cannot be applied to a declaration}}
                     [[likely]] x = x + 1;

  [[likely]]++ x;
}

void n() [[likely]] // expected-error {{'likely' attribute cannot be applied to types}}
{
  try
    [[likely]] {} // expected-error {{expected '{'}}
  catch (...) [[likely]] { // expected-error {{expected expression}}
  }
}

void o()
{
  // expected-warning@+2 {{attribute 'likely' has no effect when annotating an 'if constexpr' statement}}
  // expected-note@+1 {{annotating the 'if constexpr' statement here}}
  if constexpr (true) [[likely]];

  // expected-note@+1 {{annotating the 'if constexpr' statement here}}
  if constexpr (true) {
  // expected-warning@+1 {{attribute 'unlikely' has no effect when annotating an 'if constexpr' statement}}
  } else [[unlikely]];

  // Annotating both branches with conflicting likelihoods generates no diagnostic regarding the conflict.
  // expected-warning@+2 {{attribute 'likely' has no effect when annotating an 'if constexpr' statement}}
  // expected-note@+1 2 {{annotating the 'if constexpr' statement here}}
  if constexpr (true) [[likely]] {
  // expected-warning@+1 {{attribute 'likely' has no effect when annotating an 'if constexpr' statement}}
  } else [[likely]];

  if (1) [[likely, unlikely]] { // expected-error {{'unlikely' and 'likely' attributes are not compatible}} \
                                // expected-note {{conflicting attribute is here}}
  } else [[unlikely]][[likely]] { // expected-error {{'likely' and 'unlikely' attributes are not compatible}} \
                                  // expected-note {{conflicting attribute is here}}
  }
}

constexpr int constexpr_function() {
  [[likely]] return 0;
}
static_assert(constexpr_function() == 0);

constexpr double pow(double x, long long n) noexcept {
    if (n > 0) [[likely]]
        return x * pow(x, n - 1);
    else [[unlikely]]
        return 1;
}
constexpr long long fact(long long n) noexcept {
    if (n > 1) [[likely]]
        return n * fact(n - 1);
    else [[unlikely]]
        return 1;
}

#endif
