rZhBoYao created this revision.
rZhBoYao added reviewers: clang-language-wg, aaron.ballman, jyknight.
Herald added a project: All.
rZhBoYao requested review of this revision.
Herald added a project: clang.
Herald added a subscriber: cfe-commits.

As a language extension, if an invalid a UDL's suffix can result in macro
expansion, it is treated as if whitespace preceded it. This allows legacy
code to co-exist with new code via -Wno-reserved-user-defined-literal.
The following code results in string concat not calling literal operator.

  const char* s = "FOO"BAR;

Address comments in D153156 <https://reviews.llvm.org/D153156>


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D158372

Files:
  clang/docs/ReleaseNotes.rst
  clang/include/clang/Basic/DiagnosticLexKinds.td
  clang/include/clang/Basic/DiagnosticSemaKinds.td
  clang/lib/Lex/Lexer.cpp
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/test/CXX/drs/dr14xx.cpp
  clang/test/CXX/drs/dr17xx.cpp
  clang/test/CXX/lex/lex.literal/lex.ext/p10.cpp

Index: clang/test/CXX/lex/lex.literal/lex.ext/p10.cpp
===================================================================
--- clang/test/CXX/lex/lex.literal/lex.ext/p10.cpp
+++ clang/test/CXX/lex/lex.literal/lex.ext/p10.cpp
@@ -1,8 +1,8 @@
 // RUN: %clang_cc1 -std=c++11 -verify %s
 
 using size_t = decltype(sizeof(int));
-void operator ""wibble(const char *); // expected-warning {{user-defined literal suffixes not starting with '_' are reserved; no literal will invoke this operator}}
-void operator ""wibble(const char *, size_t); // expected-warning {{user-defined literal suffixes not starting with '_' are reserved; no literal will invoke this operator}}
+void operator ""wibble(const char *); // expected-warning {{user-defined literal suffixes not starting with '_' are reserved}}
+void operator ""wibble(const char *, size_t); // expected-warning {{user-defined literal suffixes not starting with '_' are reserved}}
 
 template<typename T>
 void f() {
Index: clang/test/CXX/drs/dr17xx.cpp
===================================================================
--- clang/test/CXX/drs/dr17xx.cpp
+++ clang/test/CXX/drs/dr17xx.cpp
@@ -143,7 +143,7 @@
 #if __cplusplus >= 201103L
   float operator ""_E(const char *);
   float operator ""E(const char *);
-  // expected-warning@-1 {{user-defined literal suffixes not starting with '_' are reserved; no literal will invoke this operator}}
+  // expected-warning@-1 {{user-defined literal suffixes not starting with '_' are reserved}}
 #endif
 }
 
Index: clang/test/CXX/drs/dr14xx.cpp
===================================================================
--- clang/test/CXX/drs/dr14xx.cpp
+++ clang/test/CXX/drs/dr14xx.cpp
@@ -489,7 +489,13 @@
 #if __cplusplus >= 201103L
   float operator ""_E(const char *);
   float operator ""E(const char *); // don't err on the lack of spaces even when the literal suffix identifier is invalid
-  // expected-warning@-1 {{user-defined literal suffixes not starting with '_' are reserved; no literal will invoke this operator}}
+  // expected-warning@-1 {{user-defined literal suffixes not starting with '_' are reserved}}
+  const char* s0 = "FOO"BAR;
+  // expected-error@-1 {{no matching literal operator for call to 'operator""BAR' with arguments of types 'const char *' and 'unsigned long', and no matching literal operator template}}
+#define BAR "BAZ"
+  const char* s1 = "FOO"BAR;
+  // expected-error@-1 {{invalid suffix on literal; C++11 requires a space between literal and a macro}}
+#undef BAR
 #endif
 }
 
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -16571,8 +16571,7 @@
     //   contain a double underscore __ are reserved for use by C++
     //   implementations.
     Diag(FnDecl->getLocation(), diag::warn_user_literal_reserved)
-        << static_cast<int>(Status)
-        << StringLiteralParser::isValidUDSuffix(getLangOpts(), II->getName());
+        << static_cast<int>(Status);
   }
 
   return false;
Index: clang/lib/Lex/Lexer.cpp
===================================================================
--- clang/lib/Lex/Lexer.cpp
+++ clang/lib/Lex/Lexer.cpp
@@ -1986,6 +1986,7 @@
   assert(LangOpts.CPlusPlus);
 
   // Maximally munch an identifier.
+  const char *TokStart = CurPtr;
   unsigned Size;
   char C = getCharAndSize(CurPtr, Size);
   bool Consumed = false;
@@ -2012,10 +2013,43 @@
   // that does not start with an underscore is ill-formed. We assume a suffix
   // beginning with a UCN or UTF-8 character is more likely to be a ud-suffix
   // than a macro, however, and accept that.
+  bool IsUDSuffix = false;
+  if (!Consumed) {
+    if (C == '_')
+      IsUDSuffix = true;
+    else if (IsStringLiteral && LangOpts.CPlusPlus14) {
+      // In C++1y, we need to look ahead a few characters to see if this is a
+      // valid suffix for a string literal or a numeric literal (this could be
+      // the 'operator""if' defining a numeric literal operator).
+      const unsigned MaxStandardSuffixLength = 3;
+      char Buffer[MaxStandardSuffixLength] = {C};
+      unsigned Consumed = Size;
+      unsigned Chars = 1;
+      while (true) {
+        unsigned NextSize;
+        char Next = getCharAndSizeNoWarn(CurPtr + Consumed, NextSize, LangOpts);
+        if (!isAsciiIdentifierContinue(Next)) {
+          // End of suffix. Check whether this is on the allowed list.
+          const StringRef CompleteSuffix(Buffer, Chars);
+          IsUDSuffix =
+              StringLiteralParser::isValidUDSuffix(LangOpts, CompleteSuffix);
+          break;
+        }
+
+        if (Chars == MaxStandardSuffixLength)
+          // Too long: can't be a standard suffix.
+          break;
+
+        Buffer[Chars++] = Next;
+        Consumed += NextSize;
+      }
+    }
+  }
+
   if (!Consumed)
     CurPtr = ConsumeChar(CurPtr, Size, Result);
-
-  Result.setFlag(Token::HasUDSuffix);
+  if (IsUDSuffix)
+    Result.setFlag(Token::HasUDSuffix);
   while (true) {
     C = getCharAndSize(CurPtr, Size);
     if (isAsciiIdentifierContinue(C)) {
@@ -2026,6 +2060,24 @@
       break;
   }
 
+  // As a conforming extension, we treat invalid suffixes as if they had
+  // whitespace before them if doing so results in macro expansions.
+  if (!isLexingRawMode() && !IsUDSuffix) {
+    unsigned TokLen = CurPtr - TokStart;
+    Result.setLength(TokLen);
+    Result.setLocation(getSourceLocation(TokStart, TokLen));
+    Result.setKind(tok::raw_identifier);
+    Result.setRawIdentifierData(TokStart);
+    IdentifierInfo *II = PP->LookUpIdentifierInfo(Result);
+    if (II->hasMacroDefinition()) {
+      Diag(TokStart, LangOpts.MSVCCompat
+                         ? diag::ext_ms_reserved_user_defined_literal
+                         : diag::ext_reserved_user_defined_literal)
+          << FixItHint::CreateInsertion(getSourceLocation(TokStart), " ");
+      return TokStart;
+    }
+  }
+
   return CurPtr;
 }
 
Index: clang/include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9319,8 +9319,7 @@
   "string literal operator templates are a GNU extension">,
   InGroup<GNUStringLiteralOperatorTemplate>;
 def warn_user_literal_reserved : Warning<
-  "user-defined literal suffixes %select{<ERROR>|not starting with '_'|containing '__'}0 are reserved"
-  "%select{; no literal will invoke this operator|}1">,
+  "user-defined literal suffixes %select{<ERROR>|not starting with '_'|containing '__'}0 are reserved">,
   InGroup<UserDefinedLiterals>;
 
 // C++ conversion functions
Index: clang/include/clang/Basic/DiagnosticLexKinds.td
===================================================================
--- clang/include/clang/Basic/DiagnosticLexKinds.td
+++ clang/include/clang/Basic/DiagnosticLexKinds.td
@@ -276,6 +276,12 @@
   "identifier after literal will be treated as a reserved user-defined literal "
   "suffix in C++11">,
   InGroup<CXX11CompatReservedUserDefinedLiteral>, DefaultIgnore;
+def ext_reserved_user_defined_literal : ExtWarn<
+  "invalid suffix on literal; C++11 requires a space between literal and "
+  "a macro">, InGroup<ReservedUserDefinedLiteral>, DefaultError;
+def ext_ms_reserved_user_defined_literal : ExtWarn<
+  "invalid suffix on literal; C++11 requires a space between literal and "
+  "a macro">, InGroup<ReservedUserDefinedLiteral>;
 def err_unsupported_string_concat : Error<
   "unsupported non-standard concatenation of string literals">;
 
Index: clang/docs/ReleaseNotes.rst
===================================================================
--- clang/docs/ReleaseNotes.rst
+++ clang/docs/ReleaseNotes.rst
@@ -92,10 +92,11 @@
 
 Resolutions to C++ Defect Reports
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- Implemented `CWG1473 <https://wg21.link/CWG1473>`_ which allows spaces after ``operator""``.
-  Clang used to err on the lack of space when the literal suffix identifier was invalid in
-  all the language modes, which contradicted the deprecation of the whitespaces.
-  Also turn on ``-Wdeprecated-literal-operator`` by default in all the language modes.
+- Implemented `CWG1473 <https://wg21.link/CWG1473>`_ allowing lack of space after ``operator""``.
+  Clang used to err on the lack of space when the literal suffix identifier was invalid,
+  contradicting ``-Wdeprecated-literal-operator`` which is now default on.
+  Instead, Clang now emits error only if the invalid suffix looks like a macro,
+  and treat it as if it were preceded by whitespace.
 
 C Language Changes
 ------------------
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to