diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -37,6 +37,8 @@ TYPE(BitFieldColon) \ TYPE(BlockComment) \ TYPE(BracedListLBrace) \ + /* The colon at the end of a case label. */ \ + TYPE(CaseLabelColon) \ TYPE(CastRParen) \ TYPE(ClassLBrace) \ TYPE(CompoundRequirementLBrace) \ @@ -73,8 +75,7 @@ TYPE(FunctionTypeLParen) \ /* The colons as part of a C11 _Generic selection */ \ TYPE(GenericSelectionColon) \ - /* The colon at the end of a goto label or a case label. Currently only used \ - * for Verilog. */ \ + /* The colon at the end of a goto label. */ \ TYPE(GotoLabelColon) \ TYPE(IfMacro) \ TYPE(ImplicitStringLiteral) \ @@ -1803,7 +1804,7 @@ bool isVerilogEndOfLabel(const FormatToken &Tok) const { const FormatToken *Next = Tok.getNextNonComment(); // In Verilog the colon in a default label is optional. - return Tok.is(TT_GotoLabelColon) || + return Tok.is(TT_CaseLabelColon) || (Tok.is(tok::kw_default) && !(Next && Next->isOneOf(tok::colon, tok::semi, kw_clocking, kw_iff, kw_input, kw_output, kw_sequence))); diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -970,6 +970,10 @@ case tok::colon: if (!Tok->Previous) return false; + // Goto labels and case labels are already identified in + // UnwrappedLineParser. + if (Tok->isTypeFinalized()) + break; // Colons from ?: are handled in parseConditional(). if (Style.isJavaScript()) { if (Contexts.back().ColonIsForRangeExpr || // colon in for loop @@ -1008,7 +1012,7 @@ // In Verilog a case label doesn't have the case keyword. We // assume a colon following an expression is a case label. // Colons from ?: are annotated in parseConditional(). - Tok->setType(TT_GotoLabelColon); + Tok->setType(TT_CaseLabelColon); if (Line.Level > 1 || (!Line.InPPDirective && Line.Level > 0)) --Line.Level; } @@ -4516,13 +4520,12 @@ Style.BitFieldColonSpacing == FormatStyle::BFCS_After; } if (Right.is(tok::colon)) { - if (Right.is(TT_GotoLabelColon) || - (!Style.isVerilog() && - Line.First->isOneOf(tok::kw_default, tok::kw_case))) { + if (Right.is(TT_CaseLabelColon)) return Style.SpaceBeforeCaseColon; - } - const FormatToken *Next = Right.getNextNonComment(); - if (!Next || Next->is(tok::semi)) + if (Right.is(TT_GotoLabelColon)) + return false; + // `private:` and `public:`. + if (!Right.getNextNonComment()) return false; if (Right.is(TT_ObjCMethodExpr)) return false; 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 @@ -711,7 +711,8 @@ // Check that the current line allows merging. This depends on whether we // are in a control flow statements as well as several style flags. - if (Line.First->is(tok::kw_case) || + if (Line.First->is(tok::kw_case) || Line.Last->is(TT_GotoLabelColon) || + Line.Last->endsSequence(tok::l_brace, TT_GotoLabelColon) || (Line.First->Next && Line.First->Next->is(tok::kw_else))) { return 0; } diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -1493,6 +1493,7 @@ } nextToken(); if (FormatTok->is(tok::colon)) { + FormatTok->setFinalizedType(TT_CaseLabelColon); parseLabel(); return; } @@ -1925,6 +1926,7 @@ if (!Style.isVerilog() && FormatTok->is(tok::colon) && !Line->MustBeDeclaration) { Line->Tokens.begin()->Tok->MustBreakBefore = true; + FormatTok->setFinalizedType(TT_GotoLabelColon); parseLabel(!Style.IndentGotoLabels); if (HasLabel) *HasLabel = true; @@ -3113,7 +3115,11 @@ // FIXME: fix handling of complex expressions here. do { nextToken(); - } while (!eof() && !FormatTok->is(tok::colon)); + if (FormatTok->is(tok::colon)) { + FormatTok->setFinalizedType(TT_CaseLabelColon); + break; + } + } while (!eof()); parseLabel(); } 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 @@ -2998,6 +2998,12 @@ "test_label:;\n" " int i = 0;\n" "}"); + verifyFormat("{\n" + " some_code();\n" + "test_label: {\n" + " some_other_code();\n" + "}\n" + "}"); FormatStyle Style = getLLVMStyle(); Style.IndentGotoLabels = false; verifyFormat("void f() {\n" @@ -3019,9 +3025,23 @@ Style); verifyFormat("{\n" " some_code();\n" - "test_label:;\n" - " int i = 0;\n" - "}"); + "test_label: {\n" + " some_other_code();\n" + "}\n" + "}", + Style); + // The opening brace may either be on the same unwrapped line as the colon or + // on a separate one. The formatter should recognize both. + Style = getLLVMStyle(); + Style.BreakBeforeBraces = FormatStyle::BraceBreakingStyle::BS_Allman; + verifyFormat("{\n" + " some_code();\n" + "test_label:\n" + "{\n" + " some_other_code();\n" + "}\n" + "}", + Style); } TEST_F(FormatTest, MultiLineControlStatements) { @@ -16777,6 +16797,21 @@ "}\n" "}", CaseStyle); + // Goto labels should not be affected. + verifyFormat("switch (x) {\n" + "goto_label:\n" + "default :\n" + "}", + CaseStyle); + verifyFormat("switch (x) {\n" + "goto_label: {\n" + " break;\n" + "}\n" + "default : {\n" + " break;\n" + "}\n" + "}", + CaseStyle); FormatStyle NoSpaceStyle = getLLVMStyle(); EXPECT_EQ(NoSpaceStyle.SpaceBeforeCaseColon, false); diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp --- a/clang/unittests/Format/TokenAnnotatorTest.cpp +++ b/clang/unittests/Format/TokenAnnotatorTest.cpp @@ -1621,7 +1621,7 @@ " x;\n" "endcase\n"); ASSERT_EQ(Tokens.size(), 10u) << Tokens; - EXPECT_TOKEN(Tokens[5], tok::colon, TT_GotoLabelColon); + EXPECT_TOKEN(Tokens[5], tok::colon, TT_CaseLabelColon); Tokens = Annotate("case (x)\n" " x ? x : x:\n" " x;\n" @@ -1629,7 +1629,7 @@ ASSERT_EQ(Tokens.size(), 14u) << Tokens; EXPECT_TOKEN(Tokens[5], tok::question, TT_ConditionalExpr); EXPECT_TOKEN(Tokens[7], tok::colon, TT_ConditionalExpr); - EXPECT_TOKEN(Tokens[9], tok::colon, TT_GotoLabelColon); + EXPECT_TOKEN(Tokens[9], tok::colon, TT_CaseLabelColon); // Non-blocking assignments. Tokens = Annotate("a <= b;"); ASSERT_EQ(Tokens.size(), 5u); @@ -1708,6 +1708,7 @@ EXPECT_TOKEN(Tokens[13], tok::l_brace, TT_FunctionLBrace); } + TEST_F(TokenAnnotatorTest, UnderstandsConditionParens) { auto Tokens = annotate("if (x) {}"); ASSERT_EQ(Tokens.size(), 7u) << Tokens; @@ -1724,6 +1725,21 @@ EXPECT_TOKEN(Tokens[8], tok::l_paren, TT_ConditionLParen); } +TEST_F(TokenAnnotatorTest, UnderstandsLabels) { + auto Tokens = annotate("{ x: break; }"); + ASSERT_EQ(Tokens.size(), 7u) << Tokens; + EXPECT_TOKEN(Tokens[2], tok::colon, TT_GotoLabelColon); + Tokens = annotate("{ case x: break; }"); + ASSERT_EQ(Tokens.size(), 8u) << Tokens; + EXPECT_TOKEN(Tokens[3], tok::colon, TT_CaseLabelColon); + Tokens = annotate("{ x: { break; } }"); + ASSERT_EQ(Tokens.size(), 9u) << Tokens; + EXPECT_TOKEN(Tokens[2], tok::colon, TT_GotoLabelColon); + Tokens = annotate("{ case x: { break; } }"); + ASSERT_EQ(Tokens.size(), 10u) << Tokens; + EXPECT_TOKEN(Tokens[3], tok::colon, TT_CaseLabelColon); +} + } // namespace } // namespace format } // namespace clang