Index: clang/docs/ClangFormatStyleOptions.rst =================================================================== --- clang/docs/ClangFormatStyleOptions.rst +++ clang/docs/ClangFormatStyleOptions.rst @@ -931,19 +931,45 @@ -**AllowShortCaseLabelsOnASingleLine** (``Boolean``) :versionbadge:`clang-format 3.6` - If ``true``, short case labels will be contracted to a single line. +**AllowShortCaseLabelsOnASingleLine** (``ShortCaseLabelStyle``) :versionbadge:`clang-format 3.6` + Determine if short case labels will be contracted to a single line. + + Possible values: + + * ``SCLS_Never`` (in configuration: ``Never``) + Never merge case code + + * ``SCLS_NoFallThrough`` (in configuration: ``NoFallThrough``) + Always contract 'simple' small case label code but + ignore cases where fallthrough occurs + (implicit or marked fallthrough) + + .. code-block:: c++ + + case 0: + case 1: + x = 1; + break; + case 2: x = 2; break; + case 3: + x = 3; + [[fallthrough]]; + case 4: + x = 4; + break; + + * ``SCLS_Always`` (in configuration: ``Always``) + Always contract short case labels to a single line. + + .. code-block:: c++ + + case 0: + case 1: x = 1; break; + case 2: x = 2; break; + case 3: x = 3; [[fallthrough]]; + case 4: x = 4; break; - .. code-block:: c++ - true: false: - switch (a) { vs. switch (a) { - case 1: x = 1; break; case 1: - case 2: return; x = 1; - } break; - case 2: - return; - } **AllowShortEnumsOnASingleLine** (``Boolean``) :versionbadge:`clang-format 11` Allow short enums on a single line. Index: clang/include/clang/Format/Format.h =================================================================== --- clang/include/clang/Format/Format.h +++ clang/include/clang/Format/Format.h @@ -465,19 +465,41 @@ /// \version 3.5 ShortBlockStyle AllowShortBlocksOnASingleLine; - /// If ``true``, short case labels will be contracted to a single line. - /// \code - /// true: false: - /// switch (a) { vs. switch (a) { - /// case 1: x = 1; break; case 1: - /// case 2: return; x = 1; - /// } break; - /// case 2: - /// return; - /// } - /// \endcode + /// Different styles for merging short case labels. + enum ShortCaseLabelStyle : int8_t { + /// Never merge case code + SCLS_Never, + /// Always contract 'simple' small case label code but + /// ignore cases where fallthrough occurs + /// (implicit or marked fallthrough) + /// \code + /// case 0: + /// case 1: + /// x = 1; + /// break; + /// case 2: x = 2; break; + /// case 3: + /// x = 3; + /// [[fallthrough]]; + /// case 4: + /// x = 4; + /// break; + /// \endcode + SCLS_NoFallThrough, + /// Always contract short case labels to a single line. + /// \code + /// case 0: + /// case 1: x = 1; break; + /// case 2: x = 2; break; + /// case 3: x = 3; [[fallthrough]]; + /// case 4: x = 4; break; + /// \endcode + SCLS_Always + }; + + /// Determine if short case labels will be contracted to a single line. /// \version 3.6 - bool AllowShortCaseLabelsOnASingleLine; + ShortCaseLabelStyle AllowShortCaseLabelsOnASingleLine; /// Different styles for merging short functions containing at most one /// statement. Index: clang/lib/Format/Format.cpp =================================================================== --- clang/lib/Format/Format.cpp +++ clang/lib/Format/Format.cpp @@ -591,6 +591,18 @@ } }; +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &IO, FormatStyle::ShortCaseLabelStyle &Value) { + IO.enumCase(Value, "Never", FormatStyle::SCLS_Never); + IO.enumCase(Value, "Always", FormatStyle::SCLS_Always); + IO.enumCase(Value, "NoFallThrough", FormatStyle::SCLS_NoFallThrough); + + // For backward compatibility. + IO.enumCase(Value, "false", FormatStyle::SCLS_Never); + IO.enumCase(Value, "true", FormatStyle::SCLS_Always); + } +}; + template <> struct MappingTraits { static void mapping(IO &IO, FormatStyle &Style) { // When reading, read the language first, we need it for getPredefinedStyle. @@ -1196,7 +1208,7 @@ LLVMStyle.AllowShortEnumsOnASingleLine = true; LLVMStyle.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_All; LLVMStyle.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Never; - LLVMStyle.AllowShortCaseLabelsOnASingleLine = false; + LLVMStyle.AllowShortCaseLabelsOnASingleLine = FormatStyle::SCLS_Never; LLVMStyle.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never; LLVMStyle.AllowShortLambdasOnASingleLine = FormatStyle::SLS_All; LLVMStyle.AllowShortLoopsOnASingleLine = false; @@ -1650,7 +1662,7 @@ Style.PenaltyReturnTypeOnItsOwnLine = 1000; Style.AllowShortEnumsOnASingleLine = false; Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None; - Style.AllowShortCaseLabelsOnASingleLine = false; + Style.AllowShortCaseLabelsOnASingleLine = FormatStyle::SCLS_Never; Style.AllowShortIfStatementsOnASingleLine = FormatStyle::SIS_Never; Style.AllowShortLoopsOnASingleLine = false; Style.AlwaysBreakAfterDefinitionReturnType = FormatStyle::DRTBS_None; Index: clang/lib/Format/UnwrappedLineFormatter.cpp =================================================================== --- clang/lib/Format/UnwrappedLineFormatter.cpp +++ clang/lib/Format/UnwrappedLineFormatter.cpp @@ -564,7 +564,7 @@ } if (TheLine->First->isOneOf(tok::kw_case, tok::kw_default)) { return Style.AllowShortCaseLabelsOnASingleLine - ? tryMergeShortCaseLabels(I, E, Limit) + ? tryMergeShortCaseLabels(I, E, Limit, PreviousLine, Style) : 0; } if (TheLine->InPPDirective && @@ -632,13 +632,28 @@ unsigned tryMergeShortCaseLabels(SmallVectorImpl::const_iterator I, SmallVectorImpl::const_iterator E, - unsigned Limit) { + unsigned Limit, const AnnotatedLine *PreviousLine, + const FormatStyle &Style) { if (Limit == 0 || I + 1 == E || I[1]->First->isOneOf(tok::kw_case, tok::kw_default)) { return 0; } if (I[0]->Last->is(tok::l_brace) || I[1]->First->is(tok::l_brace)) return 0; + bool NoFallThrough = Style.AllowShortCaseLabelsOnASingleLine == + FormatStyle::SCLS_NoFallThrough; + // Don't merge if the last thing on the line before was just `case X:`. + if (NoFallThrough && PreviousLine && PreviousLine->Last) { + if (PreviousLine->Last->is(tok::colon)) + return 0; + // Don't merge if the last thing on the line before was an attribute. + if (PreviousLine->Last->Previous && PreviousLine->Last->Previous) { + auto *PrevPrev = PreviousLine->Last->Previous->Previous; + if (PrevPrev && PrevPrev->startsSequence(TT_AttributeSquare, + TT_AttributeSquare, tok::semi)) + return 0; + } + } unsigned NumStmts = 0; unsigned Length = 0; bool EndsWithComment = false; @@ -652,6 +667,10 @@ break; if (Line->First->isOneOf(tok::kw_case, tok::kw_default, tok::r_brace)) break; + // When the contents of the case contains an [[attribute]] don't merge it + // could be a fallthough. + if (NoFallThrough && Line->First->is(TT_AttributeSquare)) + return 0; if (Line->First->isOneOf(tok::kw_if, tok::kw_for, tok::kw_switch, tok::kw_while) || EndsWithComment) { Index: clang/unittests/Format/FormatTest.cpp =================================================================== --- clang/unittests/Format/FormatTest.cpp +++ clang/unittests/Format/FormatTest.cpp @@ -2882,7 +2882,7 @@ TEST_F(FormatTest, ShortCaseLabels) { FormatStyle Style = getLLVMStyle(); - Style.AllowShortCaseLabelsOnASingleLine = true; + Style.AllowShortCaseLabelsOnASingleLine = FormatStyle::SCLS_Always; verifyFormat("switch (a) {\n" "case 1: x = 1; break;\n" "case 2: return;\n" @@ -3011,7 +3011,7 @@ "}", Style); Style.ColumnLimit = 80; - Style.AllowShortCaseLabelsOnASingleLine = false; + Style.AllowShortCaseLabelsOnASingleLine = FormatStyle::SCLS_Never; Style.IndentCaseLabels = true; EXPECT_EQ("switch (n) {\n" " default /*comments*/:\n" @@ -3026,7 +3026,7 @@ " return false;\n" "}", Style)); - Style.AllowShortCaseLabelsOnASingleLine = true; + Style.AllowShortCaseLabelsOnASingleLine = FormatStyle::SCLS_Always; Style.BreakBeforeBraces = FormatStyle::BS_Custom; Style.BraceWrapping.AfterCaseLabel = true; Style.BraceWrapping.AfterControlStatement = FormatStyle::BWACS_Always; @@ -3051,6 +3051,55 @@ " }\n" "}", Style)); + + Style.AllowShortCaseLabelsOnASingleLine = FormatStyle::SCLS_NoFallThrough; + verifyFormat("switch (a)\n" + "{\n" + " case 1: x = 1; break;\n" + " case 2: return;\n" + " case 3:\n" + " case 4:\n" + " case 5:\n" + " return;\n" + " case 6: // comment\n" + " return;\n" + " case 7:\n" + " // comment\n" + " return;\n" + " case 8:\n" + " x = 8; // comment\n" + " break;\n" + " default: y = 1; break;\n" + "}", + Style); + + verifyFormat("switch (a)\n" + "{\n" + " case 0:\n" + " case 1:\n" + " x = 1;\n" + " break;\n" + " case 2: x = 2; break;\n" + " case 3:\n" + " x = 3;\n" + " [[fallthrough]];\n" + " case 4:\n" + " x = 4;\n" + " break;\n" + " default: y = 1; break;\n" + "}", + Style); + Style.AllowShortCaseLabelsOnASingleLine = FormatStyle::SCLS_Always; + verifyFormat("switch (a)\n" + "{\n" + " case 0:\n" + " case 1: x = 1; break;\n" + " case 2: x = 2; break;\n" + " case 3: x = 3; [[fallthrough]];\n" + " case 4: x = 4; break;\n" + " default: y = 1; break;\n" + "}", + Style); } TEST_F(FormatTest, FormatsLabels) { @@ -20148,7 +20197,6 @@ CHECK_PARSE_BOOL(AlignTrailingComments); CHECK_PARSE_BOOL(AllowAllArgumentsOnNextLine); CHECK_PARSE_BOOL(AllowAllParametersOfDeclarationOnNextLine); - CHECK_PARSE_BOOL(AllowShortCaseLabelsOnASingleLine); CHECK_PARSE_BOOL(AllowShortEnumsOnASingleLine); CHECK_PARSE_BOOL(AllowShortLoopsOnASingleLine); CHECK_PARSE_BOOL(BinPackArguments); @@ -20509,6 +20557,20 @@ CHECK_PARSE("AllowShortFunctionsOnASingleLine: true", AllowShortFunctionsOnASingleLine, FormatStyle::SFS_All); + Style.AllowShortCaseLabelsOnASingleLine = FormatStyle::SCLS_Always; + CHECK_PARSE("AllowShortCaseLabelsOnASingleLine: Never", + AllowShortCaseLabelsOnASingleLine, FormatStyle::SCLS_Never); + CHECK_PARSE("AllowShortCaseLabelsOnASingleLine: Always", + AllowShortCaseLabelsOnASingleLine, FormatStyle::SCLS_Always); + CHECK_PARSE("AllowShortCaseLabelsOnASingleLine: NoFallThrough", + AllowShortCaseLabelsOnASingleLine, + FormatStyle::SCLS_NoFallThrough); + // For backward compatibility: + CHECK_PARSE("AllowShortCaseLabelsOnASingleLine: false", + AllowShortCaseLabelsOnASingleLine, FormatStyle::SCLS_Never); + CHECK_PARSE("AllowShortCaseLabelsOnASingleLine: true", + AllowShortCaseLabelsOnASingleLine, FormatStyle::SCLS_Always); + Style.SpaceAroundPointerQualifiers = FormatStyle::SAPQ_Both; CHECK_PARSE("SpaceAroundPointerQualifiers: Default", SpaceAroundPointerQualifiers, FormatStyle::SAPQ_Default);