Fix a couple of issues with literal-operator-id parsing, and provide recovery
for a few kinds of error. Specifically:

Since we're after translation phase 6, the "" token might be formed by multiple
source-level string literals. Checking the token width is not a correct way of
detecting empty string literals, due to escaped newlines. Diagnose and recover
from a missing space between "" and suffix, and from string literals other than
"", which are followed by a suffix.


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@152348 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td
index fa54990..c5cab86 100644
--- a/include/clang/Basic/DiagnosticParseKinds.td
+++ b/include/clang/Basic/DiagnosticParseKinds.td
@@ -429,6 +429,8 @@
 def err_dup_virtual : Error<"duplicate 'virtual' in base specifier">;
 
 // C++ operator overloading
+def err_literal_operator_string_prefix : Error<
+  "string literal after 'operator' cannot have an encoding prefix">;
 def err_literal_operator_string_not_empty : Error<
   "string literal after 'operator' must be '\"\"'">;
 def err_literal_operator_missing_space : Error<
diff --git a/lib/Parse/ParseExprCXX.cpp b/lib/Parse/ParseExprCXX.cpp
index c735c6f..847b396 100644
--- a/lib/Parse/ParseExprCXX.cpp
+++ b/lib/Parse/ParseExprCXX.cpp
@@ -15,6 +15,7 @@
 #include "clang/Parse/Parser.h"
 #include "RAIIObjectsForParser.h"
 #include "clang/Basic/PrettyStackTrace.h"
+#include "clang/Lex/LiteralSupport.h"
 #include "clang/Sema/DeclSpec.h"
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/ParsedTemplate.h"
@@ -1903,24 +1904,68 @@
   //   literal-operator-id: [C++0x 13.5.8]
   //     operator "" identifier
 
-  if (getLang().CPlusPlus0x && Tok.is(tok::string_literal)) {
+  if (getLang().CPlusPlus0x && isTokenStringLiteral()) {
     Diag(Tok.getLocation(), diag::warn_cxx98_compat_literal_operator);
-    // FIXME: Add a FixIt to insert a space before the suffix, and recover.
-    if (Tok.hasUDSuffix()) {
-      Diag(Tok.getLocation(), diag::err_literal_operator_missing_space);
-      return true;
-    }
-    if (Tok.getLength() != 2)
-      Diag(Tok.getLocation(), diag::err_literal_operator_string_not_empty);
-    ConsumeStringToken();
 
-    if (Tok.isNot(tok::identifier)) {
+    SourceLocation DiagLoc;
+    unsigned DiagId = 0;
+
+    // We're past translation phase 6, so perform string literal concatenation
+    // before checking for "".
+    llvm::SmallVector<Token, 4> Toks;
+    llvm::SmallVector<SourceLocation, 4> TokLocs;
+    while (isTokenStringLiteral()) {
+      if (!Tok.is(tok::string_literal) && !DiagId) {
+        DiagLoc = Tok.getLocation();
+        DiagId = diag::err_literal_operator_string_prefix;
+      }
+      Toks.push_back(Tok);
+      TokLocs.push_back(ConsumeStringToken());
+    }
+
+    StringLiteralParser Literal(Toks.data(), Toks.size(), PP);
+    if (Literal.hadError)
+      return true;
+
+    // Grab the literal operator's suffix, which will be either the next token
+    // or a ud-suffix from the string literal.
+    IdentifierInfo *II = 0;
+    SourceLocation SuffixLoc;
+    if (!Literal.getUDSuffix().empty()) {
+      II = &PP.getIdentifierTable().get(Literal.getUDSuffix());
+      SuffixLoc =
+        Lexer::AdvanceToTokenCharacter(TokLocs[Literal.getUDSuffixToken()],
+                                       Literal.getUDSuffixOffset(),
+                                       PP.getSourceManager(), getLang());
+      // This form is not permitted by the standard (yet).
+      DiagLoc = SuffixLoc;
+      DiagId = diag::err_literal_operator_missing_space;
+    } else if (Tok.is(tok::identifier)) {
+      II = Tok.getIdentifierInfo();
+      SuffixLoc = ConsumeToken();
+      TokLocs.push_back(SuffixLoc);
+    } else {
       Diag(Tok.getLocation(), diag::err_expected_ident);
       return true;
     }
 
-    IdentifierInfo *II = Tok.getIdentifierInfo();
-    Result.setLiteralOperatorId(II, KeywordLoc, ConsumeToken());
+    // The string literal must be empty.
+    if (!Literal.GetString().empty() || Literal.Pascal) {
+      DiagLoc = TokLocs.front();
+      DiagId = diag::err_literal_operator_string_not_empty;
+    }
+
+    if (DiagId) {
+      // This isn't a valid literal-operator-id, but we think we know
+      // what the user meant. Tell them what they should have written.
+      llvm::SmallString<32> Str;
+      Str += "\"\" ";
+      Str += II->getName();
+      Diag(DiagLoc, DiagId) << FixItHint::CreateReplacement(
+          SourceRange(TokLocs.front(), TokLocs.back()), Str);
+    }
+
+    Result.setLiteralOperatorId(II, KeywordLoc, SuffixLoc);
     return false;
   }
   
diff --git a/test/FixIt/fixit-cxx0x.cpp b/test/FixIt/fixit-cxx0x.cpp
index 2bf9b20..dcd9f74 100644
--- a/test/FixIt/fixit-cxx0x.cpp
+++ b/test/FixIt/fixit-cxx0x.cpp
@@ -65,3 +65,15 @@
 const char *p = "foo"bar; // expected-error {{requires a space between}}
 #define ord - '0'
 int k = '4'ord; // expected-error {{requires a space between}}
+
+void operator""_x(char); // expected-error {{requires a space}}
+void operator"x" _y(char); // expected-error {{must be '""'}}
+void operator L"" _z(char); // expected-error {{encoding prefix}}
+void operator "x" "y" U"z" ""_whoops "z" "y"(char); // expected-error {{must be '""'}}
+
+void f() {
+  'a'_x;
+  'b'_y;
+  'c'_z;
+  'd'_whoops;
+}
diff --git a/test/Parser/cxx11-user-defined-literals.cpp b/test/Parser/cxx11-user-defined-literals.cpp
index b34680d..7a4df2b 100644
--- a/test/Parser/cxx11-user-defined-literals.cpp
+++ b/test/Parser/cxx11-user-defined-literals.cpp
@@ -92,3 +92,21 @@
 int k =
 1234567.89\
 _no_such_suffix; // expected-error {{'_no_such_suffix'}}
+
+// Make sure we handle more interesting ways of writing a string literal which
+// is "" in translation phase 7.
+void operator "\
+" _foo(unsigned long long); // ok
+
+void operator R"xyzzy()xyzzy" _foo(long double); // ok
+
+void operator"" "" R"()" "" _foo(const char *); // ok
+
+// Ensure we diagnose the bad cases.
+void operator "\0" _non_empty(const char *); // expected-error {{must be '""'}}
+void operator ""_no_space(const char *); // expected-error {{C++11 requires a space}}
+void operator L"" _not_char(const char *); // expected-error {{cannot have an encoding prefix}}
+void operator "" ""
+U"" // expected-error {{cannot have an encoding prefix}}
+"" _also_not_char(const char *);
+void operator "" u8"" "\u0123" "hello"_all_of_the_things ""(const char*); // expected-error {{must be '""'}}