Index: clang/docs/ClangFormatStyleOptions.rst =================================================================== --- clang/docs/ClangFormatStyleOptions.rst +++ clang/docs/ClangFormatStyleOptions.rst @@ -1408,12 +1408,22 @@ #if FOO # if BAR # include - # endif - #endif - - - -**IndentWidth** (``unsigned``) + # endif + #endif + + * ``PPDIS_BeforeHash`` (in configuration: ``BeforeHash``) + Indents directives before the hash. + + .. code-block:: c++ + + #if FOO + #if BAR + #include + #endif + #endif + + +**IndentWidth** (``unsigned``) The number of columns to use for indentation. .. code-block:: c++ Index: clang/docs/ReleaseNotes.rst =================================================================== --- clang/docs/ReleaseNotes.rst +++ clang/docs/ReleaseNotes.rst @@ -170,8 +170,8 @@ clang-format ------------ - -- ... +- Added new option `PPDIS_BeforeHash` (in configuration: `BeforeHash`) to + `IndentPPDirectives` which indents preprocessor directives before the hash. libclang -------- Index: clang/include/clang/Format/Format.h =================================================================== --- clang/include/clang/Format/Format.h +++ clang/include/clang/Format/Format.h @@ -1123,13 +1123,22 @@ /// #if FOO /// # if BAR /// # include - /// # endif - /// #endif - /// \endcode - PPDIS_AfterHash - }; - - /// The preprocessor directive indenting style to use. + /// # endif + /// #endif + /// \endcode + PPDIS_AfterHash, + /// Indents directives before the hash. + /// \code + /// #if FOO + /// #if BAR + /// #include + /// #endif + /// #endif + /// \endcode + PPDIS_BeforeHash + }; + + /// The preprocessor directive indenting style to use. PPDirectiveIndentStyle IndentPPDirectives; /// The number of columns to use for indentation. Index: clang/lib/Format/Format.cpp =================================================================== --- clang/lib/Format/Format.cpp +++ clang/lib/Format/Format.cpp @@ -171,12 +171,13 @@ template <> struct ScalarEnumerationTraits { - static void enumeration(IO &IO, FormatStyle::PPDirectiveIndentStyle &Value) { - IO.enumCase(Value, "None", FormatStyle::PPDIS_None); - IO.enumCase(Value, "AfterHash", FormatStyle::PPDIS_AfterHash); - } -}; - + static void enumeration(IO &IO, FormatStyle::PPDirectiveIndentStyle &Value) { + IO.enumCase(Value, "None", FormatStyle::PPDIS_None); + IO.enumCase(Value, "AfterHash", FormatStyle::PPDIS_AfterHash); + IO.enumCase(Value, "BeforeHash", FormatStyle::PPDIS_BeforeHash); + } +}; + template <> struct ScalarEnumerationTraits { static void enumeration(IO &IO, FormatStyle::ReturnTypeBreakingStyle &Value) { Index: clang/lib/Format/TokenAnnotator.cpp =================================================================== --- clang/lib/Format/TokenAnnotator.cpp +++ clang/lib/Format/TokenAnnotator.cpp @@ -1918,18 +1918,21 @@ // If the comment is currently aligned with the line immediately following // it, that's probably intentional and we should keep it. if (NextNonCommentLine && CommentLine && - NextNonCommentLine->First->NewlinesBefore <= 1 && - NextNonCommentLine->First->OriginalColumn == - (*I)->First->OriginalColumn) { - // Align comments for preprocessor lines with the # in column 0. - // Otherwise, align with the next line. - (*I)->Level = (NextNonCommentLine->Type == LT_PreprocessorDirective || - NextNonCommentLine->Type == LT_ImportStatement) - ? 0 - : NextNonCommentLine->Level; - } else { - NextNonCommentLine = (*I)->First->isNot(tok::r_brace) ? (*I) : nullptr; - } + NextNonCommentLine->First->NewlinesBefore <= 1 && + NextNonCommentLine->First->OriginalColumn == + (*I)->First->OriginalColumn) { + // Align comments for preprocessor lines with the # in column 0 if + // preprocessor lines are not indented. Otherwise, align with the next + // line. + (*I)->Level = + (Style.IndentPPDirectives != FormatStyle::PPDIS_BeforeHash && + (NextNonCommentLine->Type == LT_PreprocessorDirective || + NextNonCommentLine->Type == LT_ImportStatement)) + ? 0 + : NextNonCommentLine->Level; + } else { + NextNonCommentLine = (*I)->First->isNot(tok::r_brace) ? (*I) : nullptr; + } setCommentLineLevels((*I)->Children); } Index: clang/lib/Format/UnwrappedLineFormatter.cpp =================================================================== --- clang/lib/Format/UnwrappedLineFormatter.cpp +++ clang/lib/Format/UnwrappedLineFormatter.cpp @@ -1175,14 +1175,16 @@ (!PreviousLine->InPPDirective || !RootToken.HasUnescapedNewline)) Newlines = std::min(1u, Newlines); - if (Newlines) - Indent = NewlineIndent; - - // Preprocessor directives get indented after the hash, if indented. - if (Line.Type == LT_PreprocessorDirective || Line.Type == LT_ImportStatement) - Indent = 0; - - Whitespaces->replaceWhitespace(RootToken, Newlines, Indent, Indent, + if (Newlines) + Indent = NewlineIndent; + + // Preprocessor directives get indented before the hash only if specified + if (Style.IndentPPDirectives != FormatStyle::PPDIS_BeforeHash && + (Line.Type == LT_PreprocessorDirective || + Line.Type == LT_ImportStatement)) + Indent = 0; + + Whitespaces->replaceWhitespace(RootToken, Newlines, Indent, Indent, Line.InPPDirective && !RootToken.HasUnescapedNewline); } Index: clang/lib/Format/UnwrappedLineParser.cpp =================================================================== --- clang/lib/Format/UnwrappedLineParser.cpp +++ clang/lib/Format/UnwrappedLineParser.cpp @@ -652,12 +652,13 @@ nextToken(); } -void UnwrappedLineParser::parsePPDirective() { - assert(FormatTok->Tok.is(tok::hash) && "'#' expected"); - ScopedMacroState MacroState(*Line, Tokens, FormatTok); - nextToken(); - - if (!FormatTok->Tok.getIdentifierInfo()) { +void UnwrappedLineParser::parsePPDirective() { + assert(FormatTok->Tok.is(tok::hash) && "'#' expected"); + ScopedMacroState MacroState(*Line, Tokens, FormatTok); + + nextToken(); + + if (!FormatTok->Tok.getIdentifierInfo()) { parsePPUnknown(); return; } @@ -820,13 +821,13 @@ nextToken(); if (FormatTok->Tok.getKind() == tok::l_paren && FormatTok->WhitespaceRange.getBegin() == - FormatTok->WhitespaceRange.getEnd()) { - parseParens(); - } - if (Style.IndentPPDirectives == FormatStyle::PPDIS_AfterHash) - Line->Level += PPBranchLevel + 1; - addUnwrappedLine(); - ++Line->Level; + FormatTok->WhitespaceRange.getEnd()) { + parseParens(); + } + if (Style.IndentPPDirectives != FormatStyle::PPDIS_None) + Line->Level += PPBranchLevel + 1; + addUnwrappedLine(); + ++Line->Level; // Errors during a preprocessor directive can only affect the layout of the // preprocessor directive, and thus we ignore them. An alternative approach @@ -837,13 +838,13 @@ } void UnwrappedLineParser::parsePPUnknown() { - do { - nextToken(); - } while (!eof()); - if (Style.IndentPPDirectives == FormatStyle::PPDIS_AfterHash) - Line->Level += PPBranchLevel + 1; - addUnwrappedLine(); -} + do { + nextToken(); + } while (!eof()); + if (Style.IndentPPDirectives != FormatStyle::PPDIS_None) + Line->Level += PPBranchLevel + 1; + addUnwrappedLine(); +} // Here we blacklist certain tokens that are not usually the first token in an // unwrapped line. This is used in attempt to distinguish macro calls without @@ -2668,12 +2669,15 @@ static_cast(-LevelDifference) <= Line->Level) && "LevelDifference makes Line->Level negative"); Line->Level += LevelDifference; - // Comments stored before the preprocessor directive need to be output - // before the preprocessor directive, at the same level as the - // preprocessor directive, as we consider them to apply to the directive. - flushComments(isOnNewLine(*FormatTok)); - parsePPDirective(); - } + // Comments stored before the preprocessor directive need to be output + // before the preprocessor directive, at the same level as the + // preprocessor directive, as we consider them to apply to the directive. + if (Style.IndentPPDirectives == FormatStyle::PPDIS_BeforeHash && + PPBranchLevel > 0) + Line->Level += PPBranchLevel; + flushComments(isOnNewLine(*FormatTok)); + parsePPDirective(); + } while (FormatTok->Type == TT_ConflictStart || FormatTok->Type == TT_ConflictEnd || FormatTok->Type == TT_ConflictAlternative) { Index: clang/unittests/Format/FormatTest.cpp =================================================================== --- clang/unittests/Format/FormatTest.cpp +++ clang/unittests/Format/FormatTest.cpp @@ -2993,22 +2993,25 @@ EXPECT_EQ(Expected, format(ToFormat, Style)); EXPECT_EQ(Expected, format(Expected, Style)); } - // Test with tabs. - Style.UseTab = FormatStyle::UT_Always; - Style.IndentWidth = 8; - Style.TabWidth = 8; - verifyFormat("#ifdef _WIN32\n" - "#\tdefine A 0\n" - "#\tifdef VAR2\n" - "#\t\tdefine B 1\n" - "#\t\tinclude \n" - "#\t\tdefine MACRO \\\n" - "\t\t\tsome_very_long_func_aaaaaaaaaa();\n" - "#\tendif\n" - "#else\n" - "#\tdefine A 1\n" - "#endif", - Style); + // Test AfterHash with tabs. + { + FormatStyle Tabbed = Style; + Tabbed.UseTab = FormatStyle::UT_Always; + Tabbed.IndentWidth = 8; + Tabbed.TabWidth = 8; + verifyFormat("#ifdef _WIN32\n" + "#\tdefine A 0\n" + "#\tifdef VAR2\n" + "#\t\tdefine B 1\n" + "#\t\tinclude \n" + "#\t\tdefine MACRO \\\n" + "\t\t\tsome_very_long_func_aaaaaaaaaa();\n" + "#\tendif\n" + "#else\n" + "#\tdefine A 1\n" + "#endif", + Tabbed); + } // Regression test: Multiline-macro inside include guards. verifyFormat("#ifndef HEADER_H\n" @@ -3018,6 +3021,102 @@ " int j;\n" "#endif // HEADER_H", getLLVMStyleWithColumns(20)); + + Style.IndentPPDirectives = FormatStyle::PPDIS_BeforeHash; + // Basic before hash indent tests + verifyFormat("#ifdef _WIN32\n" + " #define A 0\n" + " #ifdef VAR2\n" + " #define B 1\n" + " #include \n" + " #define MACRO \\\n" + " some_very_long_func_aaaaaaaaaa();\n" + " #endif\n" + "#else\n" + " #define A 1\n" + "#endif", + Style); + verifyFormat("#if A\n" + " #define MACRO \\\n" + " void a(int x) { \\\n" + " b(); \\\n" + " c(); \\\n" + " d(); \\\n" + " e(); \\\n" + " f(); \\\n" + " }\n" + "#endif", + Style); + // Keep comments aligned with indented directives. These + // tests cannot use verifyFormat because messUp manipulates leading + // whitespace. + { + const char *Expected = "void f() {\n" + "// Aligned to preprocessor.\n" + "#if 1\n" + " // Aligned to code.\n" + " int a;\n" + " #if 1\n" + " // Aligned to preprocessor.\n" + " #define A 0\n" + " // Aligned to code.\n" + " int b;\n" + " #endif\n" + "#endif\n" + "}"; + const char *ToFormat = "void f() {\n" + "// Aligned to preprocessor.\n" + "#if 1\n" + "// Aligned to code.\n" + "int a;\n" + "#if 1\n" + "// Aligned to preprocessor.\n" + "#define A 0\n" + "// Aligned to code.\n" + "int b;\n" + "#endif\n" + "#endif\n" + "}"; + EXPECT_EQ(Expected, format(ToFormat, Style)); + EXPECT_EQ(Expected, format(Expected, Style)); + } + { + const char *Expected = "void f() {\n" + "/* Aligned to preprocessor. */\n" + "#if 1\n" + " /* Aligned to code. */\n" + " int a;\n" + " #if 1\n" + " /* Aligned to preprocessor. */\n" + " #define A 0\n" + " /* Aligned to code. */\n" + " int b;\n" + " #endif\n" + "#endif\n" + "}"; + const char *ToFormat = "void f() {\n" + "/* Aligned to preprocessor. */\n" + "#if 1\n" + "/* Aligned to code. */\n" + "int a;\n" + "#if 1\n" + "/* Aligned to preprocessor. */\n" + "#define A 0\n" + "/* Aligned to code. */\n" + "int b;\n" + "#endif\n" + "#endif\n" + "}"; + EXPECT_EQ(Expected, format(ToFormat, Style)); + EXPECT_EQ(Expected, format(Expected, Style)); + } + + // Test single comment before preprocessor + verifyFormat("// Comment\n" + "\n" + "#if 1\n" + "#endif", + Style); } TEST_F(FormatTest, FormatHashIfNotAtStartOfLine) {