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,9 @@ def note_max_tokens_total_override : Note<"total token limit set here">; +def warn_declare_with_symbols : Warning< + "use '%select{&|&&|~|^}0' when declaring%select{ lvalue| rvalue|}1 " + "%select{references|destructors|block pointers|ref-qualified member functions}2">, + 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 @@ -10,16 +10,17 @@ // //===----------------------------------------------------------------------===// -#include "clang/Parse/Parser.h" -#include "clang/Parse/RAIIObjectsForParser.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/PrettyDeclStackTrace.h" #include "clang/Basic/AddressSpaces.h" #include "clang/Basic/Attributes.h" #include "clang/Basic/CharInfo.h" +#include "clang/Basic/Diagnostic.h" #include "clang/Basic/TargetInfo.h" #include "clang/Parse/ParseDiagnostic.h" +#include "clang/Parse/Parser.h" +#include "clang/Parse/RAIIObjectsForParser.h" #include "clang/Sema/Lookup.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" @@ -5800,6 +5801,8 @@ SourceLocation Loc = ConsumeToken(); // Eat the *, ^, & or &&. D.SetRangeEnd(Loc); + const SourceManager &SM = PP.getSourceManager(); + if (Kind == tok::star || Kind == tok::caret) { // Is a pointer. DeclSpec DS(AttrFactory); @@ -5822,22 +5825,47 @@ DS.getVolatileSpecLoc(), DS.getRestrictSpecLoc(), DS.getAtomicSpecLoc(), DS.getUnalignedSpecLoc()), std::move(DS.getAttributes()), SourceLocation()); - else + else { + if (*SM.getCharacterData(Loc) != '^') { + constexpr int Caret = 3; + constexpr int Empty = 2; + constexpr int BlockPointers = 2; + Diag(Loc, diag::warn_declare_with_symbols) + << Caret << Empty << BlockPointers + << FixItHint::CreateReplacement(Loc, "^"); + } + // Remember that we parsed a Block type, and remember the type-quals. D.AddTypeInfo( DeclaratorChunk::getBlockPointer(DS.getTypeQualifiers(), Loc), std::move(DS.getAttributes()), SourceLocation()); + } } else { // Is a reference DeclSpec DS(AttrFactory); - // Complain about rvalue references in C++03, but then go on and build - // the declarator. - if (Kind == tok::ampamp) + constexpr int References = 0; + if (Kind == tok::amp && *SM.getCharacterData(Loc) != '&') { + constexpr int Lvalue = 0; + Diag(Loc, diag::warn_declare_with_symbols) + << Lvalue << Lvalue << References + << FixItHint::CreateReplacement(Loc, "&"); + } + 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 Rvalue = 1; + Diag(Loc, diag::warn_declare_with_symbols) + << Rvalue << Rvalue << References + << FixItHint::CreateReplacement(Loc, "&&"); + } + } + // GNU-style and C++11 attributes are allowed here, as is restrict. ParseTypeQualifierListOpt(DS); D.ExtendWithDeclSpec(DS); @@ -6688,6 +6716,16 @@ RefQualifierIsLValueRef = Tok.is(tok::amp); RefQualifierLoc = ConsumeToken(); + + if (*PP.getSourceManager().getCharacterData(RefQualifierLoc) != '&') { + int Qualifier = RefQualifierIsLValueRef ? 0 : 1; + constexpr int RefQualifiedMemberFunc = 3; + Diag(Tok.getLocation(), diag::warn_declare_with_symbols) + << Qualifier << Qualifier << RefQualifiedMemberFunc + << FixItHint::CreateReplacement(Tok.getLocation(), + RefQualifierIsLValueRef ? "&" : "&&"); + } + return true; } return false; diff --git a/clang/lib/Parse/ParseExprCXX.cpp b/clang/lib/Parse/ParseExprCXX.cpp --- a/clang/lib/Parse/ParseExprCXX.cpp +++ b/clang/lib/Parse/ParseExprCXX.cpp @@ -1728,6 +1728,16 @@ // Parse the tilde. assert(Tok.is(tok::tilde) && "ParseOptionalCXXScopeSpecifier fail"); + + if (*PP.getSourceManager().getCharacterData(Tok.getLocation()) != '~') { + constexpr int Tilde = 2; + constexpr int Empty = 2; + constexpr int Destructors = 1; + Diag(Tok.getLocation(), diag::warn_declare_with_symbols) + << Tilde << Empty << Destructors + << FixItHint::CreateReplacement(Tok.getLocation(), "~"); + } + SourceLocation TildeLoc = ConsumeToken(); if (Tok.is(tok::kw_decltype) && !FirstTypeName.isValid()) { @@ -2917,6 +2927,16 @@ // unary complement rather than treating ~X as referring to a destructor. // Parse the '~'. + + if (*PP.getSourceManager().getCharacterData(Tok.getLocation()) != '~') { + constexpr int Tilde = 2; + constexpr int Empty = 2; + constexpr int Destructors = 1; + Diag(Tok.getLocation(), diag::warn_declare_with_symbols) + << Tilde << Empty << Destructors + << FixItHint::CreateReplacement(Tok.getLocation(), "~"); + } + SourceLocation TildeLoc = ConsumeToken(); if (TemplateSpecified) { 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-with-symbols -pedantic-errors -fcxx-exceptions -fexceptions %s +// RUN: %clang_cc1 -verify -fsyntax-only -triple i386-linux -Wredundant-parens -Wno-declare-with-symbols -pedantic-errors -fcxx-exceptions -fexceptions -std=c++98 %s +// RUN: %clang_cc1 -verify -fsyntax-only -triple i386-linux -Wredundant-parens -Wno-declare-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-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,130 @@ +// RUN: %clang_cc1 -std=c++98 -fblocks -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++98 -fblocks -fsyntax-only -Wdeclare-with-symbols -verify %s +// RUN: %clang_cc1 -std=c++11 -fblocks -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++11 -fblocks -fsyntax-only -Wdeclare-with-symbols -verify %s +// RUN: %clang_cc1 -std=c++14 -fblocks -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++14 -fblocks -fsyntax-only -Wdeclare-with-symbols -verify %s +// RUN: %clang_cc1 -std=c++20 -fblocks -fsyntax-only -verify %s +// RUN: %clang_cc1 -std=c++20 -fblocks -fsyntax-only -Wdeclare-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 + +int(xor bp1)(); // expected-warning{{use '^' when declaring block pointers}} +int(^bp2)(); + +struct S1 { + compl S1(); // expected-warning{{use '~' when declaring destructors}} +}; + +struct S2 { + ~S2(); +}; + +S2::compl S2() // expected-warning{{use '~' when declaring destructors}} +{} + +struct S3 { + ~S3(); // no warning +}; + +struct S4 { + ~S4(); +}; + +S4::~S4() // no warning +{} + +#if __cplusplus >= 201103L +int and rr1 = 0; // expected-warning{{use '&&' when declaring rvalue references}} +int &&rr2 = 0; // no warning + +using bad_block_ptr = int(xor)(); // expected-warning{{use '^' when declaring block pointers}} +using good_block_ptr = int(^)(); + +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 references}} +using good_rr = int &&; // no warning + +auto and fr1 = i; // expected-warning{{use '&&' when declaring rvalue references}} +auto &&fr2 = i; // no warning + +auto and fr3 = 0; // expected-warning{{use '&&' when declaring rvalue references}} +auto &&fr4 = 0; // no warning + +template +void f(T and); // expected-warning{{use '&&' when declaring rvalue references}} + +template +void f(T &&); // no warning + +struct S5 { + void f() bitand; + // expected-warning@-1{{use '&' when declaring lvalue ref-qualified member functions}} + void f() const bitand; + // expected-warning@-1{{use '&' when declaring lvalue ref-qualified member functions}} + void f() volatile bitand; + // expected-warning@-1{{use '&' when declaring lvalue ref-qualified member functions}} + void f() const volatile bitand; + // expected-warning@-1{{use '&' when declaring lvalue ref-qualified member functions}} + void f() and; + // expected-warning@-1{{use '&&' when declaring rvalue ref-qualified member functions}} + void f() const and; + // expected-warning@-1{{use '&&' when declaring rvalue ref-qualified member functions}} + void f() volatile and; + // expected-warning@-1{{use '&&' when declaring rvalue ref-qualified member functions}} + void f() const volatile and; + // expected-warning@-1{{use '&&' when declaring rvalue ref-qualified member functions}} +}; + +struct S6 { + void f() &; // no warning + void f() const &; // no warning + void f() volatile &; // no warning + void f() const volatile &; // no warning + void f() &&; // no warning + void f() const &&; // no warning + void f() volatile &&; // no warning + void f() const volatile &&; // 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 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 references}} + +template +concept C4 = requires(T &&x) { x; }; // no warning +#endif // __cplusplus >= 202002L