diff --git a/clang/lib/Format/NamespaceEndCommentsFixer.cpp b/clang/lib/Format/NamespaceEndCommentsFixer.cpp index ee11959af3a9..f5832a443fd8 100644 --- a/clang/lib/Format/NamespaceEndCommentsFixer.cpp +++ b/clang/lib/Format/NamespaceEndCommentsFixer.cpp @@ -107,6 +107,7 @@ void updateEndComment(const FormatToken *RBraceTok, StringRef EndCommentText, << llvm::toString(std::move(Err)) << "\n"; } } +} // namespace const FormatToken * getNamespaceToken(const AnnotatedLine *line, @@ -131,7 +132,6 @@ getNamespaceToken(const AnnotatedLine *line, return nullptr; return NamespaceTok; } -} // namespace NamespaceEndCommentsFixer::NamespaceEndCommentsFixer(const Environment &Env, const FormatStyle &Style) diff --git a/clang/lib/Format/NamespaceEndCommentsFixer.h b/clang/lib/Format/NamespaceEndCommentsFixer.h index 4779f0d27c92..adfe38943c8c 100644 --- a/clang/lib/Format/NamespaceEndCommentsFixer.h +++ b/clang/lib/Format/NamespaceEndCommentsFixer.h @@ -21,6 +21,16 @@ namespace clang { namespace format { +// Finds the namespace token corresponding to a closing namespace `}`, if that +// is to be formatted. +// If \p Line contains the closing `}` of a namespace, is affected and is not in +// a preprocessor directive, the result will be the matching namespace token. +// Otherwise returns null. +// \p AnnotatedLines is the sequence of lines from which \p Line is a member of. +const FormatToken * +getNamespaceToken(const AnnotatedLine *Line, + const SmallVectorImpl &AnnotatedLines); + class NamespaceEndCommentsFixer : public TokenAnalyzer { public: NamespaceEndCommentsFixer(const Environment &Env, const FormatStyle &Style); diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 2ce39fb04c6c..953a5d370c54 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -7,6 +7,7 @@ // //===----------------------------------------------------------------------===// +#include "NamespaceEndCommentsFixer.h" #include "UnwrappedLineFormatter.h" #include "WhitespaceManager.h" #include "llvm/Support/Debug.h" @@ -1050,8 +1051,7 @@ UnwrappedLineFormatter::format(const SmallVectorImpl &Lines, if (ShouldFormat && TheLine.Type != LT_Invalid) { if (!DryRun) { bool LastLine = Line->First->is(tok::eof); - formatFirstToken(TheLine, PreviousLine, - Indent, + formatFirstToken(TheLine, PreviousLine, Lines, Indent, LastLine ? LastStartColumn : NextStartColumn + Indent); } @@ -1095,7 +1095,7 @@ UnwrappedLineFormatter::format(const SmallVectorImpl &Lines, TheLine.LeadingEmptyLinesAffected); // Format the first token. if (ReformatLeadingWhitespace) - formatFirstToken(TheLine, PreviousLine, + formatFirstToken(TheLine, PreviousLine, Lines, TheLine.First->OriginalColumn, TheLine.First->OriginalColumn); else @@ -1117,10 +1117,10 @@ UnwrappedLineFormatter::format(const SmallVectorImpl &Lines, return Penalty; } -void UnwrappedLineFormatter::formatFirstToken(const AnnotatedLine &Line, - const AnnotatedLine *PreviousLine, - unsigned Indent, - unsigned NewlineIndent) { +void UnwrappedLineFormatter::formatFirstToken( + const AnnotatedLine &Line, const AnnotatedLine *PreviousLine, + const SmallVectorImpl &Lines, unsigned Indent, + unsigned NewlineIndent) { FormatToken &RootToken = *Line.First; if (RootToken.is(tok::eof)) { unsigned Newlines = std::min(RootToken.NewlinesBefore, 1u); @@ -1134,7 +1134,9 @@ void UnwrappedLineFormatter::formatFirstToken(const AnnotatedLine &Line, // Remove empty lines before "}" where applicable. if (RootToken.is(tok::r_brace) && (!RootToken.Next || - (RootToken.Next->is(tok::semi) && !RootToken.Next->Next))) + (RootToken.Next->is(tok::semi) && !RootToken.Next->Next)) && + // Do not remove empty lines before namespace closing "}". + !getNamespaceToken(&Line, Lines)) Newlines = std::min(Newlines, 1u); // Remove empty lines at the start of nested blocks (lambdas/arrow functions) if (PreviousLine == nullptr && Line.Level > 0) diff --git a/clang/lib/Format/UnwrappedLineFormatter.h b/clang/lib/Format/UnwrappedLineFormatter.h index 6432ca83a4c9..756d99d64095 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.h +++ b/clang/lib/Format/UnwrappedLineFormatter.h @@ -49,8 +49,9 @@ private: /// \brief Add a new line and the required indent before the first Token /// of the \c UnwrappedLine if there was no structural parsing error. void formatFirstToken(const AnnotatedLine &Line, - const AnnotatedLine *PreviousLine, unsigned Indent, - unsigned NewlineIndent); + const AnnotatedLine *PreviousLine, + const SmallVectorImpl &Lines, + unsigned Indent, unsigned NewlineIndent); /// \brief Returns the column limit for a line, taking into account whether we /// need an escaped newline due to a continued preprocessor directive. diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 4397cdfe6927..ddaa5cae4630 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -278,11 +278,12 @@ TEST_F(FormatTest, RemovesEmptyLines) { "\n" "}")); - // FIXME: This is slightly inconsistent. + // Don't remove empty lines before namespace endings. FormatStyle LLVMWithNoNamespaceFix = getLLVMStyle(); LLVMWithNoNamespaceFix.FixNamespaceComments = false; EXPECT_EQ("namespace {\n" "int i;\n" + "\n" "}", format("namespace {\n" "int i;\n" @@ -291,6 +292,27 @@ TEST_F(FormatTest, RemovesEmptyLines) { EXPECT_EQ("namespace {\n" "int i;\n" "}", + format("namespace {\n" + "int i;\n" + "}", LLVMWithNoNamespaceFix)); + EXPECT_EQ("namespace {\n" + "int i;\n" + "\n" + "};", + format("namespace {\n" + "int i;\n" + "\n" + "};", LLVMWithNoNamespaceFix)); + EXPECT_EQ("namespace {\n" + "int i;\n" + "};", + format("namespace {\n" + "int i;\n" + "};", LLVMWithNoNamespaceFix)); + EXPECT_EQ("namespace {\n" + "int i;\n" + "\n" + "}", format("namespace {\n" "int i;\n" "\n"