From 7c8a5bdbb0e994524cfcc25e2a8495155fd257c8 Mon Sep 17 00:00:00 2001 From: Krasimir Georgiev Date: Thu, 2 Feb 2017 14:36:50 +0000 Subject: [PATCH] [clang-format] Fix breaking of comment sections in unwrapped lines containing newlines. Summary: The breaking of line comment sections was misaligning the case where the first comment line is on an unwrapped line containing newlines. In this case, the breaking column must be based on the source column of the last token that is preceded by a newline, not on the first token of the unwrapped line. source: ``` enum A { a, // line 1 // line 2 }; ``` format before: ``` enum A { a, // line 1 // line 2 }; ``` format after: ``` enum A { a, // line 1 // line 2 }; ``` Reviewers: djasper Reviewed By: djasper Subscribers: cfe-commits, klimek Differential Revision: https://reviews.llvm.org/D29444 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@293891 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/BreakableToken.cpp | 2 +- lib/Format/FormatToken.h | 5 ++++ lib/Format/UnwrappedLineParser.cpp | 39 ++++++++++++++++++++++++++-- unittests/Format/FormatTest.cpp | 41 +++++++++++++++++++++++++++++- 4 files changed, 83 insertions(+), 4 deletions(-) diff --git a/lib/Format/BreakableToken.cpp b/lib/Format/BreakableToken.cpp index 09f17a35d0..e9dd253f3e 100644 --- a/lib/Format/BreakableToken.cpp +++ b/lib/Format/BreakableToken.cpp @@ -695,7 +695,7 @@ BreakableLineCommentSection::BreakableLineCommentSection( Content[i] = Content[i].substr(0, EndOfLine); } LineTok = CurrentTok->Next; - if (CurrentTok->Next && CurrentTok->Next->NewlinesBefore > 1) { + if (CurrentTok->Next && !CurrentTok->Next->ContinuesLineCommentSection) { // A line comment section needs to broken by a line comment that is // preceded by at least two newlines. Note that we put this break here // instead of breaking at a previous stage during parsing, since that diff --git a/lib/Format/FormatToken.h b/lib/Format/FormatToken.h index c838cfbe1d..d6a2ff286a 100644 --- a/lib/Format/FormatToken.h +++ b/lib/Format/FormatToken.h @@ -261,6 +261,11 @@ struct FormatToken { /// Only set if \c Type == \c TT_StartOfName. bool PartOfMultiVariableDeclStmt = false; + /// \brief Does this line comment continue a line comment section? + /// + /// Only set to true if \c Type == \c TT_LineComment. + bool ContinuesLineCommentSection = false; + /// \brief If this is a bracket, this points to the matching one. FormatToken *MatchingParen = nullptr; diff --git a/lib/Format/UnwrappedLineParser.cpp b/lib/Format/UnwrappedLineParser.cpp index a96d4d9be3..2c7b87ee01 100644 --- a/lib/Format/UnwrappedLineParser.cpp +++ b/lib/Format/UnwrappedLineParser.cpp @@ -2066,31 +2066,58 @@ static bool continuesLineComment(const FormatToken &FormatTok, // original start column of the min column token of the line. // // For example, the second line comment continues the first in these cases: + // // // first line // // second line + // // and: + // // // first line // // second line + // // and: + // // int i; // first line // // second line + // // and: + // // do { // first line // // second line // int i; // } while (true); // + // and: + // + // enum { + // a, // first line + // // second line + // b + // }; + // // The second line comment doesn't continue the first in these cases: + // // // first line // // second line + // // and: + // // int i; // first line // // second line + // // and: + // // do { // first line // // second line // int i; // } while (true); + // + // and: + // + // enum { + // a, // first line + // // second line + // }; const FormatToken *MinColumnToken = Line.Tokens.front().Tok; // Scan for '{//'. If found, use the column of '{' as a min column for line @@ -2103,6 +2130,11 @@ static bool continuesLineComment(const FormatToken &FormatTok, break; } PreviousToken = Node.Tok; + + // Grab the last newline preceding a token in this unwrapped line. + if (Node.Tok->NewlinesBefore > 0) { + MinColumnToken = Node.Tok; + } } if (PreviousToken && PreviousToken->is(tok::l_brace)) { MinColumnToken = PreviousToken; @@ -2130,7 +2162,8 @@ void UnwrappedLineParser::flushComments(bool NewlineBeforeNext) { // // FIXME: Consider putting separate line comment sections as children to the // unwrapped line instead. - if (isOnNewLine(**I) && JustComments && !continuesLineComment(**I, *Line)) + (*I)->ContinuesLineCommentSection = continuesLineComment(**I, *Line); + if (isOnNewLine(**I) && JustComments && !(*I)->ContinuesLineCommentSection) addUnwrappedLine(); pushToken(*I); } @@ -2196,7 +2229,9 @@ void UnwrappedLineParser::readToken() { if (!FormatTok->Tok.is(tok::comment)) return; - if (!continuesLineComment(*FormatTok, *Line) && + FormatTok->ContinuesLineCommentSection = + continuesLineComment(*FormatTok, *Line); + if (!FormatTok->ContinuesLineCommentSection && (isOnNewLine(*FormatTok) || FormatTok->IsFirst)) { CommentsInCurrentLine = false; } diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 497015bb09..1f81f5fbd2 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -951,7 +951,46 @@ TEST_F(FormatTest, UnderstandsSingleLineComments) { " c\n" "};", getLLVMStyleWithColumns(20))); - + EXPECT_EQ("enum A {\n" + " a, // line 1\n" + " // line 2\n" + "};", + format("enum A {\n" + " a, // line 1\n" + " // line 2\n" + "};", + getLLVMStyleWithColumns(20))); + EXPECT_EQ("enum A {\n" + " a, // line 1\n" + " // line 2\n" + "};", + format("enum A {\n" + " a, // line 1\n" + " // line 2\n" + "};", + getLLVMStyleWithColumns(20))); + EXPECT_EQ("enum A {\n" + " a, // line 1\n" + " // line 2\n" + " b\n" + "};", + format("enum A {\n" + " a, // line 1\n" + " // line 2\n" + " b\n" + "};", + getLLVMStyleWithColumns(20))); + EXPECT_EQ("enum A {\n" + " a, // line 1\n" + " // line 2\n" + " b\n" + "};", + format("enum A {\n" + " a, // line 1\n" + " // line 2\n" + " b\n" + "};", + getLLVMStyleWithColumns(20))); verifyFormat( "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa =\n" " bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb; // Trailing comment"); -- 2.40.0