diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1547,4 +1547,8 @@ def note_max_tokens_total_override : Note<"total token limit set here">; +def warn_declare_references_with_symbols : Warning< + "use '%select{&|&&}0' when declaring %select{lvalue|rvalue and forwarding}1 references">, + InGroup>; + } // end of Parser diagnostics diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -5830,14 +5830,26 @@ } else { // Is a reference DeclSpec DS(AttrFactory); + const SourceManager &SM = PP.getSourceManager(); - // Complain about rvalue references in C++03, but then go on and build - // the declarator. - if (Kind == tok::ampamp) + if (Kind == tok::amp && *SM.getCharacterData(Loc) != '&') { + constexpr int Amp = 0; + Diag(Loc, diag::warn_declare_references_with_symbols) << Amp << Amp; + } + if (Kind == tok::ampamp) { + // Complain about rvalue references in C++03, but then go on and build + // the declarator. Diag(Loc, getLangOpts().CPlusPlus11 ? diag::warn_cxx98_compat_rvalue_reference : diag::ext_rvalue_reference); + if (*SM.getCharacterData(Loc) != '&') { + constexpr int AmpAmp = 1; + Diag(Loc, diag::warn_declare_references_with_symbols) + << AmpAmp << AmpAmp; + } + } + // GNU-style and C++11 attributes are allowed here, as is restrict. ParseTypeQualifierListOpt(DS); D.ExtendWithDeclSpec(DS); diff --git a/clang/test/Parser/cxx-decl.cpp b/clang/test/Parser/cxx-decl.cpp --- a/clang/test/Parser/cxx-decl.cpp +++ b/clang/test/Parser/cxx-decl.cpp @@ -1,6 +1,6 @@ -// RUN: %clang_cc1 -verify -fsyntax-only -triple i386-linux -Wredundant-parens -pedantic-errors -fcxx-exceptions -fexceptions %s -// RUN: %clang_cc1 -verify -fsyntax-only -triple i386-linux -Wredundant-parens -pedantic-errors -fcxx-exceptions -fexceptions -std=c++98 %s -// RUN: %clang_cc1 -verify -fsyntax-only -triple i386-linux -Wredundant-parens -pedantic-errors -fcxx-exceptions -fexceptions -std=c++11 %s +// RUN: %clang_cc1 -verify -fsyntax-only -triple i386-linux -Wredundant-parens -Wno-declare-references-with-symbols -pedantic-errors -fcxx-exceptions -fexceptions %s +// RUN: %clang_cc1 -verify -fsyntax-only -triple i386-linux -Wredundant-parens -Wno-declare-references-with-symbols -pedantic-errors -fcxx-exceptions -fexceptions -std=c++98 %s +// RUN: %clang_cc1 -verify -fsyntax-only -triple i386-linux -Wredundant-parens -Wno-declare-references-with-symbols -pedantic-errors -fcxx-exceptions -fexceptions -std=c++11 %s const char const *x10; // expected-error {{duplicate 'const' declaration specifier}} diff --git a/clang/test/Parser/cxx0x-decl.cpp b/clang/test/Parser/cxx0x-decl.cpp --- a/clang/test/Parser/cxx0x-decl.cpp +++ b/clang/test/Parser/cxx0x-decl.cpp @@ -1,4 +1,4 @@ -// RUN: %clang_cc1 -verify -fsyntax-only -std=c++2a -pedantic-errors -triple x86_64-linux-gnu %s +// RUN: %clang_cc1 -verify -fsyntax-only -std=c++2a -Wno-declare-references-with-symbols -pedantic-errors -triple x86_64-linux-gnu %s // Make sure we know these are legitimate commas and not typos for ';'. namespace Commas { diff --git a/clang/test/Parser/warn-declare-references-with-symbols.cpp b/clang/test/Parser/warn-declare-references-with-symbols.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Parser/warn-declare-references-with-symbols.cpp @@ -0,0 +1,72 @@ +// RUN: %clang_cc1 -std=c++98 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++98 -fsyntax-only -Wdeclare-references-with-symbols -verify %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++11 -fsyntax-only -Wdeclare-references-with-symbols -verify %s +// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++14 -fsyntax-only -Wdeclare-references-with-symbols -verify %s +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++20 -fsyntax-only -Wdeclare-references-with-symbols -verify %s + +int i = 0; + +int bitand lr1 = i; // expected-warning{{use '&' when declaring lvalue references}} +int &lr2 = i; // no warning + +template +void f(T bitand); // expected-warning{{use '&' when declaring lvalue references}} + +template +void f(T &); // no warning + +#if __cplusplus >= 201103L +int and rr1 = 0; // expected-warning{{use '&&' when declaring rvalue and forwarding references}} +int &&rr2 = 0; // no warning + +using bad_lr = int bitand; // expected-warning{{use '&' when declaring lvalue references}} +using good_lr = int &; // no warning + +using bad_rr = int and; // expected-warning{{use '&&' when declaring rvalue and forwarding references}} +using good_rr = int &&; // no warning + +auto and fr1 = i; // expected-warning{{use '&&' when declaring rvalue and forwarding references}} +auto &&fr2 = i; // no warning + +auto and fr3 = 0; // expected-warning{{use '&&' when declaring rvalue and forwarding references}} +auto &&fr4 = 0; // no warning + +template +void f(T and); // expected-warning{{use '&&' when declaring rvalue and forwarding references}} + +template +void f(T &&); // no warning +#endif // __cplusplus > 201103L + +#if __cplusplus >= 201402L +template +T bitand lr3 = i; // expected-warning{{use '&' when declaring lvalue references}} + +template +T &lr4 = i; // no warning + +template +T and rr3 = i; // expected-warning{{use '&&' when declaring rvalue and forwarding references}} + +template +T &&rr4 = i; // no warning +#endif // __cplusplus >= 201402L + +#if __cplusplus >= 202002L +template +concept C1 = requires(T bitand x) { x; }; +// expected-warning@-1{{use '&' when declaring lvalue references}} + +template +concept C2 = requires(T &x) { x; }; // no warning + +template +concept C3 = requires(T and x) { x; }; +// expected-warning@-1{{use '&&' when declaring rvalue and forwarding references}} + +template +concept C4 = requires(T &&x) { x; }; // no warning +#endif // __cplusplus >= 202002L