aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOwen Pan <owenpiano@gmail.com>2023-07-24 18:24:30 -0700
committerOwen Pan <owenpiano@gmail.com>2023-07-24 18:24:30 -0700
commitac6e55146f8c48f8ced95dfe54004142c0b36756 (patch)
treea75c4e500a48a6675a707f13de601a99a8fa3c1e
parent4f093b31669a4f8e417259583141159586a05b28 (diff)
downloadllvm-ac6e55146f8c48f8ced95dfe54004142c0b36756.zip
llvm-ac6e55146f8c48f8ced95dfe54004142c0b36756.tar.gz
llvm-ac6e55146f8c48f8ced95dfe54004142c0b36756.tar.bz2
Revert "Revert "[clang-format] Add AlignConsecutiveShortCaseStatements""
This reverts commit 4f093b31669a4f8e417259583141159586a05b28.
-rw-r--r--clang/docs/ClangFormatStyleOptions.rst98
-rw-r--r--clang/docs/ReleaseNotes.rst2
-rw-r--r--clang/include/clang/Format/Format.h99
-rw-r--r--clang/lib/Format/Format.cpp14
-rw-r--r--clang/lib/Format/WhitespaceManager.cpp133
-rw-r--r--clang/lib/Format/WhitespaceManager.h3
-rw-r--r--clang/unittests/Format/ConfigParseTest.cpp5
-rw-r--r--clang/unittests/Format/FormatTest.cpp234
8 files changed, 569 insertions, 19 deletions
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 7547aa2..e85b8c0 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -789,6 +789,104 @@ the configuration (without a prefix: ``Auto``).
bbb >>= 2;
+.. _AlignConsecutiveShortCaseStatements:
+
+**AlignConsecutiveShortCaseStatements** (``ShortCaseStatementsAlignmentStyle``) :versionbadge:`clang-format 17` :ref:`¶ <AlignConsecutiveShortCaseStatements>`
+ Style of aligning consecutive short case labels.
+ Only applies if ``AllowShortCaseLabelsOnASingleLine`` is ``true``.
+
+
+ .. code-block:: yaml
+
+ # Example of usage:
+ AlignConsecutiveShortCaseStatements:
+ Enabled: true
+ AcrossEmptyLines: true
+ AcrossComments: true
+ AlignCaseColons: false
+
+ Nested configuration flags:
+
+ Alignment options.
+
+ * ``bool Enabled`` Whether aligning is enabled.
+
+ .. code-block:: c++
+
+ true:
+ switch (level) {
+ case log::info: return "info:";
+ case log::warning: return "warning:";
+ default: return "";
+ }
+
+ false:
+ switch (level) {
+ case log::info: return "info:";
+ case log::warning: return "warning:";
+ default: return "";
+ }
+
+ * ``bool AcrossEmptyLines`` Whether to align across empty lines.
+
+ .. code-block:: c++
+
+ true:
+ switch (level) {
+ case log::info: return "info:";
+ case log::warning: return "warning:";
+
+ default: return "";
+ }
+
+ false:
+ switch (level) {
+ case log::info: return "info:";
+ case log::warning: return "warning:";
+
+ default: return "";
+ }
+
+ * ``bool AcrossComments`` Whether to align across comments.
+
+ .. code-block:: c++
+
+ true:
+ switch (level) {
+ case log::info: return "info:";
+ case log::warning: return "warning:";
+ /* A comment. */
+ default: return "";
+ }
+
+ false:
+ switch (level) {
+ case log::info: return "info:";
+ case log::warning: return "warning:";
+ /* A comment. */
+ default: return "";
+ }
+
+ * ``bool AlignCaseColons`` Whether aligned case labels are aligned on the colon, or on the
+ , or on the tokens after the colon.
+
+ .. code-block:: c++
+
+ true:
+ switch (level) {
+ case log::info : return "info:";
+ case log::warning: return "warning:";
+ default : return "";
+ }
+
+ false:
+ switch (level) {
+ case log::info: return "info:";
+ case log::warning: return "warning:";
+ default: return "";
+ }
+
+
.. _AlignEscapedNewlines:
**AlignEscapedNewlines** (``EscapedNewlineAlignmentStyle``) :versionbadge:`clang-format 5` :ref:`¶ <AlignEscapedNewlines>`
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 4f82edd..13fb95b8 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -988,6 +988,8 @@ clang-format
- Add ``KeepEmptyLinesAtEOF`` to keep empty lines at end of file.
- Add ``RemoveParentheses`` to remove redundant parentheses.
- Add ``TypeNames`` to treat listed non-keyword identifiers as type names.
+- Add ``AlignConsecutiveShortCaseStatements`` which can be used to align case
+ labels in conjunction with ``AllowShortCaseLabelsOnASingleLine``.
libclang
--------
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index 8cb8dd2..7b429d5 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -299,6 +299,103 @@ struct FormatStyle {
/// \version 3.8
AlignConsecutiveStyle AlignConsecutiveDeclarations;
+ /// Alignment options.
+ ///
+ struct ShortCaseStatementsAlignmentStyle {
+ /// Whether aligning is enabled.
+ /// \code
+ /// true:
+ /// switch (level) {
+ /// case log::info: return "info:";
+ /// case log::warning: return "warning:";
+ /// default: return "";
+ /// }
+ ///
+ /// false:
+ /// switch (level) {
+ /// case log::info: return "info:";
+ /// case log::warning: return "warning:";
+ /// default: return "";
+ /// }
+ /// \endcode
+ bool Enabled;
+ /// Whether to align across empty lines.
+ /// \code
+ /// true:
+ /// switch (level) {
+ /// case log::info: return "info:";
+ /// case log::warning: return "warning:";
+ ///
+ /// default: return "";
+ /// }
+ ///
+ /// false:
+ /// switch (level) {
+ /// case log::info: return "info:";
+ /// case log::warning: return "warning:";
+ ///
+ /// default: return "";
+ /// }
+ /// \endcode
+ bool AcrossEmptyLines;
+ /// Whether to align across comments.
+ /// \code
+ /// true:
+ /// switch (level) {
+ /// case log::info: return "info:";
+ /// case log::warning: return "warning:";
+ /// /* A comment. */
+ /// default: return "";
+ /// }
+ ///
+ /// false:
+ /// switch (level) {
+ /// case log::info: return "info:";
+ /// case log::warning: return "warning:";
+ /// /* A comment. */
+ /// default: return "";
+ /// }
+ /// \endcode
+ bool AcrossComments;
+ /// Whether aligned case labels are aligned on the colon, or on the
+ /// , or on the tokens after the colon.
+ /// \code
+ /// true:
+ /// switch (level) {
+ /// case log::info : return "info:";
+ /// case log::warning: return "warning:";
+ /// default : return "";
+ /// }
+ ///
+ /// false:
+ /// switch (level) {
+ /// case log::info: return "info:";
+ /// case log::warning: return "warning:";
+ /// default: return "";
+ /// }
+ /// \endcode
+ bool AlignCaseColons;
+ bool operator==(const ShortCaseStatementsAlignmentStyle &R) const {
+ return Enabled == R.Enabled && AcrossEmptyLines == R.AcrossEmptyLines &&
+ AcrossComments == R.AcrossComments &&
+ AlignCaseColons == R.AlignCaseColons;
+ }
+ };
+
+ /// Style of aligning consecutive short case labels.
+ /// Only applies if ``AllowShortCaseLabelsOnASingleLine`` is ``true``.
+ ///
+ /// \code{.yaml}
+ /// # Example of usage:
+ /// AlignConsecutiveShortCaseStatements:
+ /// Enabled: true
+ /// AcrossEmptyLines: true
+ /// AcrossComments: true
+ /// AlignCaseColons: false
+ /// \endcode
+ /// \version 17
+ ShortCaseStatementsAlignmentStyle AlignConsecutiveShortCaseStatements;
+
/// Different styles for aligning escaped newlines.
enum EscapedNewlineAlignmentStyle : int8_t {
/// Don't align escaped newlines.
@@ -4357,6 +4454,8 @@ struct FormatStyle {
AlignConsecutiveBitFields == R.AlignConsecutiveBitFields &&
AlignConsecutiveDeclarations == R.AlignConsecutiveDeclarations &&
AlignConsecutiveMacros == R.AlignConsecutiveMacros &&
+ AlignConsecutiveShortCaseStatements ==
+ R.AlignConsecutiveShortCaseStatements &&
AlignEscapedNewlines == R.AlignEscapedNewlines &&
AlignOperands == R.AlignOperands &&
AlignTrailingComments == R.AlignTrailingComments &&
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 3df1b60d..1d20824 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -112,6 +112,17 @@ template <> struct MappingTraits<FormatStyle::AlignConsecutiveStyle> {
};
template <>
+struct MappingTraits<FormatStyle::ShortCaseStatementsAlignmentStyle> {
+ static void mapping(IO &IO,
+ FormatStyle::ShortCaseStatementsAlignmentStyle &Value) {
+ IO.mapOptional("Enabled", Value.Enabled);
+ IO.mapOptional("AcrossEmptyLines", Value.AcrossEmptyLines);
+ IO.mapOptional("AcrossComments", Value.AcrossComments);
+ IO.mapOptional("AlignCaseColons", Value.AlignCaseColons);
+ }
+};
+
+template <>
struct ScalarEnumerationTraits<FormatStyle::AttributeBreakingStyle> {
static void enumeration(IO &IO, FormatStyle::AttributeBreakingStyle &Value) {
IO.enumCase(Value, "Always", FormatStyle::ABS_Always);
@@ -857,6 +868,8 @@ template <> struct MappingTraits<FormatStyle> {
IO.mapOptional("AlignConsecutiveDeclarations",
Style.AlignConsecutiveDeclarations);
IO.mapOptional("AlignConsecutiveMacros", Style.AlignConsecutiveMacros);
+ IO.mapOptional("AlignConsecutiveShortCaseStatements",
+ Style.AlignConsecutiveShortCaseStatements);
IO.mapOptional("AlignEscapedNewlines", Style.AlignEscapedNewlines);
IO.mapOptional("AlignOperands", Style.AlignOperands);
IO.mapOptional("AlignTrailingComments", Style.AlignTrailingComments);
@@ -1333,6 +1346,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.AlignConsecutiveBitFields = {};
LLVMStyle.AlignConsecutiveDeclarations = {};
LLVMStyle.AlignConsecutiveMacros = {};
+ LLVMStyle.AlignConsecutiveShortCaseStatements = {};
LLVMStyle.AlignTrailingComments = {};
LLVMStyle.AlignTrailingComments.Kind = FormatStyle::TCAS_Always;
LLVMStyle.AlignTrailingComments.OverEmptyLines = 0;
diff --git a/clang/lib/Format/WhitespaceManager.cpp b/clang/lib/Format/WhitespaceManager.cpp
index 03dcc31..668ca38 100644
--- a/clang/lib/Format/WhitespaceManager.cpp
+++ b/clang/lib/Format/WhitespaceManager.cpp
@@ -102,6 +102,7 @@ const tooling::Replacements &WhitespaceManager::generateReplacements() {
llvm::sort(Changes, Change::IsBeforeInFile(SourceMgr));
calculateLineBreakInformation();
alignConsecutiveMacros();
+ alignConsecutiveShortCaseStatements();
alignConsecutiveDeclarations();
alignConsecutiveBitFields();
alignConsecutiveAssignments();
@@ -675,14 +676,12 @@ static unsigned AlignTokens(const FormatStyle &Style, F &&Matches,
//
// We need to adjust the StartOfTokenColumn of each Change that is on a line
// containing any matching token to be aligned and located after such token.
-static void AlignMacroSequence(
+static void AlignMatchingTokenSequence(
unsigned &StartOfSequence, unsigned &EndOfSequence, unsigned &MinColumn,
- unsigned &MaxColumn, bool &FoundMatchOnLine,
- std::function<bool(const WhitespaceManager::Change &C)> AlignMacrosMatches,
+ std::function<bool(const WhitespaceManager::Change &C)> Matches,
SmallVector<WhitespaceManager::Change, 16> &Changes) {
if (StartOfSequence > 0 && StartOfSequence < EndOfSequence) {
-
- FoundMatchOnLine = false;
+ bool FoundMatchOnLine = false;
int Shift = 0;
for (unsigned I = StartOfSequence; I != EndOfSequence; ++I) {
@@ -693,8 +692,8 @@ static void AlignMacroSequence(
// If this is the first matching token to be aligned, remember by how many
// spaces it has to be shifted, so the rest of the changes on the line are
- // shifted by the same amount
- if (!FoundMatchOnLine && AlignMacrosMatches(Changes[I])) {
+ // shifted by the same amount.
+ if (!FoundMatchOnLine && Matches(Changes[I])) {
FoundMatchOnLine = true;
Shift = MinColumn - Changes[I].StartOfTokenColumn;
Changes[I].Spaces += Shift;
@@ -708,7 +707,6 @@ static void AlignMacroSequence(
}
MinColumn = 0;
- MaxColumn = UINT_MAX;
StartOfSequence = 0;
EndOfSequence = 0;
}
@@ -747,7 +745,6 @@ void WhitespaceManager::alignConsecutiveMacros() {
};
unsigned MinColumn = 0;
- unsigned MaxColumn = UINT_MAX;
// Start and end of the token sequence we're processing.
unsigned StartOfSequence = 0;
@@ -775,8 +772,8 @@ void WhitespaceManager::alignConsecutiveMacros() {
!(LineIsComment && Style.AlignConsecutiveMacros.AcrossComments);
if (EmptyLineBreak || NoMatchBreak) {
- AlignMacroSequence(StartOfSequence, EndOfSequence, MinColumn, MaxColumn,
- FoundMatchOnLine, AlignMacrosMatches, Changes);
+ AlignMatchingTokenSequence(StartOfSequence, EndOfSequence, MinColumn,
+ AlignMacrosMatches, Changes);
}
// A new line starts, re-initialize line status tracking bools.
@@ -796,18 +793,12 @@ void WhitespaceManager::alignConsecutiveMacros() {
StartOfSequence = I;
unsigned ChangeMinColumn = Changes[I].StartOfTokenColumn;
- int LineLengthAfter = -Changes[I].Spaces;
- for (unsigned j = I; j != E && Changes[j].NewlinesBefore == 0; ++j)
- LineLengthAfter += Changes[j].Spaces + Changes[j].TokenLength;
- unsigned ChangeMaxColumn = Style.ColumnLimit - LineLengthAfter;
-
MinColumn = std::max(MinColumn, ChangeMinColumn);
- MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
}
EndOfSequence = I;
- AlignMacroSequence(StartOfSequence, EndOfSequence, MinColumn, MaxColumn,
- FoundMatchOnLine, AlignMacrosMatches, Changes);
+ AlignMatchingTokenSequence(StartOfSequence, EndOfSequence, MinColumn,
+ AlignMacrosMatches, Changes);
}
void WhitespaceManager::alignConsecutiveAssignments() {
@@ -863,6 +854,110 @@ void WhitespaceManager::alignConsecutiveBitFields() {
Changes, /*StartAt=*/0, Style.AlignConsecutiveBitFields);
}
+void WhitespaceManager::alignConsecutiveShortCaseStatements() {
+ if (!Style.AlignConsecutiveShortCaseStatements.Enabled ||
+ !Style.AllowShortCaseLabelsOnASingleLine) {
+ return;
+ }
+
+ auto Matches = [&](const Change &C) {
+ if (Style.AlignConsecutiveShortCaseStatements.AlignCaseColons)
+ return C.Tok->is(TT_CaseLabelColon);
+
+ // 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);
+ };
+
+ unsigned MinColumn = 0;
+
+ // Empty case statements don't break the alignment, but don't necessarily
+ // match our predicate, so we need to track their column so they can push out
+ // our alignment.
+ unsigned MinEmptyCaseColumn = 0;
+
+ // Start and end of the token sequence we're processing.
+ unsigned StartOfSequence = 0;
+ unsigned EndOfSequence = 0;
+
+ // Whether a matching token has been found on the current line.
+ bool FoundMatchOnLine = false;
+
+ bool LineIsComment = true;
+ bool LineIsEmptyCase = false;
+
+ unsigned I = 0;
+ for (unsigned E = Changes.size(); I != E; ++I) {
+ if (Changes[I].NewlinesBefore != 0) {
+ // Whether to break the alignment sequence because of an empty line.
+ bool EmptyLineBreak =
+ (Changes[I].NewlinesBefore > 1) &&
+ !Style.AlignConsecutiveShortCaseStatements.AcrossEmptyLines;
+
+ // Whether to break the alignment sequence because of a line without a
+ // match.
+ bool NoMatchBreak =
+ !FoundMatchOnLine &&
+ !(LineIsComment &&
+ Style.AlignConsecutiveShortCaseStatements.AcrossComments) &&
+ !LineIsEmptyCase;
+
+ if (EmptyLineBreak || NoMatchBreak) {
+ AlignMatchingTokenSequence(StartOfSequence, EndOfSequence, MinColumn,
+ Matches, Changes);
+ MinEmptyCaseColumn = 0;
+ }
+
+ // A new line starts, re-initialize line status tracking bools.
+ FoundMatchOnLine = false;
+ LineIsComment = true;
+ LineIsEmptyCase = false;
+ }
+
+ if (Changes[I].Tok->isNot(tok::comment))
+ LineIsComment = false;
+
+ if (Changes[I].Tok->is(TT_CaseLabelColon)) {
+ LineIsEmptyCase =
+ !Changes[I].Tok->Next || Changes[I].Tok->Next->isTrailingComment();
+
+ if (LineIsEmptyCase) {
+ if (Style.AlignConsecutiveShortCaseStatements.AlignCaseColons) {
+ MinEmptyCaseColumn =
+ std::max(MinEmptyCaseColumn, Changes[I].StartOfTokenColumn);
+ } else {
+ MinEmptyCaseColumn =
+ std::max(MinEmptyCaseColumn, Changes[I].StartOfTokenColumn + 2);
+ }
+ }
+ }
+
+ if (!Matches(Changes[I]))
+ continue;
+
+ if (LineIsEmptyCase)
+ continue;
+
+ FoundMatchOnLine = true;
+
+ if (StartOfSequence == 0)
+ StartOfSequence = I;
+
+ EndOfSequence = I + 1;
+
+ MinColumn = std::max(MinColumn, Changes[I].StartOfTokenColumn);
+
+ // Allow empty case statements to push out our alignment.
+ MinColumn = std::max(MinColumn, MinEmptyCaseColumn);
+ }
+
+ AlignMatchingTokenSequence(StartOfSequence, EndOfSequence, MinColumn, Matches,
+ Changes);
+}
+
void WhitespaceManager::alignConsecutiveDeclarations() {
if (!Style.AlignConsecutiveDeclarations.Enabled)
return;
diff --git a/clang/lib/Format/WhitespaceManager.h b/clang/lib/Format/WhitespaceManager.h
index cadfc1e..df7e9ad 100644
--- a/clang/lib/Format/WhitespaceManager.h
+++ b/clang/lib/Format/WhitespaceManager.h
@@ -232,6 +232,9 @@ private:
/// Align consecutive declarations over all \c Changes.
void alignChainedConditionals();
+ /// Align consecutive short case statements over all \c Changes.
+ void alignConsecutiveShortCaseStatements();
+
/// Align trailing comments over all \c Changes.
void alignTrailingComments();
diff --git a/clang/unittests/Format/ConfigParseTest.cpp b/clang/unittests/Format/ConfigParseTest.cpp
index 7903db1..e979aed 100644
--- a/clang/unittests/Format/ConfigParseTest.cpp
+++ b/clang/unittests/Format/ConfigParseTest.cpp
@@ -195,6 +195,11 @@ TEST(ConfigParseTest, ParsesConfigurationBools) {
CHECK_PARSE_BOOL(SpaceBeforeSquareBrackets);
CHECK_PARSE_BOOL(VerilogBreakBetweenInstancePorts);
+ CHECK_PARSE_NESTED_BOOL(AlignConsecutiveShortCaseStatements, Enabled);
+ CHECK_PARSE_NESTED_BOOL(AlignConsecutiveShortCaseStatements,
+ AcrossEmptyLines);
+ CHECK_PARSE_NESTED_BOOL(AlignConsecutiveShortCaseStatements, AcrossComments);
+ CHECK_PARSE_NESTED_BOOL(AlignConsecutiveShortCaseStatements, AlignCaseColons);
CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterCaseLabel);
CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterClass);
CHECK_PARSE_NESTED_BOOL(BraceWrapping, AfterEnum);
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 3e24a99..ebf1d75 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -19261,6 +19261,240 @@ TEST_F(FormatTest, AlignConsecutiveDeclarations) {
BracedAlign);
}
+TEST_F(FormatTest, AlignConsecutiveShortCaseStatements) {
+ FormatStyle Alignment = getLLVMStyle();
+ Alignment.AllowShortCaseLabelsOnASingleLine = true;
+ Alignment.AlignConsecutiveShortCaseStatements.Enabled = true;
+
+ verifyFormat("switch (level) {\n"
+ "case log::info: return \"info\";\n"
+ "case log::warning: return \"warning\";\n"
+ "default: return \"default\";\n"
+ "}",
+ Alignment);
+
+ verifyFormat("switch (level) {\n"
+ "case log::info: return \"info\";\n"
+ "case log::warning: return \"warning\";\n"
+ "}",
+ "switch (level) {\n"
+ "case log::info: return \"info\";\n"
+ "case log::warning:\n"
+ " return \"warning\";\n"
+ "}",
+ Alignment);
+
+ // Empty case statements push out the alignment, but non-short case labels
+ // don't.
+ verifyFormat("switch (level) {\n"
+ "case log::info: return \"info\";\n"
+ "case log::critical:\n"
+ "case log::warning:\n"
+ "case log::severe: return \"severe\";\n"
+ "case log::extra_severe:\n"
+ " // comment\n"
+ " return \"extra_severe\";\n"
+ "}",
+ Alignment);
+
+ // Verify comments and empty lines break the alignment.
+ verifyNoChange("switch (level) {\n"
+ "case log::info: return \"info\";\n"
+ "case log::warning: return \"warning\";\n"
+ "// comment\n"
+ "case log::critical: return \"critical\";\n"
+ "default: return \"default\";\n"
+ "\n"
+ "case log::severe: return \"severe\";\n"
+ "}",
+ Alignment);
+
+ // Empty case statements don't break the alignment, and potentially push it
+ // out.
+ verifyFormat("switch (level) {\n"
+ "case log::info: return \"info\";\n"
+ "case log::warning:\n"
+ "case log::critical:\n"
+ "default: return \"default\";\n"
+ "}",
+ Alignment);
+
+ // Implicit fallthrough cases can be aligned with either a comment or
+ // [[fallthrough]]
+ verifyFormat("switch (level) {\n"
+ "case log::info: return \"info\";\n"
+ "case log::warning: // fallthrough\n"
+ "case log::error: return \"error\";\n"
+ "case log::critical: /*fallthrough*/\n"
+ "case log::severe: return \"severe\";\n"
+ "case log::diag: [[fallthrough]];\n"
+ "default: return \"default\";\n"
+ "}",
+ Alignment);
+
+ // Verify trailing comment that needs a reflow also gets aligned properly.
+ verifyFormat("switch (level) {\n"
+ "case log::info: return \"info\";\n"
+ "case log::warning: // fallthrough\n"
+ "case log::error: return \"error\";\n"
+ "}",
+ "switch (level) {\n"
+ "case log::info: return \"info\";\n"
+ "case log::warning: //fallthrough\n"
+ "case log::error: return \"error\";\n"
+ "}",
+ Alignment);
+
+ // Verify adjacent non-short case statements don't change the alignment, and
+ // properly break the set of consecutive statements.
+ verifyFormat("switch (level) {\n"
+ "case log::critical:\n"
+ " // comment\n"
+ " return \"critical\";\n"
+ "case log::info: return \"info\";\n"
+ "case log::warning: return \"warning\";\n"
+ "default:\n"
+ " // comment\n"
+ " return \"\";\n"
+ "case log::error: return \"error\";\n"
+ "case log::severe: return \"severe\";\n"
+ "case log::extra_critical:\n"
+ " // comment\n"
+ " return \"extra critical\";\n"
+ "}",
+ Alignment);
+
+ Alignment.SpaceBeforeCaseColon = true;
+ verifyFormat("switch (level) {\n"
+ "case log::info : return \"info\";\n"
+ "case log::warning : return \"warning\";\n"
+ "default : return \"default\";\n"
+ "}",
+ Alignment);
+ Alignment.SpaceBeforeCaseColon = false;
+
+ // Make sure we don't incorrectly align correctly across nested switch cases.
+ verifyFormat("switch (level) {\n"
+ "case log::info: return \"info\";\n"
+ "case log::warning: return \"warning\";\n"
+ "case log::other:\n"
+ " switch (sublevel) {\n"
+ " case log::info: return \"info\";\n"
+ " case log::warning: return \"warning\";\n"
+ " }\n"
+ " break;\n"
+ "case log::error: return \"error\";\n"
+ "default: return \"default\";\n"
+ "}",
+ "switch (level) {\n"
+ "case log::info: return \"info\";\n"
+ "case log::warning: return \"warning\";\n"
+ "case log::other: switch (sublevel) {\n"
+ " case log::info: return \"info\";\n"
+ " case log::warning: return \"warning\";\n"
+ "}\n"
+ "break;\n"
+ "case log::error: return \"error\";\n"
+ "default: return \"default\";\n"
+ "}",
+ Alignment);
+
+ Alignment.AlignConsecutiveShortCaseStatements.AcrossEmptyLines = true;
+
+ verifyFormat("switch (level) {\n"
+ "case log::info: return \"info\";\n"
+ "\n"
+ "case log::warning: return \"warning\";\n"
+ "}",
+ "switch (level) {\n"
+ "case log::info: return \"info\";\n"
+ "\n"
+ "case log::warning: return \"warning\";\n"
+ "}",
+ Alignment);
+
+ Alignment.AlignConsecutiveShortCaseStatements.AcrossComments = true;
+
+ verifyNoChange("switch (level) {\n"
+ "case log::info: return \"info\";\n"
+ "\n"
+ "/* block comment */\n"
+ "\n"
+ "// line comment\n"
+ "case log::warning: return \"warning\";\n"
+ "}",
+ Alignment);
+
+ Alignment.AlignConsecutiveShortCaseStatements.AcrossEmptyLines = false;
+
+ verifyFormat("switch (level) {\n"
+ "case log::info: return \"info\";\n"
+ "//\n"
+ "case log::warning: return \"warning\";\n"
+ "}",
+ Alignment);
+
+ Alignment.AlignConsecutiveShortCaseStatements.AlignCaseColons = true;
+
+ verifyFormat("switch (level) {\n"
+ "case log::info : return \"info\";\n"
+ "case log::warning: return \"warning\";\n"
+ "default : return \"default\";\n"
+ "}",
+ Alignment);
+
+ // With AlignCaseColons, empty case statements don't break alignment of
+ // consecutive case statements (and are aligned).
+ verifyFormat("switch (level) {\n"
+ "case log::info : return \"info\";\n"
+ "case log::warning :\n"
+ "case log::critical:\n"
+ "default : return \"default\";\n"
+ "}",
+ Alignment);
+
+ // Final non-short case labels shouldn't have their colon aligned
+ verifyFormat("switch (level) {\n"
+ "case log::info : return \"info\";\n"
+ "case log::warning :\n"
+ "case log::critical:\n"
+ "case log::severe : return \"severe\";\n"
+ "default:\n"
+ " // comment\n"
+ " return \"default\";\n"
+ "}",
+ Alignment);
+
+ // Verify adjacent non-short case statements break the set of consecutive
+ // alignments and aren't aligned with adjacent non-short case statements if
+ // AlignCaseColons is set.
+ verifyFormat("switch (level) {\n"
+ "case log::critical:\n"
+ " // comment\n"
+ " return \"critical\";\n"
+ "case log::info : return \"info\";\n"
+ "case log::warning: return \"warning\";\n"
+ "default:\n"
+ " // comment\n"
+ " return \"\";\n"
+ "case log::error : return \"error\";\n"
+ "case log::severe: return \"severe\";\n"
+ "case log::extra_critical:\n"
+ " // comment\n"
+ " return \"extra critical\";\n"
+ "}",
+ Alignment);
+
+ Alignment.SpaceBeforeCaseColon = true;
+ verifyFormat("switch (level) {\n"
+ "case log::info : return \"info\";\n"
+ "case log::warning : return \"warning\";\n"
+ "case log::error :\n"
+ "default : return \"default\";\n"
+ "}",
+ Alignment);
+}
+
TEST_F(FormatTest, AlignWithLineBreaks) {
auto Style = getLLVMStyleWithColumns(120);