Index: lib/Format/BreakableToken.h =================================================================== --- lib/Format/BreakableToken.h +++ lib/Format/BreakableToken.h @@ -212,8 +212,8 @@ /// \p StartColumn specifies the column in which the comment will start after /// formatting. BreakableComment(const FormatToken &Token, unsigned StartColumn, - bool InPPDirective, encoding::Encoding Encoding, - const FormatStyle &Style); + bool InPPDirective, LineType ThisLineType, + encoding::Encoding Encoding, const FormatStyle &Style); public: unsigned getLineCount() const override; @@ -239,6 +239,8 @@ virtual bool mayReflow(unsigned LineIndex, llvm::Regex &CommentPragmasRegex) const = 0; + LineType ThisLineType; + // Contains the original text of the lines of the block comment. // // In case of a block comments, excludes the leading /* in the first line and @@ -286,8 +288,8 @@ public: BreakableBlockComment(const FormatToken &Token, unsigned StartColumn, unsigned OriginalStartColumn, bool FirstInLine, - bool InPPDirective, encoding::Encoding Encoding, - const FormatStyle &Style); + bool InPPDirective, LineType ThisLineType, + encoding::Encoding Encoding, const FormatStyle &Style); unsigned getLineLengthAfterSplit(unsigned LineIndex, unsigned TailOffset, StringRef::size_type Length) const override; @@ -354,7 +356,8 @@ public: BreakableLineCommentSection(const FormatToken &Token, unsigned StartColumn, unsigned OriginalStartColumn, bool FirstInLine, - bool InPPDirective, encoding::Encoding Encoding, + bool InPPDirective, LineType ThisLineType, + encoding::Encoding Encoding, const FormatStyle &Style); unsigned getLineLengthAfterSplit(unsigned LineIndex, unsigned TailOffset, Index: lib/Format/BreakableToken.cpp =================================================================== --- lib/Format/BreakableToken.cpp +++ lib/Format/BreakableToken.cpp @@ -218,10 +218,11 @@ BreakableComment::BreakableComment(const FormatToken &Token, unsigned StartColumn, bool InPPDirective, + LineType ThisLineType, encoding::Encoding Encoding, const FormatStyle &Style) : BreakableToken(Token, InPPDirective, Encoding, Style), - StartColumn(StartColumn) {} + ThisLineType(ThisLineType), StartColumn(StartColumn) {} unsigned BreakableComment::getLineCount() const { return Lines.size(); } @@ -229,8 +230,10 @@ BreakableComment::getSplit(unsigned LineIndex, unsigned TailOffset, unsigned ColumnLimit, llvm::Regex &CommentPragmasRegex) const { - // Don't break lines matching the comment pragmas regex. - if (CommentPragmasRegex.match(Content[LineIndex])) + // Don't break lines matching the comment pragmas regex and trailing comments + // in import statements, as they are usually too long. + if (CommentPragmasRegex.match(Content[LineIndex]) || + ThisLineType == LT_ImportStatement) return Split(StringRef::npos, 0); return getCommentSplit(Content[LineIndex].substr(TailOffset), getContentStartColumn(LineIndex, TailOffset), @@ -320,8 +323,10 @@ BreakableBlockComment::BreakableBlockComment( const FormatToken &Token, unsigned StartColumn, unsigned OriginalStartColumn, bool FirstInLine, bool InPPDirective, - encoding::Encoding Encoding, const FormatStyle &Style) - : BreakableComment(Token, StartColumn, InPPDirective, Encoding, Style) { + LineType ThisLineType, encoding::Encoding Encoding, + const FormatStyle &Style) + : BreakableComment(Token, StartColumn, InPPDirective, ThisLineType, + Encoding, Style) { assert(Tok.is(TT_BlockComment) && "block comment section must start with a block comment"); @@ -635,6 +640,7 @@ bool BreakableBlockComment::mayReflow(unsigned LineIndex, llvm::Regex &CommentPragmasRegex) const { + if (ThisLineType == LT_ImportStatement) return false; // Content[LineIndex] may exclude the indent after the '*' decoration. In that // case, we compute the start of the comment pragma manually. StringRef IndentContent = Content[LineIndex]; @@ -658,8 +664,10 @@ BreakableLineCommentSection::BreakableLineCommentSection( const FormatToken &Token, unsigned StartColumn, unsigned OriginalStartColumn, bool FirstInLine, bool InPPDirective, - encoding::Encoding Encoding, const FormatStyle &Style) - : BreakableComment(Token, StartColumn, InPPDirective, Encoding, Style) { + LineType ThisLineType, encoding::Encoding Encoding, + const FormatStyle &Style) + : BreakableComment(Token, StartColumn, InPPDirective, ThisLineType, + Encoding, Style) { assert(Tok.is(TT_LineComment) && "line comment section must start with a line comment"); FormatToken *LineTok = nullptr; Index: lib/Format/ContinuationIndenter.cpp =================================================================== --- lib/Format/ContinuationIndenter.cpp +++ lib/Format/ContinuationIndenter.cpp @@ -1141,9 +1141,11 @@ if (Current.isNot(TT_BlockComment) && Current.IsMultiline) return addMultilineToken(Current, State); - // Don't break implicit string literals or import statements. + // Don't break implicit string literals or import statements, however let + // BreakableComment manage trailing line comment sections in import statement + // lines. if (Current.is(TT_ImplicitStringLiteral) || - State.Line->Type == LT_ImportStatement) + (Current.isNot(TT_LineComment) && State.Line->Type == LT_ImportStatement)) return 0; if (!Current.isStringLiteral() && !Current.is(tok::comment)) @@ -1201,17 +1203,15 @@ return addMultilineToken(Current, State); Token.reset(new BreakableBlockComment( Current, StartColumn, Current.OriginalColumn, !Current.Previous, - State.Line->InPPDirective, Encoding, Style)); - } else if (Current.is(TT_LineComment) && - (Current.Previous == nullptr || - Current.Previous->isNot(TT_ImplicitStringLiteral))) { + State.Line->InPPDirective, State.Line->Type, Encoding, Style)); + } else if (Current.is(TT_LineComment)) { if (!Style.ReflowComments || CommentPragmasRegex.match(Current.TokenText.substr(2)) || switchesFormatting(Current)) return 0; Token.reset(new BreakableLineCommentSection( Current, StartColumn, Current.OriginalColumn, !Current.Previous, - /*InPPDirective=*/false, Encoding, Style)); + /*InPPDirective=*/false, State.Line->Type, Encoding, Style)); // We don't insert backslashes when breaking line comments. ColumnLimit = Style.ColumnLimit; } else { Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -702,7 +702,10 @@ if (CurrentToken && CurrentToken->is(tok::less)) { next(); while (CurrentToken) { - if (CurrentToken->isNot(tok::comment) || CurrentToken->Next) + // Mark tokens up to the trailing line comments as implicit string + // literals. + if (CurrentToken->isNot(tok::comment) && + !CurrentToken->TokenText.startswith("//")) CurrentToken->Type = TT_ImplicitStringLiteral; next(); } Index: lib/Format/UnwrappedLineParser.cpp =================================================================== --- lib/Format/UnwrappedLineParser.cpp +++ lib/Format/UnwrappedLineParser.cpp @@ -55,13 +55,18 @@ std::vector &Stack; }; +static bool isLineComment(const FormatToken &FormatTok) { + return FormatTok.is(tok::comment) && + FormatTok.TokenText.startswith("//"); +} + class ScopedMacroState : public FormatTokenSource { public: ScopedMacroState(UnwrappedLine &Line, FormatTokenSource *&TokenSource, FormatToken *&ResetToken) : Line(Line), TokenSource(TokenSource), ResetToken(ResetToken), PreviousLineLevel(Line.Level), PreviousTokenSource(TokenSource), - Token(nullptr) { + Token(nullptr), PreviousToken(nullptr) { TokenSource = this; Line.Level = 0; Line.InPPDirective = true; @@ -78,6 +83,7 @@ // The \c UnwrappedLineParser guards against this by never calling // \c getNextToken() after it has encountered the first eof token. assert(!eof()); + PreviousToken = Token; Token = PreviousTokenSource->getNextToken(); if (eof()) return getFakeEOF(); @@ -87,12 +93,17 @@ unsigned getPosition() override { return PreviousTokenSource->getPosition(); } FormatToken *setPosition(unsigned Position) override { + PreviousToken = nullptr; Token = PreviousTokenSource->setPosition(Position); return Token; } private: - bool eof() { return Token && Token->HasUnescapedNewline; } + bool eof() { + return Token && Token->HasUnescapedNewline && + !(PreviousToken && isLineComment(*PreviousToken) && + isLineComment(*Token) && Token->NewlinesBefore == 1); + } FormatToken *getFakeEOF() { static bool EOFInitialized = false; @@ -112,6 +123,7 @@ FormatTokenSource *PreviousTokenSource; FormatToken *Token; + FormatToken *PreviousToken = nullptr; }; } // end anonymous namespace @@ -2067,11 +2079,6 @@ FormatTok.NewlinesBefore > 0; } -static bool isLineComment(const FormatToken &FormatTok) { - return FormatTok.is(tok::comment) && - FormatTok.TokenText.startswith("//"); -} - // Checks if \p FormatTok is a line comment that continues the line comment // section on \p Line. static bool continuesLineComment(const FormatToken &FormatTok, Index: unittests/Format/FormatTestSelective.cpp =================================================================== --- unittests/Format/FormatTestSelective.cpp +++ unittests/Format/FormatTestSelective.cpp @@ -530,6 +530,14 @@ 20, 0)); } +TEST_F(FormatTestSelective, KeepsIndentAfterCommentSectionImport) { + std::string Code = "#include // line 1\n" // 23 chars long + " // line 2\n" // 23 chars long + "\n" // this newline is char 47 + "int i;"; // this line is not indented + EXPECT_EQ(Code, format(Code, 47, 1)); +} + } // end namespace } // end namespace format } // end namespace clang