Index: clang/lib/Format/DefinitionBlockSeparator.cpp =================================================================== --- clang/lib/Format/DefinitionBlockSeparator.cpp +++ clang/lib/Format/DefinitionBlockSeparator.cpp @@ -38,8 +38,7 @@ while (CurrentToken) { if (CurrentToken->isOneOf(tok::kw_class, tok::kw_struct, tok::kw_namespace, tok::kw_enum) || - (Style.Language == FormatStyle::LK_JavaScript && - CurrentToken->TokenText == "function")) + (Style.isJavaScript() && CurrentToken->TokenText == "function")) return true; CurrentToken = CurrentToken->Next; } @@ -56,9 +55,12 @@ : Style.UseCRLF); for (unsigned I = 0; I < Lines.size(); I++) { const auto &CurrentLine = Lines[I]; + if (CurrentLine->InPPDirective) + continue; FormatToken *TargetToken = nullptr; AnnotatedLine *TargetLine; auto OpeningLineIndex = CurrentLine->MatchingOpeningBlockLineIndex; + AnnotatedLine *OpeningLine = nullptr; const auto InsertReplacement = [&](const int NewlineToInsert) { assert(TargetLine); assert(TargetToken); @@ -72,9 +74,18 @@ TargetToken->SpacesRequiredBefore - 1, TargetToken->StartsColumn); }; + const auto IsPPConditional = [&](const size_t LineIndex) { + const auto &Line = Lines[LineIndex]; + return Line->First->is(tok::hash) && + Line->First->Next->isOneOf(tok::pp_if, tok::pp_ifdef, tok::pp_else, + tok::pp_ifndef, tok::pp_elifndef, + tok::pp_elifdef, tok::pp_elif, + tok::pp_endif); + }; const auto FollowingOtherOpening = [&]() { return OpeningLineIndex == 0 || - Lines[OpeningLineIndex - 1]->Last->opensScope(); + Lines[OpeningLineIndex - 1]->Last->opensScope() || + IsPPConditional(OpeningLineIndex - 1); }; const auto HasEnumOnLine = [CurrentLine]() { FormatToken *CurrentToken = CurrentLine->First; @@ -87,17 +98,29 @@ }; bool IsDefBlock = 0; + const auto MayPrecedeDefinition = [&](const int Direction = -1) { + const size_t OperateIndex = OpeningLineIndex + Direction; + assert(OperateIndex < Lines.size()); + const auto &OperateLine = Lines[OperateIndex]; + return (Style.isCSharp() && OperateLine->First->is(TT_AttributeSquare)) || + OperateLine->First->is(tok::comment); + }; if (HasEnumOnLine()) { // We have no scope opening/closing information for enum. IsDefBlock = 1; OpeningLineIndex = I; - TargetLine = CurrentLine; - TargetToken = CurrentLine->First; + while (OpeningLineIndex > 0 && MayPrecedeDefinition()) + OpeningLineIndex--; + OpeningLine = Lines[OpeningLineIndex]; + TargetLine = OpeningLine; + TargetToken = TargetLine->First; if (!FollowingOtherOpening()) InsertReplacement(NewlineCount); else InsertReplacement(OpeningLineIndex != 0); + TargetLine = CurrentLine; + TargetToken = TargetLine->First; while (TargetToken && !TargetToken->is(tok::r_brace)) TargetToken = TargetToken->Next; if (!TargetToken) { @@ -108,22 +131,19 @@ if (OpeningLineIndex > Lines.size()) continue; // Handling the case that opening bracket has its own line. - OpeningLineIndex -= Lines[OpeningLineIndex]->First->TokenText == "{"; - AnnotatedLine *OpeningLine = Lines[OpeningLineIndex]; + OpeningLineIndex -= Lines[OpeningLineIndex]->First->is(tok::l_brace); + OpeningLine = Lines[OpeningLineIndex]; // Closing a function definition. if (LikelyDefinition(OpeningLine)) { IsDefBlock = 1; - if (OpeningLineIndex > 0) { - OpeningLineIndex -= - Style.Language == FormatStyle::LK_CSharp && - Lines[OpeningLineIndex - 1]->First->is(tok::l_square); - OpeningLine = Lines[OpeningLineIndex]; - } + while (OpeningLineIndex > 0 && MayPrecedeDefinition()) + OpeningLineIndex--; + OpeningLine = Lines[OpeningLineIndex]; TargetLine = OpeningLine; TargetToken = TargetLine->First; if (!FollowingOtherOpening()) { // Avoid duplicated replacement. - if (!TargetToken->opensScope()) + if (TargetToken->isNot(tok::l_brace)) InsertReplacement(NewlineCount); } else InsertReplacement(OpeningLineIndex != 0); @@ -132,16 +152,25 @@ // Not the last token. if (IsDefBlock && I + 1 < Lines.size()) { - TargetLine = Lines[I + 1]; + OpeningLineIndex = I + 1; + TargetLine = Lines[OpeningLineIndex]; TargetToken = TargetLine->First; // No empty line for continuously closing scopes. The token will be // handled in another case if the line following is opening a // definition. - if (!TargetToken->closesScope()) { - if (!LikelyDefinition(TargetLine)) + if (!TargetToken->closesScope() && !IsPPConditional(OpeningLineIndex)) { + while (OpeningLineIndex + 1 < Lines.size() && MayPrecedeDefinition(0)) + OpeningLineIndex++; + TargetLine = Lines[OpeningLineIndex]; + if (!LikelyDefinition(TargetLine)) { + TargetLine = Lines[I + 1]; + TargetToken = TargetLine->First; InsertReplacement(NewlineCount); + } } else { + TargetLine = Lines[I + 1]; + TargetToken = TargetLine->First; InsertReplacement(OpeningLineIndex != 0); } } Index: clang/unittests/Format/DefinitionBlockSeparatorTest.cpp =================================================================== --- clang/unittests/Format/DefinitionBlockSeparatorTest.cpp +++ clang/unittests/Format/DefinitionBlockSeparatorTest.cpp @@ -133,21 +133,43 @@ FormatStyle Style = getLLVMStyle(); Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; std::string Prefix = "namespace {\n"; - std::string Postfix = "enum Foo { FOO, BAR };\n" + std::string Postfix = "// Enum test1\n" + "// Enum test2\n" + "enum Foo { FOO, BAR };\n" "\n" + "/*\n" + "test1\n" + "test2\n" + "*/\n" "int foo(int i, int j) {\n" " int r = i + j;\n" " return r;\n" "}\n" "\n" + "// Foobar\n" "int i, j, k;\n" "\n" + "// Comment for function\n" + "// Comment line 2\n" + "// Comment line 3\n" "int bar(int j, int k) {\n" " int r = j * k;\n" " return r;\n" "}\n" "\n" + "int bar2(int j, int k) {\n" + " int r = j / k;\n" + " return r;\n" + "}\n" + "\n" + "/* Comment block in one line*/\n" "enum Bar { FOOBAR, BARFOO };\n" + "\n" + "int bar3(int j, int k) {\n" + " // A comment\n" + " int r = j % k;\n" + " return r;\n" + "}\n" "} // namespace"; verifyFormat(Prefix + "\n\n\n" + removeEmptyLines(Postfix), Style, Prefix + Postfix); @@ -157,21 +179,43 @@ FormatStyle Style = getLLVMStyle(); Style.SeparateDefinitionBlocks = FormatStyle::SDS_Never; std::string Prefix = "namespace {\n"; - std::string Postfix = "enum Foo { FOO, BAR };\n" + std::string Postfix = "// Enum test1\n" + "// Enum test2\n" + "enum Foo { FOO, BAR };\n" "\n" + "/*\n" + "test1\n" + "test2\n" + "*/\n" "int foo(int i, int j) {\n" " int r = i + j;\n" " return r;\n" "}\n" "\n" + "// Foobar\n" "int i, j, k;\n" "\n" + "// Comment for function\n" + "// Comment line 2\n" + "// Comment line 3\n" "int bar(int j, int k) {\n" " int r = j * k;\n" " return r;\n" "}\n" "\n" + "int bar2(int j, int k) {\n" + " int r = j / k;\n" + " return r;\n" + "}\n" + "\n" + "/* Comment block in one line*/\n" "enum Bar { FOOBAR, BARFOO };\n" + "\n" + "int bar3(int j, int k) {\n" + " // A comment\n" + " int r = j % k;\n" + " return r;\n" + "}\n" "} // namespace"; verifyFormat(Prefix + "\n\n\n" + Postfix, Style, Prefix + removeEmptyLines(Postfix)); @@ -181,31 +225,54 @@ FormatStyle Style = getLLVMStyle(); Style.BreakBeforeBraces = FormatStyle::BS_Allman; Style.SeparateDefinitionBlocks = FormatStyle::SDS_Always; - verifyFormat("enum Foo\n" + verifyFormat("// Enum test1\n" + "// Enum test2\n" + "enum Foo\n" "{\n" " FOO,\n" " BAR\n" "};\n" "\n" + "/*\n" + "test1\n" + "test2\n" + "*/\n" "int foo(int i, int j)\n" "{\n" " int r = i + j;\n" " return r;\n" "}\n" "\n" + "// Foobar\n" "int i, j, k;\n" "\n" + "// Comment for function\n" + "// Comment line 2\n" + "// Comment line 3\n" "int bar(int j, int k)\n" "{\n" " int r = j * k;\n" " return r;\n" "}\n" "\n" + "int bar2(int j, int k)\n" + "{\n" + " int r = j / k;\n" + " return r;\n" + "}\n" + "\n" "enum Bar\n" "{\n" " FOOBAR,\n" " BARFOO\n" - "};", + "};\n" + "\n" + "int bar3(int j, int k)\n" + "{\n" + " // A comment\n" + " int r = j % k;\n" + " return r;\n" + "}", Style); } @@ -215,21 +282,42 @@ Style.MaxEmptyLinesToKeep = 3; std::string LeaveAs = "namespace {\n" "\n" + "// Enum test1\n" + "// Enum test2\n" "enum Foo { FOO, BAR };\n" "\n\n\n" + "/*\n" + "test1\n" + "test2\n" + "*/\n" "int foo(int i, int j) {\n" " int r = i + j;\n" " return r;\n" "}\n" "\n" + "// Foobar\n" "int i, j, k;\n" "\n" + "// Comment for function\n" + "// Comment line 2\n" + "// Comment line 3\n" "int bar(int j, int k) {\n" " int r = j * k;\n" " return r;\n" "}\n" "\n" + "int bar2(int j, int k) {\n" + " int r = j / k;\n" + " return r;\n" + "}\n" + "\n" + "// Comment for inline enum\n" "enum Bar { FOOBAR, BARFOO };\n" + "int bar3(int j, int k) {\n" + " // A comment\n" + " int r = j % k;\n" + " return r;\n" + "}\n" "} // namespace"; verifyFormat(LeaveAs, Style, LeaveAs); } @@ -251,6 +339,7 @@ "internal static String toString() {\r\n" "}\r\n" "\r\n" + "// Comment for enum\r\n" "public enum var {\r\n" " none,\r\n" " @string,\r\n" @@ -258,6 +347,7 @@ " @enum\r\n" "}\r\n" "\r\n" + "// Test\r\n" "[STAThread]\r\n" "static void Main(string[] args) {\r\n" " Console.WriteLine(\"HelloWorld\");\r\n"