diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -4122,7 +4122,8 @@ bool RValueThis, unsigned ThisQuals); CXXDestructorDecl *LookupDestructor(CXXRecordDecl *Class); - bool checkLiteralOperatorId(const CXXScopeSpec &SS, const UnqualifiedId &Id); + bool checkLiteralOperatorId(const CXXScopeSpec &SS, const UnqualifiedId &Id, + bool IsUDSuffix); LiteralOperatorLookupResult LookupLiteralOperator(Scope *S, LookupResult &R, ArrayRef ArgTys, bool AllowRaw, bool AllowTemplate, diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -1081,10 +1081,9 @@ ReservedIdentifierStatus NamedDecl::isReserved(const LangOptions &LangOpts) const { const IdentifierInfo *II = getIdentifier(); - if (!II) - if (const auto *FD = dyn_cast(this)) - II = FD->getLiteralIdentifier(); + // This triggers at least for CXXLiteralIdentifiers, which we already checked + // at lexing time. if (!II) return ReservedIdentifierStatus::NotReserved; 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 @@ -9,7 +9,6 @@ // This file implements the Expression parsing implementation for C++. // //===----------------------------------------------------------------------===// -#include "clang/Parse/Parser.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclTemplate.h" @@ -17,6 +16,7 @@ #include "clang/Basic/PrettyStackTrace.h" #include "clang/Lex/LiteralSupport.h" #include "clang/Parse/ParseDiagnostic.h" +#include "clang/Parse/Parser.h" #include "clang/Parse/RAIIObjectsForParser.h" #include "clang/Sema/DeclSpec.h" #include "clang/Sema/ParsedTemplate.h" @@ -2636,9 +2636,10 @@ // Grab the literal operator's suffix, which will be either the next token // or a ud-suffix from the string literal. + bool IsUDSuffix = !Literal.getUDSuffix().empty(); IdentifierInfo *II = nullptr; SourceLocation SuffixLoc; - if (!Literal.getUDSuffix().empty()) { + if (IsUDSuffix) { II = &PP.getIdentifierTable().get(Literal.getUDSuffix()); SuffixLoc = Lexer::AdvanceToTokenCharacter(TokLocs[Literal.getUDSuffixToken()], @@ -2675,7 +2676,7 @@ Result.setLiteralOperatorId(II, KeywordLoc, SuffixLoc); - return Actions.checkLiteralOperatorId(SS, Result); + return Actions.checkLiteralOperatorId(SS, Result, IsUDSuffix); } // Parse a conversion-function-id. diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp --- a/clang/lib/Sema/SemaExprCXX.cpp +++ b/clang/lib/Sema/SemaExprCXX.cpp @@ -484,8 +484,25 @@ } bool Sema::checkLiteralOperatorId(const CXXScopeSpec &SS, - const UnqualifiedId &Name) { + const UnqualifiedId &Name, bool IsUDSuffix) { assert(Name.getKind() == UnqualifiedIdKind::IK_LiteralOperatorId); + if (!IsUDSuffix) { + // [over.literal] p8 + // + // double operator""_Bq(long double); // OK: not a reserved identifier + // double operator"" _Bq(long double); // ill-formed, no diagnostic required + IdentifierInfo *II = Name.Identifier; + ReservedIdentifierStatus Status = II->isReserved(PP.getLangOpts()); + SourceLocation Loc = Name.getEndLoc(); + if (Status != ReservedIdentifierStatus::NotReserved && + !PP.getSourceManager().isInSystemHeader(Loc)) { + Diag(Loc, diag::warn_reserved_extern_symbol) + << II << static_cast(Status) + << FixItHint::CreateReplacement( + Name.getSourceRange(), + (StringRef("operator\"\"") + II->getName()).str()); + } + } if (!SS.isValid()) return false; diff --git a/clang/test/Sema/reserved-identifier.cpp b/clang/test/Sema/reserved-identifier.cpp --- a/clang/test/Sema/reserved-identifier.cpp +++ b/clang/test/Sema/reserved-identifier.cpp @@ -76,11 +76,19 @@ int _barbatruc; // no-warning } -long double operator"" _BarbeBleue(long double) // expected-warning {{identifier 'operator""_BarbeBleue' is reserved because it starts with '_' followed by a capital letter}} +long double operator"" _BarbeBleue(long double) // expected-warning {{identifier '_BarbeBleue' is reserved because it starts with '_' followed by a capital letter}} { return 0.; } +long double operator""_SacreBleu(long double) // no-warning +{ + return 0.; +} + +long double sacrebleu = operator"" _SacreBleu(1.2); // expected-warning {{identifier '_SacreBleu' is reserved because it starts with '_' followed by a capital letter}} +long double sangbleu = operator""_SacreBleu(1.2); // no-warning + struct _BarbeRouge { // expected-warning {{identifier '_BarbeRouge' is reserved because it starts with '_' followed by a capital letter}} } p; struct _BarbeNoire { // expected-warning {{identifier '_BarbeNoire' is reserved because it starts with '_' followed by a capital letter}}