Index: include/clang/Basic/DiagnosticGroups.td =================================================================== --- include/clang/Basic/DiagnosticGroups.td +++ include/clang/Basic/DiagnosticGroups.td @@ -273,6 +273,7 @@ def InitializerOverrides : DiagGroup<"initializer-overrides">; def NonNull : DiagGroup<"nonnull">; def NonPODVarargs : DiagGroup<"non-pod-varargs">; +def NonTemplateFriend : DiagGroup<"non-template-friend">; def ClassVarargs : DiagGroup<"class-varargs", [NonPODVarargs]>; def : DiagGroup<"nonportable-cfstrings">; def NonVirtualDtor : DiagGroup<"non-virtual-dtor">; Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -1142,6 +1142,11 @@ "enclosing namespace is a Microsoft extension; add a nested name specifier">, InGroup; def err_pure_friend : Error<"friend declaration cannot have a pure-specifier">; +def warn_non_template_friend : Warning<"friend declaration %q0 declares a " + "non-template function">, InGroup; +def note_non_template_friend : Note<"if this is not what you intended, make " + "sure the function template has already been declared and add <> after the " + "function name here">; def err_invalid_member_in_interface : Error< "%select{data member |non-public member function |static member function |" Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -8312,6 +8312,15 @@ AddToScope = false; } + if (isFriend && !NewFD->isInvalidDecl()) { + if (R->isDependentType() && TemplateParamLists.empty() && + !isFunctionTemplateSpecialization && + (D.getFunctionDefinitionKind() == FDK_Declaration)) { + Diag(NameInfo.getLoc(), diag::warn_non_template_friend) << NewFD; + Diag(NameInfo.getLoc(), diag::note_non_template_friend); + } + } + return NewFD; } Index: test/CXX/drs/dr3xx.cpp =================================================================== --- test/CXX/drs/dr3xx.cpp +++ test/CXX/drs/dr3xx.cpp @@ -291,7 +291,8 @@ template void g(N::A<0>::B<0>); namespace N { - template struct I { friend bool operator==(const I&, const I&); }; + template struct I { friend bool operator==(const I&, const I&); }; // expected-warning{{friend declaration 'dr321::N::operator==' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}} } N::I i, j; bool x = i == j; Index: test/CXX/drs/dr5xx.cpp =================================================================== --- test/CXX/drs/dr5xx.cpp +++ test/CXX/drs/dr5xx.cpp @@ -581,8 +581,10 @@ namespace dr557 { // dr557: yes template struct S { - friend void f(S *); - friend void g(S > *); + friend void f(S *); // expected-warning{{friend declaration 'dr557::f' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}} + friend void g(S > *); // expected-warning{{friend declaration 'dr557::g' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}} }; void x(S *p, S > *q) { f(p); Index: test/CXX/temp/temp.decls/temp.friend/p1.cpp =================================================================== --- test/CXX/temp/temp.decls/temp.friend/p1.cpp +++ test/CXX/temp/temp.decls/temp.friend/p1.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -verify -emit-llvm-only %s +// RUN: %clang_cc1 -verify -emit-llvm-only -Wno-non-template-friend %s namespace test0 { template struct Num { Index: test/PCH/cxx-templates.h =================================================================== --- test/PCH/cxx-templates.h +++ test/PCH/cxx-templates.h @@ -1,4 +1,5 @@ // Header for PCH test cxx-templates.cpp +#pragma clang diagnostic ignored "-Wnon-template-friend" template struct S; Index: test/PCH/cxx-templates.cpp =================================================================== --- test/PCH/cxx-templates.cpp +++ test/PCH/cxx-templates.cpp @@ -104,7 +104,7 @@ } #ifndef NO_ERRORS -// expected-error@cxx-templates.h:305 {{incomplete}} +// expected-error@cxx-templates.h:306 {{incomplete}} template int local_extern::f(); // expected-note {{in instantiation of}} #endif template int local_extern::g(); Index: test/SemaCXX/friend.cpp =================================================================== --- test/SemaCXX/friend.cpp +++ test/SemaCXX/friend.cpp @@ -21,7 +21,8 @@ template struct Outer { void foo(T); struct Inner { - friend void Outer::foo(T); + friend void Outer::foo(T); // expected-warning{{friend declaration 'test1::Outer::Inner::foo' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}} }; }; @@ -363,3 +364,47 @@ f_pr6954(5); // expected-error{{undeclared identifier 'f_pr6954'}} } + +template void pr23342_func(T x); +template +struct pr23342_C1 { + friend void pr23342_func<>(T x); + friend bool func(T x); // expected-warning{{friend declaration 'func' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}} + friend bool func2(int x); + template friend bool func3(T2 x); + friend T func4(); // expected-warning{{friend declaration 'func4' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}} +}; + +namespace pr23342 { + +template +struct C1 { + friend void pr23342_func<>(T x); + friend bool func(T x); // expected-warning{{friend declaration 'pr23342::func' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}} + friend bool func2(int x); + template friend bool func3(T2 x); + friend T func4(); // expected-warning{{friend declaration 'pr23342::func4' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}}}; +}; + +template +struct Arg { + friend bool operator==(const Arg& lhs, T rhs) { + return false; + } + friend bool operator!=(const Arg& lhs, T rhs); // expected-warning{{friend declaration 'pr23342::operator!=' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}}}; +}; +template +bool operator!=(const Arg& lhs, T rhs) { + return true; +} +bool foo() { + Arg arg; + return (arg == 42) || (arg != 42); +} + +} Index: test/SemaCXX/overload-call.cpp =================================================================== --- test/SemaCXX/overload-call.cpp +++ test/SemaCXX/overload-call.cpp @@ -566,13 +566,15 @@ // Ensure that overload resolution attempts to complete argument types when // performing ADL. template struct S { - friend int f(const S&); + friend int f(const S&); // expected-warning{{friend declaration 'IncompleteArg::f' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}} }; extern S s; int k = f(s); template struct Op { - friend bool operator==(const Op &, const Op &); + friend bool operator==(const Op &, const Op &); // expected-warning{{friend declaration 'IncompleteArg::operator==' declares a non-template function}} + // expected-note@-1{{if this is not what you intended, make sure the function template has already been declared and add <> after the function name here}} }; extern Op op; bool b = op == op;