// RUN: %check_clang_tidy -std=c++11-or-later %s bugprone-crtp-constructor-accessibility %t -- -- -fno-delayed-template-parsing

namespace class_implicit_ctor {
template <typename T>
class CRTP {};
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the implicit default constructor of the CRTP is publicly accessible; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: CRTP() = default;
// CHECK-FIXES: friend T;

class A : CRTP<A> {};
} // namespace class_implicit_ctor

namespace class_unconstructible {
template <typename T>
class CRTP {
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the CRTP cannot be constructed from the derived class; consider declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: friend T;
    CRTP() = default;
};

class A : CRTP<A> {};
} // namespace class_unconstructible

namespace class_public_default_ctor {
template <typename T>
class CRTP {
public:
    CRTP() = default;
    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public contructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
    // CHECK-FIXES: private:{{[[:space:]]*}}CRTP() = default;{{[[:space:]]*}}public:
    // CHECK-FIXES: friend T;
};

class A : CRTP<A> {};
} // namespace class_public_default_ctor

namespace class_public_user_provided_ctor {
template <typename T>
class CRTP {
public:
    CRTP(int) {}
    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public contructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
    // CHECK-FIXES: private:{{[[:space:]]*}}CRTP(int) {}{{[[:space:]]*}}public:
    // CHECK-FIXES: friend T;
};

class A : CRTP<A> {};
} // namespace class_public_user_provided_ctor

namespace class_public_multiple_user_provided_ctors {
template <typename T>
class CRTP {
public:
    CRTP(int) {}
    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public contructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
    // CHECK-FIXES: private:{{[[:space:]]*}}CRTP(int) {}{{[[:space:]]*}}public:
    CRTP(float) {}
    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public contructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
    // CHECK-FIXES: private:{{[[:space:]]*}}CRTP(float) {}{{[[:space:]]*}}public:
    
    // CHECK-FIXES: friend T;
    // CHECK-FIXES: friend T;
};

class A : CRTP<A> {};
} // namespace class_public_multiple_user_provided_ctors

namespace class_protected_ctors {
template <typename T>
class CRTP {
protected:
    CRTP(int) {}
    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: protected contructor allows the CRTP to be inherited from as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
    // CHECK-FIXES: private:{{[[:space:]]*}}CRTP(int) {}{{[[:space:]]*}}protected:
    CRTP() = default;
    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: protected contructor allows the CRTP to be inherited from as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
    // CHECK-FIXES: private:{{[[:space:]]*}}CRTP() = default;{{[[:space:]]*}}protected:
    CRTP(float) {}
    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: protected contructor allows the CRTP to be inherited from as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
    // CHECK-FIXES: private:{{[[:space:]]*}}CRTP(float) {}{{[[:space:]]*}}protected:
    
    // CHECK-FIXES: friend T;
    // CHECK-FIXES: friend T;
    // CHECK-FIXES: friend T;
};

class A : CRTP<A> {};
} // namespace class_protected_ctors

namespace struct_implicit_ctor {
template <typename T>
struct CRTP {};
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: the implicit default constructor of the CRTP is publicly accessible; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: private:{{[[:space:]]*}}CRTP() = default;{{[[:space:]]*}}public:
// CHECK-FIXES: friend T;

class A : CRTP<A> {};
} // namespace struct_implicit_ctor

namespace struct_default_ctor {
template <typename T>
struct CRTP {
    CRTP() = default;
    // CHECK-MESSAGES: :[[@LINE-1]]:5: warning: public contructor allows the CRTP to be constructed as a regular template class; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
    // CHECK-FIXES: private:{{[[:space:]]*}}CRTP() = default;{{[[:space:]]*}}public:
    // CHECK-FIXES: friend T;
};

class A : CRTP<A> {};
} // namespace struct_default_ctor

namespace same_class_multiple_crtps {
template <typename T>
struct CRTP {};
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: the implicit default constructor of the CRTP is publicly accessible; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: private:{{[[:space:]]*}}CRTP() = default;{{[[:space:]]*}}public:
// CHECK-FIXES: friend T;

template <typename T>
struct CRTP2 {};
// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: the implicit default constructor of the CRTP is publicly accessible; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: private:{{[[:space:]]*}}CRTP2() = default;{{[[:space:]]*}}public:
// CHECK-FIXES: friend T;

class A : CRTP<A>, CRTP2<A> {};
} // namespace same_class_multiple_crtps

namespace same_crtp_multiple_classes {
template <typename T>
class CRTP {
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the CRTP cannot be constructed from the derived class; consider declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: friend T;
    CRTP() = default;
};

class A : CRTP<A> {};
class B : CRTP<B> {};
} // namespace same_crtp_multiple_classes

namespace crtp_template {
template <typename T, typename U>
class CRTP {
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the CRTP cannot be constructed from the derived class; consider declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: friend U;
    CRTP() = default;
};

class A : CRTP<int, A> {};
} // namespace crtp_template

namespace crtp_template2 {
template <typename T, typename U>
class CRTP {
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the CRTP cannot be constructed from the derived class; consider declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: friend T;
    CRTP() = default;
};

class A : CRTP<A, A> {};
} // namespace crtp_template2

namespace template_derived {
template <typename T>
class CRTP {};
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the implicit default constructor of the CRTP is publicly accessible; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: CRTP() = default;
// CHECK-FIXES: friend T;

template<typename T>
class A : CRTP<A<T>> {};

// FIXME: Ideally the warning should be triggered without instantiation.
void foo() {
  A<int> A;
  (void) A;
}
} // namespace template_derived

namespace template_derived_explicit_specialization {
template <typename T>
class CRTP {};
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the implicit default constructor of the CRTP is publicly accessible; consider making it private and declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: CRTP() = default;
// CHECK-FIXES: friend T;

template<typename T>
class A : CRTP<A<T>> {};

template<>
class A<int> : CRTP<A<int>> {};
} // namespace template_derived_explicit_specialization

namespace explicit_derived_friend {
class A;

template <typename T>
class CRTP {
    CRTP() = default;
    friend A;
};

class A : CRTP<A> {};
} // namespace explicit_derived_friend

namespace explicit_derived_friend_multiple {
class A;

template <typename T>
class CRTP {
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the CRTP cannot be constructed from the derived class; consider declaring the derived class as friend [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: friend T;
    CRTP() = default;
    friend A;
};

class A : CRTP<A> {};
class B : CRTP<B> {};
} // namespace explicit_derived_friend_multiple

namespace no_need_for_friend {
class A;

template <typename T>
class CRTP {
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: the implicit default constructor of the CRTP is publicly accessible; consider making it private [bugprone-crtp-constructor-accessibility]
// CHECK-FIXES: CRTP() = default;
    friend A;
};

class A : CRTP<A> {};
} // namespace no_need_for_friend

namespace no_warning {
template <typename T>
class CRTP
{
    CRTP() = default;
    friend T;
};

class A : CRTP<A> {};
} // namespace no_warning

namespace no_warning_unsupported {
template<typename... Types>
class CRTP
{};

class A : CRTP<A> {};

void foo() {
    A A;
    (void) A;
}
} // namespace no_warning_unsupported
