Index: cfe/trunk/lib/Format/NamespaceEndCommentsFixer.h =================================================================== --- cfe/trunk/lib/Format/NamespaceEndCommentsFixer.h +++ cfe/trunk/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); Index: cfe/trunk/lib/Format/NamespaceEndCommentsFixer.cpp =================================================================== --- cfe/trunk/lib/Format/NamespaceEndCommentsFixer.cpp +++ cfe/trunk/lib/Format/NamespaceEndCommentsFixer.cpp @@ -107,6 +107,7 @@ << llvm::toString(std::move(Err)) << "\n"; } } +} // namespace const FormatToken * getNamespaceToken(const AnnotatedLine *line, @@ -131,7 +132,6 @@ return nullptr; return NamespaceTok; } -} // namespace NamespaceEndCommentsFixer::NamespaceEndCommentsFixer(const Environment &Env, const FormatStyle &Style) Index: cfe/trunk/lib/Format/UnwrappedLineFormatter.h =================================================================== --- cfe/trunk/lib/Format/UnwrappedLineFormatter.h +++ cfe/trunk/lib/Format/UnwrappedLineFormatter.h @@ -49,8 +49,9 @@ /// \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. Index: cfe/trunk/lib/Format/UnwrappedLineFormatter.cpp =================================================================== --- cfe/trunk/lib/Format/UnwrappedLineFormatter.cpp +++ cfe/trunk/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 @@ 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 @@ 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 @@ 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 @@ // 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) Index: cfe/trunk/unittests/Format/FormatTest.cpp =================================================================== --- cfe/trunk/unittests/Format/FormatTest.cpp +++ cfe/trunk/unittests/Format/FormatTest.cpp @@ -278,11 +278,12 @@ "\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" @@ -293,6 +294,27 @@ "}", 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" "}")); EXPECT_EQ("namespace {\n"