// RUN: %clang_cc1 -fsyntax-only -verify %s -std=c++14

friend class A; // expected-error {{'friend' used outside of class}}
void f() { friend class A; } // expected-error {{'friend' used outside of class}}
class C { friend class A; };
class D { void f() { friend class A; } }; // expected-error {{'friend' used outside of class}}

// PR5760
namespace test0 {
  namespace ns {
    void f(int);
  }

  struct A {
    friend void ns::f(int a);
  };
}

// Test derived from LLVM's Registry.h
namespace test1 {
  template <class T> struct Outer {
    void foo(T);
    struct Inner {
      friend void Outer::foo(T);
    };
  };

  void test() {
    (void) Outer<int>::Inner();
  }
}

// PR5476
namespace test2 {
  namespace foo {
    void Func(int x);
  }

  class Bar {
    friend void ::test2::foo::Func(int x);
  };
}

// PR5134
namespace test3 {
  class Foo {
    friend const int getInt(int inInt = 0) {}

  };
}

namespace test4 {
  class T4A {
    friend class T4B;
  
  public:
    T4A(class T4B *);

  protected:
    T4B *mB;          // error here
  };
 
  class T4B {};
}

namespace rdar8529993 {
struct A { ~A(); };

struct B : A
{
  template<int> friend A::~A(); // expected-error {{destructor cannot be declared as a template}}
};
}

// PR7915
namespace test5 {
  struct A;
  struct A1 { friend void A(); };

  struct B { friend void B(); };
}

// PR8479
namespace test6_1 {
  class A {
   public:
   private:
    friend class vectorA;
    A() {}
  };
  class vectorA {
   public:
    vectorA(int i, const A& t = A()) {}
  };
  void f() {
    vectorA v(1);
  }
}
namespace test6_2 {
  template<class T>
  class vector {
   public:
    vector(int i, const T& t = T()) {}
  };
  class A {
   public:
   private:
    friend class vector<A>;
    A() {}
  };
  void f() {
    vector<A> v(1);
  }
}
namespace test6_3 {
  template<class T>
  class vector {
   public:
    vector(int i) {}
    void f(const T& t = T()) {}
  };
  class A {
   public:
   private:
    friend void vector<A>::f(const A&);
    A() {}
  };
  void f() {
    vector<A> v(1);
    v.f();
  }
}

namespace test7 {
  extern "C" {
    class X {
      friend int test7_f() { return 42; }
    };
  }
}

// PR15485
namespace test8 {
  namespace ns1 {
    namespace ns2 {
      template<class T> void f(T t); // expected-note {{target of using declaration}}
    }
    using ns2::f; // expected-note {{using declaration}}
  }
  struct A { void f(); }; // expected-note 2{{target of using declaration}}
  struct B : public A { using A::f; }; // expected-note {{using declaration}}
  template<typename T> struct C : A { using A::f; }; // expected-note {{using declaration}}
  struct X {
    template<class T> friend void ns1::f(T t); // expected-error {{cannot befriend target of using declaration}}
    friend void B::f(); // expected-error {{cannot befriend target of using declaration}}
    friend void C<int>::f(); // expected-error {{cannot befriend target of using declaration}}
  };
}

// PR16423
namespace test9 {
  class C {
  };
  struct A {
    friend void C::f(int, int, int) {}  // expected-error {{no function named 'f' with type 'void (int, int, int)' was found in the specified scope}}
  };
}

namespace test10 {
  struct X {};
  extern void f10_a();
  extern void f10_a(X);
  struct A {
    friend void f10_a();
    friend void f10_b();
    friend void f10_c();
    friend void f10_d();
    friend void f10_a(X);
    friend void f10_b(X);
    friend void f10_c(X);
    friend void f10_d(X);
  };
  extern void f10_b();
  extern void f10_b(X);
  struct B {
    friend void f10_a();
    friend void f10_b();
    friend void f10_c();
    friend void f10_d();
    friend void f10_a(X);
    friend void f10_b(X);
    friend void f10_c(X);
    friend void f10_d(X);
  };
  extern void f10_c();
  extern void f10_c(X);

  // FIXME: Give a better diagnostic for the case where a function exists but is
  // not visible.
  void g(X x) {
    f10_a();
    f10_b();
    f10_c();
    f10_d(); // expected-error {{undeclared identifier}}

    ::test10::f10_a();
    ::test10::f10_b();
    ::test10::f10_c();
    ::test10::f10_d(); // expected-error {{no member named 'f10_d'}}

    f10_a(x);
    f10_b(x);
    f10_c(x);
    f10_d(x); // PR16597: expected-error {{undeclared identifier}}

    ::test10::f10_a(x);
    ::test10::f10_b(x);
    ::test10::f10_c(x);
    ::test10::f10_d(x); // expected-error {{no type named 'f10_d'}}
  }

  struct Y : X {
    friend void f10_d();
    friend void f10_d(X);
  };

  struct Z {
    operator X();
    friend void f10_d();
    friend void f10_d(X);
  };

  void g(X x, Y y, Z z) {
    f10_d(); // expected-error {{undeclared identifier}}
    ::test10::f10_d(); // expected-error {{no member named 'f10_d'}}

    // f10_d is visible to ADL in the second and third cases.
    f10_d(x); // expected-error {{undeclared identifier}}
    f10_d(y);
    f10_d(z);

    // No ADL here.
    ::test10::f10_d(x); // expected-error {{no type named 'f10_d'}}
    ::test10::f10_d(y); // expected-error {{no type named 'f10_d'}}
    ::test10::f10_d(z); // expected-error {{no type named 'f10_d'}}
  }

  void local_externs(X x, Y y) {
    extern void f10_d();
    extern void f10_d(X);
    f10_d();
    f10_d(x);
    // FIXME: This lookup should fail, because the local extern declaration
    // should suppress ADL.
    f10_d(y);
    {
      int f10_d;
      f10_d(); // expected-error {{not a function}}
      f10_d(x); // expected-error {{not a function}}
      f10_d(y); // expected-error {{not a function}}
    }
  }

  void i(X x, Y y) {
    f10_d(); // expected-error {{undeclared identifier}}
    f10_d(x); // expected-error {{undeclared identifier}}
    f10_d(y);
  }

  struct C {
    friend void f10_d();
    friend void f10_d(X);
  };

  void j(X x, Y y) {
    f10_d(); // expected-error {{undeclared identifier}}
    f10_d(x); // expected-error {{undeclared identifier}}
    f10_d(y);
  }

  extern void f10_d();
  extern void f10_d(X);
  void k(X x, Y y, Z z) {
    // All OK now.
    f10_d();
    f10_d(x);
    ::test10::f10_d();
    ::test10::f10_d(x);
    ::test10::f10_d(y);
    ::test10::f10_d(z);
  }
}

namespace test11 {
  class __attribute__((visibility("hidden"))) B;

  class A {
    friend class __attribute__((visibility("hidden"), noreturn)) B; // expected-warning {{'noreturn' attribute only applies to functions and methods}}
  };
}

namespace pr21851 {
// PR21851 was a problem where we assumed that when the friend function redecl
// lookup found a C++ method, it would necessarily have a qualifier. Below we
// have some test cases where unqualified lookup finds C++ methods without using
// qualifiers. Unfortunately, we can't exercise the case of an access check
// failure because nested classes always have access to the members of outer
// classes.

void friend_own_method() {
  class A {
    void m() {}
    friend void m();
  };
}

void friend_enclosing_method() {
  class A;
  class C {
    int p;
    friend class A;
  };
  class A {
    void enclosing_friend() {
      (void)b->p;
      (void)c->p;
    }
    class B {
      void b(A *a) {
        (void)a->c->p;
      }
      int p;
      friend void enclosing_friend();
    };
    B *b;
    C *c;
  };
}

static auto friend_file_func() {
  extern void file_scope_friend();
  class A {
    int p;
    friend void file_scope_friend();
  };
  return A();
}

void file_scope_friend() {
  auto a = friend_file_func();
  (void)a.p;
}
}

template<typename T>
struct X_pr6954 {
  operator int();
  friend void f_pr6954(int x);
};

int array0_pr6954[sizeof(X_pr6954<int>)];
int array1_pr6954[sizeof(X_pr6954<float>)];

void g_pr6954() {
  f_pr6954(5); // expected-error{{undeclared identifier 'f_pr6954'}}
}

namespace tag_redecl {
  namespace N {
    struct X *p;
    namespace {
      class K {
        friend struct X;
      };
    }
  }
  namespace N {
    struct X;
    X *q = p;
  }
}
