MSVC: A wide string literal from L#macro_arg in a macro
authorAlexey Bataev <a.bataev@hotmail.com>
Mon, 15 Dec 2014 04:18:11 +0000 (04:18 +0000)
committerAlexey Bataev <a.bataev@hotmail.com>
Mon, 15 Dec 2014 04:18:11 +0000 (04:18 +0000)
Clang should form a wide string literal from L#macro_arg in a function-like macro in -fms-compatibility mode.
Fix for http://llvm.org/PR9984.
Differential Revision: http://reviews.llvm.org/D6604

llvm-svn: 224228

clang/include/clang/Lex/Token.h
clang/lib/Lex/TokenLexer.cpp
clang/test/Lexer/ms-compatibility.c [new file with mode: 0644]

index a58c0c5..a048757 100644 (file)
@@ -66,7 +66,7 @@ class Token {
   tok::TokenKind Kind;
 
   /// Flags - Bits we track about this token, members of the TokenFlags enum.
-  unsigned char Flags;
+  unsigned short Flags;
 public:
 
   // Various flags set per token:
@@ -80,7 +80,9 @@ public:
     LeadingEmptyMacro = 0x10, // Empty macro exists before this token.
     HasUDSuffix = 0x20,    // This string or character literal has a ud-suffix.
     HasUCN = 0x40,         // This identifier contains a UCN.
-    IgnoredComma = 0x80    // This comma is not a macro argument separator (MS).
+    IgnoredComma = 0x80,   // This comma is not a macro argument separator (MS).
+    StringifiedInMacro = 0x100, // This string or character literal is formed by
+                                // macro stringizing or charizing operator.
   };
 
   tok::TokenKind getKind() const { return Kind; }
@@ -262,6 +264,12 @@ public:
 
   /// Returns true if this token contains a universal character name.
   bool hasUCN() const { return (Flags & HasUCN) ? true : false; }
+
+  /// Returns true if this token is formed by macro by stringizing or charizing
+  /// operator.
+  bool stringifiedInMacro() const {
+    return (Flags & StringifiedInMacro) ? true : false;
+  }
 };
 
 /// \brief Information about the conditional stack (\#if directives)
index 45c8b01..5f4705e 100644 (file)
@@ -206,6 +206,7 @@ void TokenLexer::ExpandFunctionArguments() {
                                            ExpansionLocStart,
                                            ExpansionLocEnd);
       }
+      Res.setFlag(Token::StringifiedInMacro);
 
       // The stringified/charified string leading space flag gets set to match
       // the #/#@ operator.
@@ -405,6 +406,14 @@ void TokenLexer::ExpandFunctionArguments() {
   }
 }
 
+/// \brief Checks if two tokens form wide string literal.
+static bool isWideStringLiteralFromMacro(const Token &FirstTok,
+                                         const Token &SecondTok) {
+  return FirstTok.is(tok::identifier) &&
+         FirstTok.getIdentifierInfo()->isStr("L") && SecondTok.isLiteral() &&
+         SecondTok.stringifiedInMacro();
+}
+
 /// Lex - Lex and return a token from this macro stream.
 ///
 bool TokenLexer::Lex(Token &Tok) {
@@ -435,7 +444,13 @@ bool TokenLexer::Lex(Token &Tok) {
 
   // If this token is followed by a token paste (##) operator, paste the tokens!
   // Note that ## is a normal token when not expanding a macro.
-  if (!isAtEnd() && Tokens[CurToken].is(tok::hashhash) && Macro) {
+  if (!isAtEnd() && Macro &&
+      (Tokens[CurToken].is(tok::hashhash) ||
+       // Special processing of L#x macros in -fms-compatibility mode.
+       // Microsoft compiler is able to form a wide string literal from
+       // 'L#macro_arg' construct in a function-like macro.
+       (PP.getLangOpts().MSVCCompat &&
+        isWideStringLiteralFromMacro(Tok, Tokens[CurToken])))) {
     // When handling the microsoft /##/ extension, the final token is
     // returned by PasteTokens, not the pasted token.
     if (PasteTokens(Tok))
@@ -511,9 +526,10 @@ bool TokenLexer::PasteTokens(Token &Tok) {
   SourceLocation StartLoc = Tok.getLocation();
   SourceLocation PasteOpLoc;
   do {
-    // Consume the ## operator.
+    // Consume the ## operator if any.
     PasteOpLoc = Tokens[CurToken].getLocation();
-    ++CurToken;
+    if (Tokens[CurToken].is(tok::hashhash))
+      ++CurToken;
     assert(!isAtEnd() && "No token on the RHS of a paste operator!");
 
     // Get the RHS token.
diff --git a/clang/test/Lexer/ms-compatibility.c b/clang/test/Lexer/ms-compatibility.c
new file mode 100644 (file)
index 0000000..d159ad1
--- /dev/null
@@ -0,0 +1,11 @@
+// RUN: %clang_cc1 -fsyntax-only -E -fms-compatibility %s | FileCheck --check-prefix=CHECK-MS-COMPAT %s
+// RUN: %clang_cc1 -fsyntax-only -E %s | FileCheck --check-prefix=CHECK-NO-MS-COMPAT %s
+
+#define FN(x) L#x
+#define F L "aaa"
+void *v1 = FN(aaa);
+void *v2 = F;
+// CHECK-MS-COMPAT: void *v1 = L"aaa";
+// CHECK-MS-COMPAT: void *v2 = L "aaa";
+// CHECK-NO-MS-COMPAT: void *v1 = L "aaa";
+// CHECK-NO-MS-COMPAT: void *v2 = L "aaa";