]> granicus.if.org Git - clang/commitdiff
Fix whitespace handling in empty macro arguments
authorJustin Bogner <mail@justinbogner.com>
Tue, 4 Feb 2014 19:18:32 +0000 (19:18 +0000)
committerJustin Bogner <mail@justinbogner.com>
Tue, 4 Feb 2014 19:18:32 +0000 (19:18 +0000)
When a function-like macro definition ends with one of the macro's
parameters, and the argument is empty, any whitespace before the
parameter name in the macro definition needs to be preserved. Promoting
the existing NextTokGetsSpace to a preserved bit-field and checking it
at the end of the macro expansion allows it to be moved to the first
token following the macro expansion result.

Patch by Harald van Dijk!

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@200786 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Lex/TokenLexer.h
lib/Lex/TokenLexer.cpp
test/Preprocessor/macro_arg_empty.c [new file with mode: 0644]

index 7c8cfd028a28aa75a749a160221294ad32ea3263..659643dedf9f0a9be5ec4788cce51d1fccc4a9ce 100644 (file)
@@ -81,6 +81,14 @@ class TokenLexer {
   bool AtStartOfLine : 1;
   bool HasLeadingSpace : 1;
 
+  // NextTokGetsSpace - When this is true, the next token appended to the
+  // output list during function argument expansion will get a leading space,
+  // regardless of whether it had one to begin with or not. This is used for
+  // placemarker support. If still true after function argument expansion, the
+  // leading space will be applied to the first token following the macro
+  // expansion.
+  bool NextTokGetsSpace : 1;
+
   /// OwnsTokens - This is true if this TokenLexer allocated the Tokens
   /// array, and thus needs to free it when destroyed.  For simple object-like
   /// macros (for example) we just point into the token buffer of the macro
@@ -182,6 +190,13 @@ private:
   void updateLocForMacroArgTokens(SourceLocation ArgIdSpellLoc,
                                   Token *begin_tokens, Token *end_tokens);
 
+  /// Remove comma ahead of __VA_ARGS__, if present, according to compiler
+  /// dialect settings.  Returns true if the comma is removed.
+  bool MaybeRemoveCommaBeforeVaArgs(SmallVectorImpl<Token> &ResultToks,
+                                    bool HasPasteOperator,
+                                    MacroInfo *Macro, unsigned MacroArgNo,
+                                    Preprocessor &PP);
+
   void PropagateLineStartLeadingSpaceInfo(Token &Result);
 };
 
index d41af3f41a926bb2a67bb65fe923657d03ad49a7..2f29f7c9c8cd57ff62f4be8658fa7ba6edb4d6b7 100644 (file)
@@ -37,6 +37,7 @@ void TokenLexer::Init(Token &Tok, SourceLocation ELEnd, MacroInfo *MI,
   ExpandLocEnd = ELEnd;
   AtStartOfLine = Tok.isAtStartOfLine();
   HasLeadingSpace = Tok.hasLeadingSpace();
+  NextTokGetsSpace = false;
   Tokens = &*Macro->tokens_begin();
   OwnsTokens = false;
   DisableMacroExpansion = false;
@@ -95,6 +96,7 @@ void TokenLexer::Init(const Token *TokArray, unsigned NumToks,
   ExpandLocStart = ExpandLocEnd = SourceLocation();
   AtStartOfLine = false;
   HasLeadingSpace = false;
+  NextTokGetsSpace = false;
   MacroExpansionStart = SourceLocation();
 
   // Set HasLeadingSpace/AtStartOfLine so that the first token will be
@@ -119,13 +121,10 @@ void TokenLexer::destroy() {
   if (ActualArgs) ActualArgs->destroy(PP);
 }
 
-/// Remove comma ahead of __VA_ARGS__, if present, according to compiler dialect
-/// settings.  Returns true if the comma is removed.
-static bool MaybeRemoveCommaBeforeVaArgs(SmallVectorImpl<Token> &ResultToks,
-                                         bool &NextTokGetsSpace,
-                                         bool HasPasteOperator,
-                                         MacroInfo *Macro, unsigned MacroArgNo,
-                                         Preprocessor &PP) {
+bool TokenLexer::MaybeRemoveCommaBeforeVaArgs(SmallVectorImpl<Token> &ResultToks,
+                                              bool HasPasteOperator,
+                                              MacroInfo *Macro, unsigned MacroArgNo,
+                                              Preprocessor &PP) {
   // Is the macro argument __VA_ARGS__?
   if (!Macro->isVariadic() || MacroArgNo != Macro->getNumArgs()-1)
     return false;
@@ -179,11 +178,6 @@ void TokenLexer::ExpandFunctionArguments() {
   // we install the newly expanded sequence as the new 'Tokens' list.
   bool MadeChange = false;
 
-  // NextTokGetsSpace - When this is true, the next token appended to the
-  // output list will get a leading space, regardless of whether it had one to
-  // begin with or not.  This is used for placemarker support.
-  bool NextTokGetsSpace = false;
-
   for (unsigned i = 0, e = NumTokens; i != e; ++i) {
     // If we found the stringify operator, get the argument stringified.  The
     // preprocessor already verified that the following token is a macro name
@@ -256,7 +250,7 @@ void TokenLexer::ExpandFunctionArguments() {
     // In Microsoft mode, remove the comma before __VA_ARGS__ to ensure there
     // are no trailing commas if __VA_ARGS__ is empty.
     if (!PasteBefore && ActualArgs->isVarargsElidedUse() &&
-        MaybeRemoveCommaBeforeVaArgs(ResultToks, NextTokGetsSpace,
+        MaybeRemoveCommaBeforeVaArgs(ResultToks,
                                      /*HasPasteOperator=*/false,
                                      Macro, ArgNo, PP))
       continue;
@@ -311,9 +305,10 @@ void TokenLexer::ExpandFunctionArguments() {
                                              NextTokGetsSpace);
         NextTokGetsSpace = false;
       } else {
-        // If this is an empty argument, and if there was whitespace before the
-        // formal token, make sure the next token gets whitespace before it.
-        NextTokGetsSpace = CurTok.hasLeadingSpace();
+        // If this is an empty argument, if there was whitespace before the
+        // formal token, and this is not the first token in the macro
+        // definition, make sure the next token gets whitespace before it.
+        NextTokGetsSpace |= i != 0 && CurTok.hasLeadingSpace();
       }
       continue;
     }
@@ -396,7 +391,7 @@ void TokenLexer::ExpandFunctionArguments() {
     // the ## was a comma, remove the comma.  This is a GCC extension which is
     // disabled when using -std=c99.
     if (ActualArgs->isVarargsElidedUse())
-      MaybeRemoveCommaBeforeVaArgs(ResultToks, NextTokGetsSpace,
+      MaybeRemoveCommaBeforeVaArgs(ResultToks,
                                    /*HasPasteOperator=*/true,
                                    Macro, ArgNo, PP);
 
@@ -428,7 +423,7 @@ bool TokenLexer::Lex(Token &Tok) {
 
     Tok.startToken();
     Tok.setFlagValue(Token::StartOfLine , AtStartOfLine);
-    Tok.setFlagValue(Token::LeadingSpace, HasLeadingSpace);
+    Tok.setFlagValue(Token::LeadingSpace, HasLeadingSpace || NextTokGetsSpace);
     if (CurToken == 0)
       Tok.setFlag(Token::LeadingEmptyMacro);
     return PP.HandleEndOfTokenLexer(Tok);
diff --git a/test/Preprocessor/macro_arg_empty.c b/test/Preprocessor/macro_arg_empty.c
new file mode 100644 (file)
index 0000000..b5ecaa2
--- /dev/null
@@ -0,0 +1,7 @@
+// RUN: %clang_cc1 -E %s | FileCheck --strict-whitespace %s
+
+#define FOO(x) x
+#define BAR(x) x x
+#define BAZ(x) [x] [ x] [x ]
+[FOO()] [ FOO()] [FOO() ] [BAR()] [ BAR()] [BAR() ] BAZ()
+// CHECK: [] [ ] [ ] [ ] [ ] [ ] [] [ ] [ ]