diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst index ce9035a2770ee..6d092219877f9 100644 --- a/clang/docs/ClangFormatStyleOptions.rst +++ b/clang/docs/ClangFormatStyleOptions.rst @@ -861,7 +861,8 @@ the configuration (without a prefix: ``Auto``). **AlignConsecutiveShortCaseStatements** (``ShortCaseStatementsAlignmentStyle``) :versionbadge:`clang-format 17` :ref:`¶ ` Style of aligning consecutive short case labels. - Only applies if ``AllowShortCaseLabelsOnASingleLine`` is ``true``. + Only applies if ``AllowShortCaseExpressionOnASingleLine`` or + ``AllowShortCaseLabelsOnASingleLine`` is ``true``. .. code-block:: yaml @@ -935,6 +936,24 @@ the configuration (without a prefix: ``Auto``). default: return ""; } + * ``bool AlignCaseArrows`` Whether to align the case arrows when aligning short case expressions. + + .. code-block:: java + + true: + i = switch (day) { + case THURSDAY, SATURDAY -> 8; + case WEDNESDAY -> 9; + default -> 0; + }; + + false: + i = switch (day) { + case THURSDAY, SATURDAY -> 8; + case WEDNESDAY -> 9; + default -> 0; + }; + * ``bool AlignCaseColons`` Whether aligned case labels are aligned on the colon, or on the tokens after the colon. @@ -1692,6 +1711,21 @@ the configuration (without a prefix: ``Auto``). +.. _AllowShortCaseExpressionOnASingleLine: + +**AllowShortCaseExpressionOnASingleLine** (``Boolean``) :versionbadge:`clang-format 19` :ref:`¶ ` + Whether to merge a short switch labeled rule into a single line. + + .. code-block:: java + + true: false: + switch (a) { vs. switch (a) { + case 1 -> 1; case 1 -> + default -> 0; 1; + }; default -> + 0; + }; + .. _AllowShortCaseLabelsOnASingleLine: **AllowShortCaseLabelsOnASingleLine** (``Boolean``) :versionbadge:`clang-format 3.6` :ref:`¶ ` diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index b146a9b56884a..a85095e424b64 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -834,6 +834,9 @@ clang-format ``BreakTemplateDeclarations``. - ``AlwaysBreakAfterReturnType`` is deprecated and renamed to ``BreakAfterReturnType``. +- Handles Java ``switch`` expressions. +- Adds ``AllowShortCaseExpressionOnASingleLine`` option. +- Adds ``AlignCaseArrows`` suboption to ``AlignConsecutiveShortCaseStatements``. libclang -------- diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 8ebdc86b98329..74893f23210cd 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -375,6 +375,23 @@ struct FormatStyle { /// } /// \endcode bool AcrossComments; + /// Whether to align the case arrows when aligning short case expressions. + /// \code{.java} + /// true: + /// i = switch (day) { + /// case THURSDAY, SATURDAY -> 8; + /// case WEDNESDAY -> 9; + /// default -> 0; + /// }; + /// + /// false: + /// i = switch (day) { + /// case THURSDAY, SATURDAY -> 8; + /// case WEDNESDAY -> 9; + /// default -> 0; + /// }; + /// \endcode + bool AlignCaseArrows; /// Whether aligned case labels are aligned on the colon, or on the tokens /// after the colon. /// \code @@ -396,12 +413,14 @@ struct FormatStyle { bool operator==(const ShortCaseStatementsAlignmentStyle &R) const { return Enabled == R.Enabled && AcrossEmptyLines == R.AcrossEmptyLines && AcrossComments == R.AcrossComments && + AlignCaseArrows == R.AlignCaseArrows && AlignCaseColons == R.AlignCaseColons; } }; /// Style of aligning consecutive short case labels. - /// Only applies if ``AllowShortCaseLabelsOnASingleLine`` is ``true``. + /// Only applies if ``AllowShortCaseExpressionOnASingleLine`` or + /// ``AllowShortCaseLabelsOnASingleLine`` is ``true``. /// /// \code{.yaml} /// # Example of usage: @@ -724,6 +743,19 @@ struct FormatStyle { /// \version 3.5 ShortBlockStyle AllowShortBlocksOnASingleLine; + /// Whether to merge a short switch labeled rule into a single line. + /// \code{.java} + /// true: false: + /// switch (a) { vs. switch (a) { + /// case 1 -> 1; case 1 -> + /// default -> 0; 1; + /// }; default -> + /// 0; + /// }; + /// \endcode + /// \version 19 + bool AllowShortCaseExpressionOnASingleLine; + /// If ``true``, short case labels will be contracted to a single line. /// \code /// true: false: @@ -4923,6 +4955,8 @@ struct FormatStyle { AllowBreakBeforeNoexceptSpecifier == R.AllowBreakBeforeNoexceptSpecifier && AllowShortBlocksOnASingleLine == R.AllowShortBlocksOnASingleLine && + AllowShortCaseExpressionOnASingleLine == + R.AllowShortCaseExpressionOnASingleLine && AllowShortCaseLabelsOnASingleLine == R.AllowShortCaseLabelsOnASingleLine && AllowShortCompoundRequirementOnASingleLine == diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index c8d8ec3afbd99..c4eac1c99a663 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -100,6 +100,7 @@ struct MappingTraits { IO.mapOptional("Enabled", Value.Enabled); IO.mapOptional("AcrossEmptyLines", Value.AcrossEmptyLines); IO.mapOptional("AcrossComments", Value.AcrossComments); + IO.mapOptional("AlignCaseArrows", Value.AlignCaseArrows); IO.mapOptional("AlignCaseColons", Value.AlignCaseColons); } }; @@ -911,6 +912,8 @@ template <> struct MappingTraits { Style.AllowBreakBeforeNoexceptSpecifier); IO.mapOptional("AllowShortBlocksOnASingleLine", Style.AllowShortBlocksOnASingleLine); + IO.mapOptional("AllowShortCaseExpressionOnASingleLine", + Style.AllowShortCaseExpressionOnASingleLine); IO.mapOptional("AllowShortCaseLabelsOnASingleLine", Style.AllowShortCaseLabelsOnASingleLine); IO.mapOptional("AllowShortCompoundRequirementOnASingleLine", @@ -1423,6 +1426,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { LLVMStyle.AllowAllParametersOfDeclarationOnNextLine = true; LLVMStyle.AllowBreakBeforeNoexceptSpecifier = FormatStyle::BBNSS_Never; LLVMStyle.AllowShortBlocksOnASingleLine = FormatStyle::SBS_Never; + LLVMStyle.AllowShortCaseExpressionOnASingleLine = true; LLVMStyle.AllowShortCaseLabelsOnASingleLine = false; LLVMStyle.AllowShortCompoundRequirementOnASingleLine = true; LLVMStyle.AllowShortEnumsOnASingleLine = true; diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h index 28b6488e54a42..95f16fde5005f 100644 --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -38,6 +38,7 @@ namespace format { /* l_brace of a block that is not the body of a (e.g. loop) statement. */ \ TYPE(BlockLBrace) \ TYPE(BracedListLBrace) \ + TYPE(CaseLabelArrow) \ /* The colon at the end of a case label. */ \ TYPE(CaseLabelColon) \ TYPE(CastRParen) \ @@ -148,6 +149,8 @@ namespace format { TYPE(StructLBrace) \ TYPE(StructRBrace) \ TYPE(StructuredBindingLSquare) \ + TYPE(SwitchExpressionLabel) \ + TYPE(SwitchExpressionLBrace) \ TYPE(TableGenBangOperator) \ TYPE(TableGenCondOperator) \ TYPE(TableGenCondOperatorColon) \ diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index d366ae2080bc2..e935d3e2709cc 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -5051,6 +5051,8 @@ bool TokenAnnotator::spaceRequiredBefore(const AnnotatedLine &Line, return true; // "x! as string", "x! in y" } } else if (Style.Language == FormatStyle::LK_Java) { + if (Left.is(TT_CaseLabelArrow) || Right.is(TT_CaseLabelArrow)) + return true; if (Left.is(tok::r_square) && Right.is(tok::l_brace)) return true; // spaces inside square brackets. diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index 4ae54e56331bd..4d53361aaf333 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -515,6 +515,12 @@ class LineJoiner { } } + if (TheLine->First->is(TT_SwitchExpressionLabel)) { + return Style.AllowShortCaseExpressionOnASingleLine + ? tryMergeShortCaseLabels(I, E, Limit) + : 0; + } + if (TheLine->Last->is(tok::l_brace)) { bool ShouldMerge = false; // Try to merge records. diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index e8a8dd58d07ee..dd71368b0ee50 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -430,9 +430,9 @@ bool UnwrappedLineParser::parseLevel(const FormatToken *OpeningBrace, unsigned StoredPosition = Tokens->getPosition(); auto *Next = Tokens->getNextNonComment(); FormatTok = Tokens->setPosition(StoredPosition); - if (Next->isNot(tok::colon)) { - // default not followed by ':' is not a case label; treat it like - // an identifier. + if (!Next->isOneOf(tok::colon, tok::arrow)) { + // default not followed by `:` or `->` is not a case label; treat it + // like an identifier. parseStructuralElement(); break; } @@ -451,6 +451,7 @@ bool UnwrappedLineParser::parseLevel(const FormatToken *OpeningBrace, } if (!SwitchLabelEncountered && (Style.IndentCaseLabels || + (OpeningBrace && OpeningBrace->is(TT_SwitchExpressionLBrace)) || (Line->InPPDirective && Line->Level == 1))) { ++Line->Level; } @@ -1519,9 +1520,9 @@ void UnwrappedLineParser::parseStructuralElement( // 'switch: string' field declaration. break; } - parseSwitch(); + parseSwitch(/*IsExpr=*/false); return; - case tok::kw_default: + case tok::kw_default: { // In Verilog default along with other labels are handled in the next loop. if (Style.isVerilog()) break; @@ -1529,14 +1530,22 @@ void UnwrappedLineParser::parseStructuralElement( // 'default: string' field declaration. break; } + auto *Default = FormatTok; nextToken(); if (FormatTok->is(tok::colon)) { FormatTok->setFinalizedType(TT_CaseLabelColon); parseLabel(); return; } + if (FormatTok->is(tok::arrow)) { + FormatTok->setFinalizedType(TT_CaseLabelArrow); + Default->setFinalizedType(TT_SwitchExpressionLabel); + parseLabel(); + return; + } // e.g. "default void f() {}" in a Java interface. break; + } case tok::kw_case: // Proto: there are no switch/case statements. if (Style.Language == FormatStyle::LK_Proto) { @@ -2061,6 +2070,11 @@ void UnwrappedLineParser::parseStructuralElement( case tok::kw_new: parseNew(); break; + case tok::kw_switch: + if (Style.Language == FormatStyle::LK_Java) + parseSwitch(/*IsExpr=*/true); + nextToken(); + break; case tok::kw_case: // Proto: there are no switch/case statements. if (Style.Language == FormatStyle::LK_Proto) { @@ -2583,6 +2597,9 @@ bool UnwrappedLineParser::parseParens(TokenType AmpAmpTokenType) { else nextToken(); break; + case tok::kw_switch: + parseSwitch(/*IsExpr=*/true); + break; case tok::kw_requires: { auto RequiresToken = FormatTok; nextToken(); @@ -3240,6 +3257,7 @@ void UnwrappedLineParser::parseLabel(bool LeftAlignLabel) { void UnwrappedLineParser::parseCaseLabel() { assert(FormatTok->is(tok::kw_case) && "'case' expected"); + auto *Case = FormatTok; // FIXME: fix handling of complex expressions here. do { @@ -3248,11 +3266,16 @@ void UnwrappedLineParser::parseCaseLabel() { FormatTok->setFinalizedType(TT_CaseLabelColon); break; } + if (Style.Language == FormatStyle::LK_Java && FormatTok->is(tok::arrow)) { + FormatTok->setFinalizedType(TT_CaseLabelArrow); + Case->setFinalizedType(TT_SwitchExpressionLabel); + break; + } } while (!eof()); parseLabel(); } -void UnwrappedLineParser::parseSwitch() { +void UnwrappedLineParser::parseSwitch(bool IsExpr) { assert(FormatTok->is(tok::kw_switch) && "'switch' expected"); nextToken(); if (FormatTok->is(tok::l_paren)) @@ -3262,10 +3285,15 @@ void UnwrappedLineParser::parseSwitch() { if (FormatTok->is(tok::l_brace)) { CompoundStatementIndenter Indenter(this, Style, Line->Level); - FormatTok->setFinalizedType(TT_ControlStatementLBrace); - parseBlock(); + FormatTok->setFinalizedType(IsExpr ? TT_SwitchExpressionLBrace + : TT_ControlStatementLBrace); + if (IsExpr) + parseChildBlock(); + else + parseBlock(); setPreviousRBraceType(TT_ControlStatementRBrace); - addUnwrappedLine(); + if (!IsExpr) + addUnwrappedLine(); } else { addUnwrappedLine(); ++Line->Level; diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h index e2cf28c0c065d..2a0fe19d09572 100644 --- a/clang/lib/Format/UnwrappedLineParser.h +++ b/clang/lib/Format/UnwrappedLineParser.h @@ -157,7 +157,7 @@ class UnwrappedLineParser { void parseDoWhile(); void parseLabel(bool LeftAlignLabel = false); void parseCaseLabel(); - void parseSwitch(); + void parseSwitch(bool IsExpr); void parseNamespace(); bool parseModuleImport(); void parseNew(); diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp index 44fd807ec27ea..ed06d6098a9f2 100644 --- a/clang/lib/Format/WhitespaceManager.cpp +++ b/clang/lib/Format/WhitespaceManager.cpp @@ -107,7 +107,8 @@ const tooling::Replacements &WhitespaceManager::generateReplacements() { llvm::sort(Changes, Change::IsBeforeInFile(SourceMgr)); calculateLineBreakInformation(); alignConsecutiveMacros(); - alignConsecutiveShortCaseStatements(); + alignConsecutiveShortCaseStatements(/*IsExpr=*/true); + alignConsecutiveShortCaseStatements(/*IsExpr=*/false); alignConsecutiveDeclarations(); alignConsecutiveBitFields(); alignConsecutiveAssignments(); @@ -878,22 +879,27 @@ void WhitespaceManager::alignConsecutiveColons( Changes, /*StartAt=*/0, AlignStyle); } -void WhitespaceManager::alignConsecutiveShortCaseStatements() { +void WhitespaceManager::alignConsecutiveShortCaseStatements(bool IsExpr) { if (!Style.AlignConsecutiveShortCaseStatements.Enabled || - !Style.AllowShortCaseLabelsOnASingleLine) { + !(IsExpr ? Style.AllowShortCaseExpressionOnASingleLine + : Style.AllowShortCaseLabelsOnASingleLine)) { return; } + const auto Type = IsExpr ? TT_CaseLabelArrow : TT_CaseLabelColon; + const auto &Option = Style.AlignConsecutiveShortCaseStatements; + const bool AlignArrowOrColon = + IsExpr ? Option.AlignCaseArrows : Option.AlignCaseColons; + auto Matches = [&](const Change &C) { - if (Style.AlignConsecutiveShortCaseStatements.AlignCaseColons) - return C.Tok->is(TT_CaseLabelColon); + if (AlignArrowOrColon) + return C.Tok->is(Type); // Ignore 'IsInsideToken' to allow matching trailing comments which // need to be reflowed as that causes the token to appear in two // different changes, which will cause incorrect alignment as we'll // reflow early due to detecting multiple aligning tokens per line. - return !C.IsInsideToken && C.Tok->Previous && - C.Tok->Previous->is(TT_CaseLabelColon); + return !C.IsInsideToken && C.Tok->Previous && C.Tok->Previous->is(Type); }; unsigned MinColumn = 0; @@ -944,7 +950,7 @@ void WhitespaceManager::alignConsecutiveShortCaseStatements() { if (Changes[I].Tok->isNot(tok::comment)) LineIsComment = false; - if (Changes[I].Tok->is(TT_CaseLabelColon)) { + if (Changes[I].Tok->is(Type)) { LineIsEmptyCase = !Changes[I].Tok->Next || Changes[I].Tok->Next->isTrailingComment(); diff --git a/clang/lib/Format/WhitespaceManager.h b/clang/lib/Format/WhitespaceManager.h index 98cf4a260cc46..7b91d8bf4db72 100644 --- a/clang/lib/Format/WhitespaceManager.h +++ b/clang/lib/Format/WhitespaceManager.h @@ -233,7 +233,7 @@ class WhitespaceManager { void alignChainedConditionals(); /// Align consecutive short case statements over all \c Changes. - void alignConsecutiveShortCaseStatements(); + void alignConsecutiveShortCaseStatements(bool IsExpr); /// Align consecutive TableGen DAGArg colon over all \c Changes. void alignConsecutiveTableGenBreakingDAGArgColons(); diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index 8c74ed2d119a3..82e72f08ffb5e 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -153,6 +153,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) { Style.Language = FormatStyle::LK_Cpp; CHECK_PARSE_BOOL(AllowAllArgumentsOnNextLine); CHECK_PARSE_BOOL(AllowAllParametersOfDeclarationOnNextLine); + CHECK_PARSE_BOOL(AllowShortCaseExpressionOnASingleLine); CHECK_PARSE_BOOL(AllowShortCaseLabelsOnASingleLine); CHECK_PARSE_BOOL(AllowShortCompoundRequirementOnASingleLine); CHECK_PARSE_BOOL(AllowShortEnumsOnASingleLine); @@ -205,6 +206,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) { CHECK_PARSE_NESTED_BOOL(AlignConsecutiveShortCaseStatements, AcrossEmptyLines); CHECK_PARSE_NESTED_BOOL(AlignConsecutiveShortCaseStatements, AcrossComments); + CHECK_PARSE_NESTED_BOOL(AlignConsecutiveShortCaseStatements, AlignCaseArrows); CHECK_PARSE_NESTED_BOOL(AlignConsecutiveShortCaseStatements, AlignCaseColons); CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterCaseLabel); CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterClass); diff --git a/clang/unittests/Format/FormatTestJava.cpp b/clang/unittests/Format/FormatTestJava.cpp index 6da5f4fa25433..33998bc7ff858 100644 --- a/clang/unittests/Format/FormatTestJava.cpp +++ b/clang/unittests/Format/FormatTestJava.cpp @@ -618,6 +618,177 @@ TEST_F(FormatTestJava, ConfigurableSpacesInSquareBrackets) { verifyFormat("types[ i ] = arguments[ i ].getClass();", Spaces); } +TEST_F(FormatTestJava, SwitchExpression) { + auto Style = getLLVMStyle(FormatStyle::LK_Java); + EXPECT_TRUE(Style.AllowShortCaseExpressionOnASingleLine); + + verifyFormat("foo(switch (day) {\n" + " case THURSDAY, SATURDAY -> 8;\n" + " case WEDNESDAY -> 9;\n" + " default -> 1;\n" + "});", + Style); + + constexpr StringRef Code1{"i = switch (day) {\n" + " case THURSDAY, SATURDAY -> 8;\n" + " case WEDNESDAY -> 9;\n" + " default -> 0;\n" + "};"}; + verifyFormat(Code1, Style); + + Style.IndentCaseLabels = true; + verifyFormat(Code1, Style); + + constexpr StringRef Code2{"i = switch (day) {\n" + " case THURSDAY, SATURDAY -> {\n" + " foo();\n" + " yield 8;\n" + " }\n" + " case WEDNESDAY -> {\n" + " bar();\n" + " yield 9;\n" + " }\n" + " default -> {\n" + " yield 0;\n" + " }\n" + "};"}; + verifyFormat(Code2, Style); + + Style.IndentCaseLabels = false; + verifyFormat(Code2, Style); + + constexpr StringRef Code3{"switch (day) {\n" + "case THURSDAY, SATURDAY -> i = 8;\n" + "case WEDNESDAY -> i = 9;\n" + "default -> i = 0;\n" + "};"}; + verifyFormat(Code3, Style); + + Style.IndentCaseLabels = true; + verifyFormat("switch (day) {\n" + " case THURSDAY, SATURDAY -> i = 8;\n" + " case WEDNESDAY -> i = 9;\n" + " default -> i = 0;\n" + "};", + Code3, Style); +} + +TEST_F(FormatTestJava, ShortCaseExpression) { + auto Style = getLLVMStyle(FormatStyle::LK_Java); + + verifyFormat("i = switch (a) {\n" + " case 1 -> 1;\n" + " case 2 -> // comment\n" + " 2;\n" + " case 3 ->\n" + " // comment\n" + " 3;\n" + " case 4 -> 4; // comment\n" + " default -> 0;\n" + "};", + Style); + + verifyNoChange("i = switch (a) {\n" + " case 1 -> 1;\n" + " // comment\n" + " case 2 -> 2;\n" + " // comment 1\n" + " // comment 2\n" + " case 3 -> 3; /* comment */\n" + " case 4 -> /* comment */ 4;\n" + " case 5 -> x + /* comment */ 1;\n" + " default ->\n" + " 0; // comment line 1\n" + " // comment line 2\n" + "};", + Style); + + Style.ColumnLimit = 18; + verifyFormat("i = switch (a) {\n" + " case Monday ->\n" + " 1;\n" + " default -> 9999;\n" + "};", + Style); + + Style.ColumnLimit = 80; + Style.AllowShortCaseExpressionOnASingleLine = false; + Style.IndentCaseLabels = true; + verifyFormat("i = switch (n) {\n" + " default /*comments*/ ->\n" + " 1;\n" + " case 0 ->\n" + " 0;\n" + "};", + Style); + + Style.AllowShortCaseExpressionOnASingleLine = true; + Style.BreakBeforeBraces = FormatStyle::BS_Custom; + Style.BraceWrapping.AfterCaseLabel = true; + Style.BraceWrapping.AfterControlStatement = FormatStyle::BWACS_Always; + verifyFormat("i = switch (n)\n" + "{\n" + " case 0 ->\n" + " {\n" + " yield 0;\n" + " }\n" + " default ->\n" + " {\n" + " yield 1;\n" + " }\n" + "};", + Style); +} + +TEST_F(FormatTestJava, AlignCaseArrows) { + auto Style = getLLVMStyle(FormatStyle::LK_Java); + Style.AlignConsecutiveShortCaseStatements.Enabled = true; + + verifyFormat("foo(switch (day) {\n" + " case THURSDAY, SATURDAY -> 8;\n" + " case WEDNESDAY -> 9;\n" + " default -> 1;\n" + "});", + Style); + + verifyFormat("i = switch (day) {\n" + " case THURSDAY, SATURDAY -> 8;\n" + " case WEDNESDAY -> 9;\n" + " default -> 0;\n" + "};", + Style); + + verifyFormat("switch (day) {\n" + "case THURSDAY, SATURDAY -> i = 8;\n" + "case WEDNESDAY -> i = 9;\n" + "default -> i = 0;\n" + "};", + Style); + + Style.AlignConsecutiveShortCaseStatements.AlignCaseArrows = true; + + verifyFormat("foo(switch (day) {\n" + " case THURSDAY, SATURDAY -> 8;\n" + " case WEDNESDAY -> 9;\n" + " default -> 1;\n" + "});", + Style); + + verifyFormat("i = switch (day) {\n" + " case THURSDAY, SATURDAY -> 8;\n" + " case WEDNESDAY -> 9;\n" + " default -> 0;\n" + "};", + Style); + + verifyFormat("switch (day) {\n" + "case THURSDAY, SATURDAY -> i = 8;\n" + "case WEDNESDAY -> i = 9;\n" + "default -> i = 0;\n" + "};", + Style); +} + } // namespace } // namespace test } // namespace format diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp index 01daf8dee505b..b567120152668 100644 --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -2977,6 +2977,24 @@ TEST_F(TokenAnnotatorTest, BlockLBrace) { EXPECT_BRACE_KIND(Tokens[5], BK_Block); } +TEST_F(TokenAnnotatorTest, SwitchExpression) { + auto Style = getLLVMStyle(FormatStyle::LK_Java); + auto Tokens = annotate("i = switch (day) {\n" + " case THURSDAY, SATURDAY -> 8;\n" + " case WEDNESDAY -> 9;\n" + " default -> 1;\n" + "};", + Style); + ASSERT_EQ(Tokens.size(), 26u) << Tokens; + EXPECT_TOKEN(Tokens[6], tok::l_brace, TT_SwitchExpressionLBrace); + EXPECT_TOKEN(Tokens[7], tok::kw_case, TT_SwitchExpressionLabel); + EXPECT_TOKEN(Tokens[11], tok::arrow, TT_CaseLabelArrow); + EXPECT_TOKEN(Tokens[14], tok::kw_case, TT_SwitchExpressionLabel); + EXPECT_TOKEN(Tokens[16], tok::arrow, TT_CaseLabelArrow); + EXPECT_TOKEN(Tokens[19], tok::kw_default, TT_SwitchExpressionLabel); + EXPECT_TOKEN(Tokens[20], tok::arrow, TT_CaseLabelArrow); +} + } // namespace } // namespace format } // namespace clang