llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang-format Author: Anan Yablonko (ananski-the-3rd) <details> <summary>Changes</summary> --- Full diff: https://github.com/llvm/llvm-project/pull/227656.diff 8 Files Affected: - (modified) clang/docs/ClangFormatStyleOptions.md (+17) - (modified) clang/include/clang/Format/Format.h (+15) - (modified) clang/lib/Format/Format.cpp (+58-1) - (modified) clang/lib/Format/UnwrappedLineFormatter.cpp (+9) - (modified) clang/lib/Format/UnwrappedLineParser.cpp (+17-1) - (modified) clang/lib/Format/UnwrappedLineParser.h (+1) - (modified) clang/unittests/Format/ConfigParseTest.cpp (+1) - (modified) clang/unittests/Format/FormatTest.cpp (+53) ``````````diff diff --git a/clang/docs/ClangFormatStyleOptions.md b/clang/docs/ClangFormatStyleOptions.md index 81984ff185e53..ae50a75886a84 100644 --- a/clang/docs/ClangFormatStyleOptions.md +++ b/clang/docs/ClangFormatStyleOptions.md @@ -5431,6 +5431,23 @@ the configuration (without a prefix: `Auto`). newlines. (See www.gnu.org/prep/standards/html_node/Formatting.html#:~:text=formfeed.) +(kwbreakbeforecaselabel)= + +**KwBreakBeforeCaseLabel** (`Boolean`) {ref}`¶ <KwBreakBeforeCaseLabel>` + +: Keep the break keyword and the following case label on the same line. + + ```c++ + false: true: + switch (foo) { switch (foo) { + case 1: case 1: + bar(); bar(); + break; break; case 2: + case 2: bar(); + bar(); } + } + ``` + (lambdabodyindentation)= **LambdaBodyIndentation** (`LambdaBodyIndentationKind`) {versionbadge}`clang-format 13` {ref}`¶ <LambdaBodyIndentation>` diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h index 6d4fa6e8ee2a7..1574585439971 100644 --- a/clang/include/clang/Format/Format.h +++ b/clang/include/clang/Format/Format.h @@ -3791,6 +3791,20 @@ struct FormatStyle { /// \version 20 bool KeepFormFeed; + /// Keep the break keyword and the following case label on the same line. + /// + /// \code + /// false: true: + /// switch (foo) { switch (foo) { + /// case 1: case 1: + /// bar(); bar(); + /// break; break; case 2: + /// case 2: bar(); + /// bar(); } + /// } + /// \endcode + bool KwBreakBeforeCaseLabel; + /// Indentation logic for lambda bodies. enum LambdaBodyIndentationKind : int8_t { /// Align lambda body relative to the lambda signature. This is the default. @@ -6259,6 +6273,7 @@ struct FormatStyle { JavaScriptWrapImports == R.JavaScriptWrapImports && KeepEmptyLines == R.KeepEmptyLines && KeepFormFeed == R.KeepFormFeed && Language == R.Language && + KwBreakBeforeCaseLabel == R.KwBreakBeforeCaseLabel && LambdaBodyIndentation == R.LambdaBodyIndentation && LineEnding == R.LineEnding && MacroBlockBegin == R.MacroBlockBegin && MacroBlockEnd == R.MacroBlockEnd && Macros == R.Macros && diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 4c78c1dbe9f80..447254147423f 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -1409,7 +1409,7 @@ template <> struct MappingTraits<FormatStyle> { IO.mapOptional("IndentWidth", Style.IndentWidth); IO.mapOptional("IndentWrappedFunctionNames", Style.IndentWrappedFunctionNames); - IO.mapOptional("InsertBraces", Style.InsertBraces); + IO.mapOptional("InsertBraces", Style.InsertBraces); IO.mapOptional("InsertNewlineAtEOF", Style.InsertNewlineAtEOF); IO.mapOptional("InsertTrailingCommas", Style.InsertTrailingCommas); IO.mapOptional("IntegerLiteralSeparator", Style.IntegerLiteralSeparator); @@ -1418,6 +1418,7 @@ template <> struct MappingTraits<FormatStyle> { IO.mapOptional("JavaScriptWrapImports", Style.JavaScriptWrapImports); IO.mapOptional("KeepEmptyLines", Style.KeepEmptyLines); IO.mapOptional("KeepFormFeed", Style.KeepFormFeed); + IO.mapOptional("KwBreakBeforeCaseLabel", Style.KwBreakBeforeCaseLabel); IO.mapOptional("LambdaBodyIndentation", Style.LambdaBodyIndentation); IO.mapOptional("LineEnding", Style.LineEnding); IO.mapOptional("MacroBlockBegin", Style.MacroBlockBegin); @@ -1998,6 +1999,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) { /*AtStartOfFile=*/true, }; LLVMStyle.KeepFormFeed = false; + LLVMStyle.KwBreakBeforeCaseLabel = false; LLVMStyle.LambdaBodyIndentation = FormatStyle::LBI_Signature; LLVMStyle.Language = Language; LLVMStyle.LineEnding = FormatStyle::LE_DeriveLF; @@ -2794,6 +2796,53 @@ class BracesRemover : public TokenAnalyzer { } }; +class KwBreakInserter : public TokenAnalyzer { +public: + KwBreakInserter(const Environment &Env, const FormatStyle &Style) + : TokenAnalyzer(Env, Style) {} + + std::pair<tooling::Replacements, unsigned> + analyze(TokenAnnotator &Annotator, + SmallVectorImpl<AnnotatedLine *> &AnnotatedLines, + FormatTokenLexer &Tokens) override { + AffectedRangeMgr.computeAffectedLines(AnnotatedLines); + tooling::Replacements Result; + insertKwBreak(AnnotatedLines, Result); + return {Result, 0}; + } + +private: + static constexpr StringRef KwBreak = "break;"; + + void insertKwBreak(SmallVectorImpl<AnnotatedLine *> &Lines, + tooling::Replacements &Result) { + + const auto &SourceMgr = Env.getSourceManager(); + const auto *End = Lines.end(); + for (const auto *I = Lines.begin(); I != End; ++I) { + const auto &Line = *I; + if (!Line->Children.empty()) + insertKwBreak(Line->Children, Result); + if (!Line->Affected || !Line->startsWith(tok::kw_case)) + continue; + + const AnnotatedLine *Prev = nullptr; + for (auto IReverse = std::make_reverse_iterator(I); + IReverse != Lines.rend(); ++IReverse) { + if (!(*IReverse)->isComment()) { + Prev = *IReverse; + break; + } + } + if (!Prev || Prev->getLastNonComment()->isNot(tok::l_brace)) + continue; + cantFail(Result.add(tooling::Replacement( + SourceMgr, Line->getFirstNonComment()->Tok.getLocation(), 0, + KwBreak))); + } + } +}; + class SemiRemover : public TokenAnalyzer { public: SemiRemover(const Environment &Env, const FormatStyle &Style) @@ -4408,6 +4457,14 @@ reformat(const FormatStyle &Style, StringRef Code, }); } + if (Style.KwBreakBeforeCaseLabel) { + FormatStyle S = Expanded; + S.KwBreakBeforeCaseLabel = true; + Passes.emplace_back([&, S = std::move(S)](const Environment &Env) { + return KwBreakInserter(Env, S).process(/*SkipAnnotation=*/true); + }); + } + if (Style.EnumTrailingComma != FormatStyle::ETC_Leave) { Passes.emplace_back([&](const Environment &Env) { return EnumTrailingCommaEditor(Env, Expanded) diff --git a/clang/lib/Format/UnwrappedLineFormatter.cpp b/clang/lib/Format/UnwrappedLineFormatter.cpp index f005f228328ce..b046585ac0d02 100644 --- a/clang/lib/Format/UnwrappedLineFormatter.cpp +++ b/clang/lib/Format/UnwrappedLineFormatter.cpp @@ -281,6 +281,15 @@ class LineJoiner { return MergedLines; } + // Handle case labels preceeeded by a break statement + if (Style.KwBreakBeforeCaseLabel + && NextLine.First->isOneOf(tok::kw_case, tok::kw_default) + && TheLine->endsWith(tok::semi, tok::kw_break) + && TheLine->Level <= NextLine.Level) + { + return 1; + } + const auto *PreviousLine = I != AnnotatedLines.begin() ? I[-1] : nullptr; // Handle blocks where the brace has already been wrapped. diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp index 4825e825af1fa..d56381f7b8242 100644 --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -344,6 +344,18 @@ bool UnwrappedLineParser::precededByCommentOrPPDirective() const { (Previous->IsMultiline || Previous->NewlinesBefore > 0); } +bool UnwrappedLineParser::linePrecededByKwBreak() const { + const UnwrappedLine &previousLine = Lines.back(); + + if (Lines.empty() || previousLine.Tokens.empty()) + return false; + + if (previousLine.Level > Line->Level) + return false; + + return previousLine.Tokens.front().Tok->is(tok::kw_break); +} + /// Parses a level, that is ???. /// \param OpeningBrace Opening brace (\p nullptr if absent) of that level. /// \param IfKind The \p if statement kind in the level. @@ -3472,6 +3484,9 @@ void UnwrappedLineParser::parseLabel(bool IsGotoLabel) { const auto OldLineLevel = Line->Level; auto &Level = Line->Level; + if (!IsGotoLabel && Style.KwBreakBeforeCaseLabel && linePrecededByKwBreak()) + Lines.back().Level = Level - 1; + if (IsGotoLabel && IndentGotoLabel == FormatStyle::IGLS_NoIndent) Level = 0; @@ -3488,7 +3503,8 @@ void UnwrappedLineParser::parseLabel(bool IsGotoLabel) { parseBlock(); if (FormatTok->is(tok::kw_break)) { if (Style.BraceWrapping.AfterControlStatement == - FormatStyle::BWACS_Always) { + FormatStyle::BWACS_Always || + Style.KwBreakBeforeCaseLabel) { addUnwrappedLine(); if (!Style.IndentCaseBlocks && Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths) { diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h index 5b93c8f346d75..3225e44a064c5 100644 --- a/clang/lib/Format/UnwrappedLineParser.h +++ b/clang/lib/Format/UnwrappedLineParser.h @@ -124,6 +124,7 @@ class UnwrappedLineParser { void reset(); void parseFile(); bool precededByCommentOrPPDirective() const; + bool linePrecededByKwBreak() const; bool parseLevel(const FormatToken *OpeningBrace = nullptr, IfStmtKind *IfKind = nullptr, FormatToken **IfLeftBrace = nullptr); diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp index 86511edb9d40d..0c37acc439239 100644 --- a/clang/unittests/Format/ConfigParseTest.cpp +++ b/clang/unittests/Format/ConfigParseTest.cpp @@ -202,6 +202,7 @@ TEST(ConfigParseTest, ParsesConfigurationBools) { CHECK_PARSE_BOOL_FIELD(KeepEmptyLines.AtStartOfBlock, "KeepEmptyLinesAtTheStartOfBlocks"); CHECK_PARSE_BOOL(KeepFormFeed); + CHECK_PARSE_BOOL(KwBreakBeforeCaseLabel); CHECK_PARSE_BOOL(ObjCSpaceAfterMethodDeclarationPrefix); CHECK_PARSE_BOOL(ObjCSpaceAfterProperty); CHECK_PARSE_BOOL(ObjCSpaceBeforeProtocolList); diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index bb630da34d7d9..543f717288f77 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -2835,6 +2835,59 @@ TEST_F(FormatTest, FormatsSwitchStatement) { Style); } +TEST_F(FormatTest, FormatsSwitchStatementKwBreakBeforeCase) { + FormatStyle Style = getLLVMStyle(); + Style.IndentCaseLabels = true; + Style.KwBreakBeforeCaseLabel = true; + + verifyFormat("switch (n) {\n" + " break; case 0:;\n" + "}", + Style); + + verifyFormat("switch (n) {\n" + " break; case 0: {\n" + " return false;\n" + " }\n" + " break; case 1:\n" + " break; default: {\n" + " return true;\n" + " }\n" + "}", + Style); + + Style.IndentCaseLabels = true; + Style.IndentCaseBlocks = false; + Style.KwBreakBeforeCaseLabel = true; + verifyFormat("switch (n) {\n" + " break; case 0: {\n" + " return false;\n" + " }\n" + " break; case 1:\n" + " while (x)\n" + " break;\n" + " case 2:\n" + " break; default: {\n" + " return true;\n" + " }\n" + "}", + "switch (n) {\n" + "case 0: {\n" + " return false;\n" + "}\n" + " break;\n" + "case 1:\n" + " while (x)\n" + " break;\n" + "case 2:\n" + " break;\n" + "default: {\n" + " return true;\n" + "}\n" + "}", + Style); +} + TEST_F(FormatTest, CaseRanges) { verifyFormat("switch (x) {\n" "case 'A' ... 'Z':\n" `````````` </details> https://github.com/llvm/llvm-project/pull/227656 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
