From a080a187fa7e538da3212c7d5e678e4b7ae03253 Mon Sep 17 00:00:00 2001 From: Manuel Klimek Date: Wed, 2 Jan 2013 16:30:12 +0000 Subject: [PATCH] Fixes use of unescaped newlines when formatting preprocessor directives. This is the first step towards handling preprocessor directives. This patch only fixes the most pressing issue, namely correctly escaping newlines for tokens within a sequence of a preprocessor directive. The next step will be to fix incorrect format decisions on #define directives. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@171393 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/Format.cpp | 22 +++++++++++++++++++-- lib/Format/UnwrappedLineParser.cpp | 24 +++++++++++++++++------ lib/Format/UnwrappedLineParser.h | 12 ++++++++++-- unittests/Format/FormatTest.cpp | 31 ++++++++++++++++++++++++++++++ 4 files changed, 79 insertions(+), 10 deletions(-) diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index 60b2f56745..8ea95c4862 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -434,9 +434,21 @@ private: /// each \c FormatToken. void replaceWhitespace(const FormatToken &Tok, unsigned NewLines, unsigned Spaces) { + std::string NewLineText; + if (!Line.InPPDirective) { + NewLineText = std::string(NewLines, '\n'); + } else if (NewLines > 0) { + unsigned Offset = + SourceMgr.getSpellingColumnNumber(Tok.WhiteSpaceStart) - 1; + for (unsigned i = 0; i < NewLines; ++i) { + NewLineText += std::string(Style.ColumnLimit - Offset - 1, ' '); + NewLineText += "\\\n"; + Offset = 0; + } + } Replaces.insert(tooling::Replacement( SourceMgr, Tok.WhiteSpaceStart, Tok.WhiteSpaceLength, - std::string(NewLines, '\n') + std::string(Spaces, ' '))); + NewLineText + std::string(Spaces, ' '))); } /// \brief Add a new line and the required indent before the first Token @@ -634,6 +646,9 @@ public: next(); if (Index >= Tokens.size()) return; + // It is the responsibility of the UnwrappedLineParser to make sure + // this sequence is not produced inside an unwrapped line. + assert(Tokens[Index].Tok.getIdentifierInfo() != NULL); switch (Tokens[Index].Tok.getIdentifierInfo()->getPPKeywordID()) { case tok::pp_include: case tok::pp_import: @@ -969,7 +984,10 @@ public: // Consume and record whitespace until we find a significant token. while (FormatTok.Tok.is(tok::unknown)) { - FormatTok.NewlinesBefore += tokenText(FormatTok.Tok).count('\n'); + StringRef Text = tokenText(FormatTok.Tok); + FormatTok.NewlinesBefore += Text.count('\n'); + FormatTok.HasUnescapedNewline = + Text.count("\\\n") != FormatTok.NewlinesBefore; FormatTok.WhiteSpaceLength += FormatTok.Tok.getLength(); if (FormatTok.Tok.is(tok::eof)) diff --git a/lib/Format/UnwrappedLineParser.cpp b/lib/Format/UnwrappedLineParser.cpp index 78a1abdcf8..614125b943 100644 --- a/lib/Format/UnwrappedLineParser.cpp +++ b/lib/Format/UnwrappedLineParser.cpp @@ -80,13 +80,25 @@ bool UnwrappedLineParser::parseBlock(unsigned AddLevels) { } void UnwrappedLineParser::parsePPDirective() { - while (!eof()) { - nextToken(); - if (FormatTok.NewlinesBefore > 0) { - addUnwrappedLine(); - return; - } + assert(FormatTok.Tok.is(tok::hash) && "'#' expected"); + nextToken(); + + Line.InPPDirective = true; + if (FormatTok.Tok.getIdentifierInfo() == NULL) { + addUnwrappedLine(); + Line.InPPDirective = false; + return; } + + do { + if (FormatTok.NewlinesBefore > 0 && + FormatTok.HasUnescapedNewline) { + break; + } + nextToken(); + } while (!eof()); + addUnwrappedLine(); + Line.InPPDirective = false; } void UnwrappedLineParser::parseComments() { diff --git a/lib/Format/UnwrappedLineParser.h b/lib/Format/UnwrappedLineParser.h index ccf437f507..9ed796d45b 100644 --- a/lib/Format/UnwrappedLineParser.h +++ b/lib/Format/UnwrappedLineParser.h @@ -30,7 +30,8 @@ namespace format { /// \brief A wrapper around a \c Token storing information about the /// whitespace characters preceeding it. struct FormatToken { - FormatToken() : NewlinesBefore(0), WhiteSpaceLength(0) { + FormatToken() + : NewlinesBefore(0), HasUnescapedNewline(false), WhiteSpaceLength(0) { } /// \brief The \c Token. @@ -42,6 +43,10 @@ struct FormatToken { /// and thereby e.g. leave an empty line between two function definitions. unsigned NewlinesBefore; + /// \brief Whether there is at least one unescaped newline before the \c + /// Token. + bool HasUnescapedNewline; + /// \brief The location of the start of the whitespace immediately preceeding /// the \c Token. /// @@ -60,7 +65,7 @@ struct FormatToken { /// \c UnwrappedLineFormatter. The key property is that changing the formatting /// within an unwrapped line does not affect any other unwrapped lines. struct UnwrappedLine { - UnwrappedLine() : Level(0) { + UnwrappedLine() : Level(0), InPPDirective(false) { } /// \brief The \c Token comprising this \c UnwrappedLine. @@ -68,6 +73,9 @@ struct UnwrappedLine { /// \brief The indent level of the \c UnwrappedLine. unsigned Level; + + /// \brief Whether this \c UnwrappedLine is part of a preprocessor directive. + bool InPPDirective; }; class UnwrappedLineConsumer { diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index b52a9fd7c3..daae94bb13 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -373,6 +373,37 @@ TEST_F(FormatTest, StaticInitializers) { " \"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\" };"); } +TEST_F(FormatTest, FormatsSmallMacroDefinitionsInSingleLine) { + verifyFormat("#define ALooooooooooooooooooooooooooooooooooooooongMacro(" + " \\\n" + " aLoooooooooooooooooooooooongFuuuuuuuuuuuuuunctiooooooooo)"); +} + +TEST_F(FormatTest, BreaksOnHashWhenDirectiveIsInvalid) { + EXPECT_EQ("#\n;", format("#;")); +} + +TEST_F(FormatTest, UnescapedEndOfLineEndsPPDirective) { + EXPECT_EQ("#line 42 \"test\"\n", + format("# \\\n line \\\n 42 \\\n \"test\"\n")); + EXPECT_EQ("#define A B\n", format("# \\\n define \\\n A \\\n B\n")); +} + +TEST_F(FormatTest, EndOfFileEndsPPDirective) { + EXPECT_EQ("#line 42 \"test\"", + format("# \\\n line \\\n 42 \\\n \"test\"")); + EXPECT_EQ("#define A B", format("# \\\n define \\\n A \\\n B")); +} + +TEST_F(FormatTest, MixingPreprocessorDirectivesAndNormalCode) { + verifyFormat("#define ALooooooooooooooooooooooooooooooooooooooongMacro(" + " \\\n" + " aLoooooooooooooooooooooooongFuuuuuuuuuuuuuunctiooooooooo)\n" + "\n" + "AlooooooooooooooooooooooooooooooooooooooongCaaaaaaaaaal(\n" + " aLooooooooooooooooooooooonPaaaaaaaaaaaaaaaaaaaaarmmmm);\n"); +} + //===----------------------------------------------------------------------===// // Line break tests. //===----------------------------------------------------------------------===// -- 2.40.0