diff --git a/clang/lib/Format/UnwrappedLineFormatter.h b/clang/lib/Format/UnwrappedLineFormatter.h --- a/clang/lib/Format/UnwrappedLineFormatter.h +++ b/clang/lib/Format/UnwrappedLineFormatter.h @@ -47,6 +47,7 @@ /// of the \c UnwrappedLine if there was no structural parsing error. void formatFirstToken(const AnnotatedLine &Line, const AnnotatedLine *PreviousLine, + const AnnotatedLine *PrevPrevLine, const SmallVectorImpl &Lines, unsigned Indent, unsigned NewlineIndent); diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -1122,6 +1122,7 @@ unsigned Penalty = 0; LevelIndentTracker IndentTracker(Style, Keywords, Lines[0]->Level, AdditionalIndent); + const AnnotatedLine *PrevPrevLine = nullptr; const AnnotatedLine *PreviousLine = nullptr; const AnnotatedLine *NextLine = nullptr; @@ -1160,7 +1161,7 @@ if (ShouldFormat && TheLine.Type != LT_Invalid) { if (!DryRun) { bool LastLine = Line->First->is(tok::eof); - formatFirstToken(TheLine, PreviousLine, Lines, Indent, + formatFirstToken(TheLine, PreviousLine, PrevPrevLine, Lines, Indent, LastLine ? LastStartColumn : NextStartColumn + Indent); } @@ -1206,7 +1207,7 @@ TheLine.LeadingEmptyLinesAffected); // Format the first token. if (ReformatLeadingWhitespace) - formatFirstToken(TheLine, PreviousLine, Lines, + formatFirstToken(TheLine, PreviousLine, PrevPrevLine, Lines, TheLine.First->OriginalColumn, TheLine.First->OriginalColumn); else @@ -1222,6 +1223,7 @@ } if (!DryRun) markFinalized(TheLine.First); + PrevPrevLine = PreviousLine; PreviousLine = &TheLine; } PenaltyCache[CacheKey] = Penalty; @@ -1230,6 +1232,7 @@ void UnwrappedLineFormatter::formatFirstToken( const AnnotatedLine &Line, const AnnotatedLine *PreviousLine, + const AnnotatedLine *PrevPrevLine, const SmallVectorImpl &Lines, unsigned Indent, unsigned NewlineIndent) { FormatToken &RootToken = *Line.First; @@ -1261,6 +1264,8 @@ if (!Style.KeepEmptyLinesAtTheStartOfBlocks && PreviousLine && PreviousLine->Last->is(tok::l_brace) && !PreviousLine->startsWithNamespace() && + !(PrevPrevLine && PrevPrevLine->startsWithNamespace() && + PreviousLine->startsWith(tok::l_brace)) && !startsExternCBlock(*PreviousLine)) Newlines = 1; diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -262,6 +262,89 @@ "}", getGoogleStyle())); + auto CustomStyle = clang::format::getLLVMStyle(); + CustomStyle.BreakBeforeBraces = clang::format::FormatStyle::BS_Custom; + CustomStyle.BraceWrapping.AfterNamespace = true; + CustomStyle.KeepEmptyLinesAtTheStartOfBlocks = false; + EXPECT_EQ("namespace N\n" + "{\n" + "\n" + "int i;\n" + "}", + format("namespace N\n" + "{\n" + "\n" + "\n" + "int i;\n" + "}", + CustomStyle)); + EXPECT_EQ("/* something */ namespace N\n" + "{\n" + "\n" + "int i;\n" + "}", + format("/* something */ namespace N {\n" + "\n" + "\n" + "int i;\n" + "}", + CustomStyle)); + EXPECT_EQ("inline namespace N\n" + "{\n" + "\n" + "int i;\n" + "}", + format("inline namespace N\n" + "{\n" + "\n" + "\n" + "int i;\n" + "}", + CustomStyle)); + EXPECT_EQ("/* something */ inline namespace N\n" + "{\n" + "\n" + "int i;\n" + "}", + format("/* something */ inline namespace N\n" + "{\n" + "\n" + "int i;\n" + "}", + CustomStyle)); + EXPECT_EQ("export namespace N\n" + "{\n" + "\n" + "int i;\n" + "}", + format("export namespace N\n" + "{\n" + "\n" + "int i;\n" + "}", + CustomStyle)); + EXPECT_EQ("namespace a\n" + "{\n" + "namespace b\n" + "{\n" + "\n" + "class AA {};\n" + "\n" + "} // namespace b\n" + "} // namespace a\n", + format("namespace a\n" + "{\n" + "namespace b\n" + "{\n" + "\n" + "\n" + "class AA {};\n" + "\n" + "\n" + "}\n" + "}\n", + CustomStyle)); + // ...but do keep inlining and removing empty lines for non-block extern "C" // functions. verifyFormat("extern \"C\" int f() { return 42; }", getGoogleStyle());