[clang] 9aab0db - [clang-format] Improve require and concept handling
Björn Schäpers via cfe-commits
cfe-commits at lists.llvm.org
Fri Feb 11 13:43:18 PST 2022
Author: Björn Schäpers
Date: 2022-02-11T22:42:37+01:00
New Revision: 9aab0db13fb6d21d1b70247a9b5e4cf916ee1c3a
URL: https://github.com/llvm/llvm-project/commit/9aab0db13fb6d21d1b70247a9b5e4cf916ee1c3a
DIFF: https://github.com/llvm/llvm-project/commit/9aab0db13fb6d21d1b70247a9b5e4cf916ee1c3a.diff
LOG: [clang-format] Improve require and concept handling
- Added an option where to put the requires clauses.
- Renamed IndentRequires to IndentRequiresClause.
- Changed BreakBeforeConceptDeclaration from bool to an enum.
Fixes https://llvm.org/PR32165, and https://llvm.org/PR52401.
Differential Revision: https://reviews.llvm.org/D113319
Added:
Modified:
clang/docs/ClangFormatStyleOptions.rst
clang/docs/ReleaseNotes.rst
clang/include/clang/Format/Format.h
clang/lib/Format/ContinuationIndenter.cpp
clang/lib/Format/Format.cpp
clang/lib/Format/FormatToken.h
clang/lib/Format/TokenAnnotator.cpp
clang/lib/Format/UnwrappedLineParser.cpp
clang/lib/Format/UnwrappedLineParser.h
clang/unittests/Format/FormatTest.cpp
clang/unittests/Format/TokenAnnotatorTest.cpp
Removed:
################################################################################
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index d610c19faf2b6..e89523d0e5676 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -1988,17 +1988,33 @@ the configuration (without a prefix: ``Auto``).
-**BreakBeforeConceptDeclarations** (``Boolean``) :versionbadge:`clang-format 13`
- If ``true``, concept will be placed on a new line.
+**BreakBeforeConceptDeclarations** (``BreakBeforeConceptDeclarationsStyle``) :versionbadge:`clang-format 13`
+ The concept declaration style to use.
- .. code-block:: c++
+ Possible values:
+
+ * ``BBCDS_Never`` (in configuration: ``Never``)
+ Keep the template declaration line together with ``concept``.
+
+ .. code-block:: c++
+
+ template <typename T> concept C = ...;
+
+ * ``BBCDS_Allowed`` (in configuration: ``Allowed``)
+ Breaking between template declaration and ``concept`` is allowed. The
+ actual behavior depends on the content and line breaking rules and
+ penalities.
+
+ * ``BBCDS_Always`` (in configuration: ``Always``)
+ Always break before ``concept``, putting it in the line after the
+ template declaration.
+
+ .. code-block:: c++
+
+ template <typename T>
+ concept C = ...;
- true:
- template<typename T>
- concept ...
- false:
- template<typename T> concept ...
**BreakBeforeTernaryOperators** (``Boolean``) :versionbadge:`clang-format 3.7`
If ``true``, ternary operators will be placed after line breaks.
@@ -2690,8 +2706,9 @@ the configuration (without a prefix: ``Auto``).
-**IndentRequires** (``Boolean``) :versionbadge:`clang-format 13`
- Indent the requires clause in a template
+**IndentRequiresClause** (``Boolean``) :versionbadge:`clang-format 13`
+ Indent the requires clause in a template. This only applies when
+ ``RequiresClausePosition`` is ``OwnLine``, or ``WithFollowing``.
.. code-block:: c++
@@ -3474,6 +3491,92 @@ the configuration (without a prefix: ``Auto``).
}
}
+**RequiresClausePosition** (``RequiresClausePositionStyle``) :versionbadge:`clang-format 15`
+ The position of the ``requires`` clause.
+
+ Possible values:
+
+ * ``RCPS_OwnLine`` (in configuration: ``OwnLine``)
+ Always put the ``requires`` clause on its own line.
+
+ .. code-block:: c++
+
+ template <typename T>
+ requires C<T>
+ struct Foo {...
+
+ template <typename T>
+ requires C<T>
+ void bar(T t) {...
+
+ template <typename T>
+ void baz(T t)
+ requires C<T>
+ {...
+
+ * ``RCPS_WithPreceding`` (in configuration: ``WithPreceding``)
+ Try to put the clause together with the preceding part of a declaration.
+ For class templates: stick to the template declaration.
+ For function templates: stick to the template declaration.
+ For function declaration followed by a requires clause: stick to the
+ parameter list.
+
+ .. code-block:: c++
+
+ template <typename T> requires C<T>
+ struct Foo {...
+
+ template <typename T> requires C<T>
+ void bar(T t) {...
+
+ template <typename T>
+ void baz(T t) requires C<T>
+ {...
+
+ * ``RCPS_WithFollowing`` (in configuration: ``WithFollowing``)
+ Try to put the ``requires`` clause together with the class or function
+ declaration.
+
+ .. code-block:: c++
+
+ template <typename T>
+ requires C<T> struct Foo {...
+
+ template <typename T>
+ requires C<T> void bar(T t) {...
+
+ template <typename T>
+ void baz(T t)
+ requires C<T> {...
+
+ * ``RCPS_SingleLine`` (in configuration: ``SingleLine``)
+ Try to put everything in the same line if possible. Otherwise normal
+ line breaking rules take over.
+
+ .. code-block:: c++
+
+ // Fitting:
+ template <typename T> requires C<T> struct Foo {...
+
+ template <typename T> requires C<T> void bar(T t) {...
+
+ template <typename T> void bar(T t) requires C<T> {...
+
+ // Not fitting, one possible example:
+ template <typename LongName>
+ requires C<LongName>
+ struct Foo {...
+
+ template <typename LongName>
+ requires C<LongName>
+ void bar(LongName ln) {
+
+ template <typename LongName>
+ void bar(LongName ln)
+ requires C<LongName> {
+
+
+
**SeparateDefinitionBlocks** (``SeparateDefinitionStyle``) :versionbadge:`clang-format 14`
Specifies the use of empty lines to separate definition blocks, including
classes, structs, enums, and functions.
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index ba8e028eb6aea..4a63959412678 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -167,6 +167,14 @@ AST Matchers
clang-format
------------
+- **Important change**: Renamed ``IndentRequires`` to ``IndentRequiresClause``
+ and changed the default for all styles from ``false`` to ``true``.
+
+- Reworked and improved handling of concepts and requires. Added the
+ ``RequiresClausePosition`` option as part of that.
+
+- Changed ``BreakBeforeConceptDeclarations`` from ``Boolean`` to an enum.
+
libclang
--------
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index d4e859f4decc8..9d6df403230da 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -1770,17 +1770,29 @@ struct FormatStyle {
/// \version 3.8
BraceWrappingFlags BraceWrapping;
- /// If ``true``, concept will be placed on a new line.
- /// \code
- /// true:
- /// template<typename T>
- /// concept ...
- ///
- /// false:
- /// template<typename T> concept ...
- /// \endcode
+ /// Different ways to break before concept declarations.
+ enum BreakBeforeConceptDeclarationsStyle {
+ /// Keep the template declaration line together with ``concept``.
+ /// \code
+ /// template <typename T> concept C = ...;
+ /// \endcode
+ BBCDS_Never,
+ /// Breaking between template declaration and ``concept`` is allowed. The
+ /// actual behavior depends on the content and line breaking rules and
+ /// penalities.
+ BBCDS_Allowed,
+ /// Always break before ``concept``, putting it in the line after the
+ /// template declaration.
+ /// \code
+ /// template <typename T>
+ /// concept C = ...;
+ /// \endcode
+ BBCDS_Always,
+ };
+
+ /// The concept declaration style to use.
/// \version 13
- bool BreakBeforeConceptDeclarations;
+ BreakBeforeConceptDeclarationsStyle BreakBeforeConceptDeclarations;
/// If ``true``, ternary operators will be placed after line breaks.
/// \code
@@ -2509,7 +2521,8 @@ struct FormatStyle {
/// \version 12
IndentExternBlockStyle IndentExternBlock;
- /// Indent the requires clause in a template
+ /// Indent the requires clause in a template. This only applies when
+ /// ``RequiresClausePosition`` is ``OwnLine``, or ``WithFollowing``.
/// \code
/// true:
/// template <typename It>
@@ -2526,7 +2539,7 @@ struct FormatStyle {
/// }
/// \endcode
/// \version 13
- bool IndentRequires;
+ bool IndentRequiresClause;
/// The number of columns to use for indentation.
/// \code
@@ -3116,6 +3129,87 @@ struct FormatStyle {
/// \version 14
bool RemoveBracesLLVM;
+ /// \brief The possible positions for the requires clause. The
+ /// ``IndentRequires`` option is only used if the ``requires`` is put on the
+ /// start of a line.
+ enum RequiresClausePositionStyle {
+ /// Always put the ``requires`` clause on its own line.
+ /// \code
+ /// template <typename T>
+ /// requires C<T>
+ /// struct Foo {...
+ ///
+ /// template <typename T>
+ /// requires C<T>
+ /// void bar(T t) {...
+ ///
+ /// template <typename T>
+ /// void baz(T t)
+ /// requires C<T>
+ /// {...
+ /// \endcode
+ RCPS_OwnLine,
+ /// Try to put the clause together with the preceding part of a declaration.
+ /// For class templates: stick to the template declaration.
+ /// For function templates: stick to the template declaration.
+ /// For function declaration followed by a requires clause: stick to the
+ /// parameter list.
+ /// \code
+ /// template <typename T> requires C<T>
+ /// struct Foo {...
+ ///
+ /// template <typename T> requires C<T>
+ /// void bar(T t) {...
+ ///
+ /// template <typename T>
+ /// void baz(T t) requires C<T>
+ /// {...
+ /// \endcode
+ RCPS_WithPreceding,
+ /// Try to put the ``requires`` clause together with the class or function
+ /// declaration.
+ /// \code
+ /// template <typename T>
+ /// requires C<T> struct Foo {...
+ ///
+ /// template <typename T>
+ /// requires C<T> void bar(T t) {...
+ ///
+ /// template <typename T>
+ /// void baz(T t)
+ /// requires C<T> {...
+ /// \endcode
+ RCPS_WithFollowing,
+ /// Try to put everything in the same line if possible. Otherwise normal
+ /// line breaking rules take over.
+ /// \code
+ /// // Fitting:
+ /// template <typename T> requires C<T> struct Foo {...
+ ///
+ /// template <typename T> requires C<T> void bar(T t) {...
+ ///
+ /// template <typename T> void bar(T t) requires C<T> {...
+ ///
+ /// // Not fitting, one possible example:
+ /// template <typename LongName>
+ /// requires C<LongName>
+ /// struct Foo {...
+ ///
+ /// template <typename LongName>
+ /// requires C<LongName>
+ /// void bar(LongName ln) {
+ ///
+ /// template <typename LongName>
+ /// void bar(LongName ln)
+ /// requires C<LongName> {
+ /// \endcode
+ RCPS_SingleLine,
+ };
+
+ /// \brief The position of the ``requires`` clause.
+ /// \version 15
+ RequiresClausePositionStyle RequiresClausePosition;
+
/// \brief The style if definition blocks should be separated.
enum SeparateDefinitionStyle {
/// Leave definition blocks as they are.
@@ -3889,8 +3983,8 @@ struct FormatStyle {
IndentGotoLabels == R.IndentGotoLabels &&
IndentPPDirectives == R.IndentPPDirectives &&
IndentExternBlock == R.IndentExternBlock &&
- IndentRequires == R.IndentRequires && IndentWidth == R.IndentWidth &&
- Language == R.Language &&
+ IndentRequiresClause == R.IndentRequiresClause &&
+ IndentWidth == R.IndentWidth && Language == R.Language &&
IndentWrappedFunctionNames == R.IndentWrappedFunctionNames &&
JavaImportGroups == R.JavaImportGroups &&
JavaScriptQuotes == R.JavaScriptQuotes &&
@@ -3926,6 +4020,7 @@ struct FormatStyle {
RawStringFormats == R.RawStringFormats &&
ReferenceAlignment == R.ReferenceAlignment &&
RemoveBracesLLVM == R.RemoveBracesLLVM &&
+ RequiresClausePosition == R.RequiresClausePosition &&
SeparateDefinitionBlocks == R.SeparateDefinitionBlocks &&
ShortNamespaceLines == R.ShortNamespaceLines &&
SortIncludes == R.SortIncludes &&
diff --git a/clang/lib/Format/ContinuationIndenter.cpp b/clang/lib/Format/ContinuationIndenter.cpp
index 93d4091181287..42c3d2e4326b9 100644
--- a/clang/lib/Format/ContinuationIndenter.cpp
+++ b/clang/lib/Format/ContinuationIndenter.cpp
@@ -478,11 +478,32 @@ bool ContinuationIndenter::mustBreak(const LineState &State) {
return true;
if (Current.NestingLevel == 0 && !Current.isTrailingComment()) {
- // Always break after "template <...>" and leading annotations. This is only
- // for cases where the entire line does not fit on a single line as a
+ // Always break after "template <...>"(*) and leading annotations. This is
+ // only for cases where the entire line does not fit on a single line as a
//
diff erent LineFormatter would be used otherwise.
- if (Previous.ClosesTemplateDeclaration)
+ // *: Except when another option interferes with that, like concepts.
+ if (Previous.ClosesTemplateDeclaration) {
+ if (Current.is(tok::kw_concept)) {
+ switch (Style.BreakBeforeConceptDeclarations) {
+ case FormatStyle::BBCDS_Allowed:
+ break;
+ case FormatStyle::BBCDS_Always:
+ return true;
+ case FormatStyle::BBCDS_Never:
+ return false;
+ }
+ }
+ if (Current.is(TT_RequiresClause)) {
+ switch (Style.RequiresClausePosition) {
+ case FormatStyle::RCPS_SingleLine:
+ case FormatStyle::RCPS_WithPreceding:
+ return false;
+ default:
+ return true;
+ }
+ }
return Style.AlwaysBreakTemplateDeclarations != FormatStyle::BTDS_No;
+ }
if (Previous.is(TT_FunctionAnnotationRParen) &&
State.Line->Type != LT_PreprocessorDirective)
return true;
@@ -669,6 +690,7 @@ void ContinuationIndenter::addTokenOnCurrentLine(LineState &State, bool DryRun,
if (Style.AlignAfterOpenBracket != FormatStyle::BAS_DontAlign &&
!State.Stack.back().IsCSharpGenericTypeConstraint &&
Previous.opensScope() && Previous.isNot(TT_ObjCMethodExpr) &&
+ Previous.isNot(TT_RequiresClause) &&
(Current.isNot(TT_LineComment) || Previous.is(BK_BracedInit))) {
State.Stack.back().Indent = State.Column + Spaces;
State.Stack.back().IsAligned = true;
@@ -880,7 +902,8 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
Previous.is(TT_BinaryOperator))
State.Stack.back().BreakBeforeParameter = false;
if (PreviousNonComment &&
- PreviousNonComment->isOneOf(TT_TemplateCloser, TT_JavaAnnotation) &&
+ (PreviousNonComment->isOneOf(TT_TemplateCloser, TT_JavaAnnotation) ||
+ PreviousNonComment->ClosesRequiresClause) &&
Current.NestingLevel == 0)
State.Stack.back().BreakBeforeParameter = false;
if (NextNonComment->is(tok::question) ||
@@ -927,13 +950,19 @@ unsigned ContinuationIndenter::addTokenOnNewLine(LineState &State,
State.Stack[State.Stack.size() - 2].NestedBlockInlined) ||
(Style.Language == FormatStyle::LK_ObjC && Current.is(tok::r_brace) &&
State.Stack.size() > 1 && !Style.ObjCBreakBeforeNestedBlockParam);
+ // Do not force parameter break for statements with requires expressions.
+ NestedBlockSpecialCase =
+ NestedBlockSpecialCase ||
+ (Current.MatchingParen &&
+ Current.MatchingParen->is(TT_RequiresExpressionLBrace));
if (!NestedBlockSpecialCase)
for (unsigned i = 0, e = State.Stack.size() - 1; i != e; ++i)
State.Stack[i].BreakBeforeParameter = true;
if (PreviousNonComment &&
!PreviousNonComment->isOneOf(tok::comma, tok::colon, tok::semi) &&
- (PreviousNonComment->isNot(TT_TemplateCloser) ||
+ ((PreviousNonComment->isNot(TT_TemplateCloser) &&
+ !PreviousNonComment->ClosesRequiresClause) ||
Current.NestingLevel != 0) &&
!PreviousNonComment->isOneOf(
TT_BinaryOperator, TT_FunctionAnnotationRParen, TT_JavaAnnotation,
@@ -1096,8 +1125,20 @@ unsigned ContinuationIndenter::getNewLineColumn(const LineState &State) {
}
if (Previous.is(tok::comma) && State.Stack.back().VariablePos != 0)
return State.Stack.back().VariablePos;
+ if (Current.is(TT_RequiresClause)) {
+ if (Style.IndentRequiresClause)
+ return State.Stack.back().Indent + Style.IndentWidth;
+ switch (Style.RequiresClausePosition) {
+ case FormatStyle::RCPS_OwnLine:
+ case FormatStyle::RCPS_WithFollowing:
+ return State.Stack.back().Indent;
+ default:
+ break;
+ }
+ }
if ((PreviousNonComment &&
(PreviousNonComment->ClosesTemplateDeclaration ||
+ PreviousNonComment->ClosesRequiresClause ||
PreviousNonComment->isOneOf(
TT_AttributeParen, TT_AttributeSquare, TT_FunctionAnnotationRParen,
TT_JavaAnnotation, TT_LeadingJavaAnnotation))) ||
@@ -1288,6 +1329,8 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State,
State.Column + Current.ColumnWidth + 1;
if (Current.isOneOf(TT_LambdaLSquare, TT_LambdaArrow))
State.Stack.back().LastSpace = State.Column;
+ if (Current.is(TT_RequiresExpression))
+ State.Stack.back().NestedBlockIndent = State.Column;
// Insert scopes created by fake parenthesis.
const FormatToken *Previous = Current.getPreviousNonComment();
@@ -1298,8 +1341,8 @@ unsigned ContinuationIndenter::moveStateToNextToken(LineState &State,
// foo();
// bar();
// }, a, b, c);
- if (Current.isNot(tok::comment) && Previous &&
- Previous->isOneOf(tok::l_brace, TT_ArrayInitializerLSquare) &&
+ if (Current.isNot(tok::comment) && !Current.ClosesRequiresClause &&
+ Previous && Previous->isOneOf(tok::l_brace, TT_ArrayInitializerLSquare) &&
!Previous->is(TT_DictLiteral) && State.Stack.size() > 1 &&
!State.Stack.back().HasMultipleNestedBlocks) {
if (State.Stack[State.Stack.size() - 2].NestedBlockInlined && Newline)
@@ -1359,14 +1402,15 @@ void ContinuationIndenter::moveStatePastFakeLParens(LineState &State,
const FormatToken *Previous = Current.getPreviousNonComment();
// Don't add extra indentation for the first fake parenthesis after
- // 'return', assignments or opening <({[. The indentation for these cases
- // is special cased.
+ // 'return', assignments, opening <({[, or requires clauses. The indentation
+ // for these cases is special cased.
bool SkipFirstExtraIndent =
- (Previous && (Previous->opensScope() ||
- Previous->isOneOf(tok::semi, tok::kw_return) ||
- (Previous->getPrecedence() == prec::Assignment &&
- Style.AlignOperands != FormatStyle::OAS_DontAlign) ||
- Previous->is(TT_ObjCMethodExpr)));
+ Previous &&
+ (Previous->opensScope() ||
+ Previous->isOneOf(tok::semi, tok::kw_return, TT_RequiresClause) ||
+ (Previous->getPrecedence() == prec::Assignment &&
+ Style.AlignOperands != FormatStyle::OAS_DontAlign) ||
+ Previous->is(TT_ObjCMethodExpr));
for (const auto &PrecedenceLevel : llvm::reverse(Current.FakeLParens)) {
ParenState NewParenState = State.Stack.back();
NewParenState.Tok = nullptr;
@@ -1399,7 +1443,7 @@ void ContinuationIndenter::moveStatePastFakeLParens(LineState &State,
if (Previous &&
(Previous->getPrecedence() == prec::Assignment ||
- Previous->is(tok::kw_return) ||
+ Previous->isOneOf(tok::kw_return, TT_RequiresClause) ||
(PrecedenceLevel == prec::Conditional && Previous->is(tok::question) &&
Previous->is(TT_ConditionalExpr))) &&
!Newline) {
@@ -1457,6 +1501,12 @@ void ContinuationIndenter::moveStatePastFakeRParens(LineState &State) {
State.Stack.pop_back();
State.Stack.back().VariablePos = VariablePos;
}
+
+ if (State.NextToken->ClosesRequiresClause && Style.IndentRequiresClause) {
+ // Remove the indentation of the requires clauses (which is not in Indent,
+ // but in LastSpace).
+ State.Stack.back().LastSpace -= Style.IndentWidth;
+ }
}
void ContinuationIndenter::moveStatePastScopeOpener(LineState &State,
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 551d8cfe7ec17..8aa12867c35f1 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -257,6 +257,21 @@ struct ScalarEnumerationTraits<
}
};
+template <>
+struct ScalarEnumerationTraits<
+ FormatStyle::BreakBeforeConceptDeclarationsStyle> {
+ static void
+ enumeration(IO &IO, FormatStyle::BreakBeforeConceptDeclarationsStyle &Value) {
+ IO.enumCase(Value, "Never", FormatStyle::BBCDS_Never);
+ IO.enumCase(Value, "Allowed", FormatStyle::BBCDS_Allowed);
+ IO.enumCase(Value, "Always", FormatStyle::BBCDS_Always);
+
+ // For backward compatibility.
+ IO.enumCase(Value, "true", FormatStyle::BBCDS_Always);
+ IO.enumCase(Value, "false", FormatStyle::BBCDS_Allowed);
+ }
+};
+
template <>
struct ScalarEnumerationTraits<FormatStyle::BreakConstructorInitializersStyle> {
static void
@@ -463,6 +478,17 @@ struct ScalarEnumerationTraits<FormatStyle::ReferenceAlignmentStyle> {
}
};
+template <>
+struct ScalarEnumerationTraits<FormatStyle::RequiresClausePositionStyle> {
+ static void enumeration(IO &IO,
+ FormatStyle::RequiresClausePositionStyle &Value) {
+ IO.enumCase(Value, "OwnLine", FormatStyle::RCPS_OwnLine);
+ IO.enumCase(Value, "WithPreceding", FormatStyle::RCPS_WithPreceding);
+ IO.enumCase(Value, "WithFollowing", FormatStyle::RCPS_WithFollowing);
+ IO.enumCase(Value, "SingleLine", FormatStyle::RCPS_SingleLine);
+ }
+};
+
template <>
struct ScalarEnumerationTraits<FormatStyle::SpaceBeforeParensStyle> {
static void enumeration(IO &IO, FormatStyle::SpaceBeforeParensStyle &Value) {
@@ -565,6 +591,7 @@ template <> struct MappingTraits<FormatStyle> {
IO.mapOptional("DerivePointerBinding", Style.DerivePointerAlignment);
IO.mapOptional("IndentFunctionDeclarationAfterType",
Style.IndentWrappedFunctionNames);
+ IO.mapOptional("IndentRequires", Style.IndentRequiresClause);
IO.mapOptional("PointerBindsToType", Style.PointerAlignment);
IO.mapOptional("SpaceAfterControlStatementKeyword",
Style.SpaceBeforeParens);
@@ -737,7 +764,7 @@ template <> struct MappingTraits<FormatStyle> {
IO.mapOptional("IndentGotoLabels", Style.IndentGotoLabels);
IO.mapOptional("IndentPPDirectives", Style.IndentPPDirectives);
IO.mapOptional("IndentExternBlock", Style.IndentExternBlock);
- IO.mapOptional("IndentRequires", Style.IndentRequires);
+ IO.mapOptional("IndentRequiresClause", Style.IndentRequiresClause);
IO.mapOptional("IndentWidth", Style.IndentWidth);
IO.mapOptional("IndentWrappedFunctionNames",
Style.IndentWrappedFunctionNames);
@@ -782,6 +809,7 @@ template <> struct MappingTraits<FormatStyle> {
IO.mapOptional("ReferenceAlignment", Style.ReferenceAlignment);
IO.mapOptional("ReflowComments", Style.ReflowComments);
IO.mapOptional("RemoveBracesLLVM", Style.RemoveBracesLLVM);
+ IO.mapOptional("RequiresClausePosition", Style.RequiresClausePosition);
IO.mapOptional("SeparateDefinitionBlocks", Style.SeparateDefinitionBlocks);
IO.mapOptional("ShortNamespaceLines", Style.ShortNamespaceLines);
IO.mapOptional("SortIncludes", Style.SortIncludes);
@@ -1130,7 +1158,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.BinPackArguments = true;
LLVMStyle.BinPackParameters = true;
LLVMStyle.BreakBeforeBinaryOperators = FormatStyle::BOS_None;
- LLVMStyle.BreakBeforeConceptDeclarations = true;
+ LLVMStyle.BreakBeforeConceptDeclarations = FormatStyle::BBCDS_Always;
LLVMStyle.BreakBeforeTernaryOperators = true;
LLVMStyle.BreakBeforeBraces = FormatStyle::BS_Attach;
LLVMStyle.BraceWrapping = {/*AfterCaseLabel=*/false,
@@ -1188,7 +1216,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.IndentCaseBlocks = false;
LLVMStyle.IndentGotoLabels = true;
LLVMStyle.IndentPPDirectives = FormatStyle::PPDIS_None;
- LLVMStyle.IndentRequires = false;
+ LLVMStyle.IndentRequiresClause = true;
LLVMStyle.IndentWrappedFunctionNames = false;
LLVMStyle.IndentWidth = 2;
LLVMStyle.PPIndentWidth = -1;
@@ -1207,6 +1235,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.ObjCSpaceBeforeProtocolList = true;
LLVMStyle.PointerAlignment = FormatStyle::PAS_Right;
LLVMStyle.ReferenceAlignment = FormatStyle::RAS_Pointer;
+ LLVMStyle.RequiresClausePosition = FormatStyle::RCPS_OwnLine;
LLVMStyle.SeparateDefinitionBlocks = FormatStyle::SDS_Leave;
LLVMStyle.ShortNamespaceLines = 1;
LLVMStyle.SpacesBeforeTrailingComments = 1;
@@ -3048,6 +3077,15 @@ reformat(const FormatStyle &Style, StringRef Code,
FormatStyle Expanded = Style;
expandPresetsBraceWrapping(Expanded);
expandPresetsSpaceBeforeParens(Expanded);
+ switch (Expanded.RequiresClausePosition) {
+ case FormatStyle::RCPS_SingleLine:
+ case FormatStyle::RCPS_WithPreceding:
+ Expanded.IndentRequiresClause = false;
+ break;
+ default:
+ break;
+ }
+
if (Expanded.DisableFormat)
return {tooling::Replacements(), 0};
if (isLikelyXml(Code))
diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h
index 4c03f436dde3e..fee365ecc8f91 100644
--- a/clang/lib/Format/FormatToken.h
+++ b/clang/lib/Format/FormatToken.h
@@ -35,12 +35,13 @@ namespace format {
TYPE(BinaryOperator) \
TYPE(BitFieldColon) \
TYPE(BlockComment) \
+ TYPE(BracedListLBrace) \
TYPE(CastRParen) \
+ TYPE(CompoundRequirementLBrace) \
TYPE(ConditionalExpr) \
TYPE(ConflictAlternative) \
TYPE(ConflictEnd) \
TYPE(ConflictStart) \
- TYPE(ConstraintJunctions) \
TYPE(CtorInitializerColon) \
TYPE(CtorInitializerComma) \
TYPE(DesignatedInitializerLSquare) \
@@ -98,6 +99,11 @@ namespace format {
TYPE(RangeBasedForLoopColon) \
TYPE(RecordLBrace) \
TYPE(RegexLiteral) \
+ TYPE(RequiresClause) \
+ TYPE(RequiresClauseInARequiresExpression) \
+ TYPE(RequiresExpression) \
+ TYPE(RequiresExpressionLBrace) \
+ TYPE(RequiresExpressionLParen) \
TYPE(SelectorName) \
TYPE(StartOfName) \
TYPE(StatementAttributeLikeMacro) \
@@ -245,8 +251,9 @@ struct FormatToken {
CanBreakBefore(false), ClosesTemplateDeclaration(false),
StartsBinaryExpression(false), EndsBinaryExpression(false),
PartOfMultiVariableDeclStmt(false), ContinuesLineCommentSection(false),
- Finalized(false), BlockKind(BK_Unknown), Decision(FD_Unformatted),
- PackingKind(PPK_Inconclusive), Type(TT_Unknown) {}
+ Finalized(false), ClosesRequiresClause(false), BlockKind(BK_Unknown),
+ Decision(FD_Unformatted), PackingKind(PPK_Inconclusive),
+ Type(TT_Unknown) {}
/// The \c Token.
Token Tok;
@@ -312,6 +319,9 @@ struct FormatToken {
/// changes.
unsigned Finalized : 1;
+ /// \c true if this is the last token within requires clause.
+ unsigned ClosesRequiresClause : 1;
+
private:
/// Contains the kind of block if this token is a brace.
unsigned BlockKind : 2;
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 70f92c26fa8d5..dabecbf9c74ab 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -1019,7 +1019,7 @@ class AnnotatingParser {
return false;
if (Line.MustBeDeclaration && Contexts.size() == 1 &&
!Contexts.back().IsExpression && !Line.startsWith(TT_ObjCProperty) &&
- !Tok->is(TT_TypeDeclarationParen) &&
+ !Tok->isOneOf(TT_TypeDeclarationParen, TT_RequiresExpressionLParen) &&
(!Tok->Previous || !Tok->Previous->isOneOf(tok::kw___attribute,
TT_LeadingJavaAnnotation)))
Line.MightBeFunctionDecl = true;
@@ -1152,6 +1152,10 @@ class AnnotatingParser {
parseCSharpGenericTypeConstraint();
}
break;
+ case tok::arrow:
+ if (Tok->Previous && Tok->Previous->is(tok::kw_noexcept))
+ Tok->setType(TT_TrailingReturnArrow);
+ break;
default:
break;
}
@@ -1412,9 +1416,12 @@ class AnnotatingParser {
TT_ImplicitStringLiteral, TT_InlineASMBrace, TT_FatArrow,
TT_LambdaArrow, TT_NamespaceMacro, TT_OverloadedOperator,
TT_RegexLiteral, TT_TemplateString, TT_ObjCStringLiteral,
- TT_UntouchableMacroFunc, TT_ConstraintJunctions,
- TT_StatementAttributeLikeMacro, TT_FunctionLikeOrFreestandingMacro,
- TT_RecordLBrace))
+ TT_UntouchableMacroFunc, TT_StatementAttributeLikeMacro,
+ TT_FunctionLikeOrFreestandingMacro, TT_RecordLBrace,
+ TT_RequiresClause, TT_RequiresClauseInARequiresExpression,
+ TT_RequiresExpression, TT_RequiresExpressionLParen,
+ TT_RequiresExpressionLBrace, TT_BinaryOperator,
+ TT_CompoundRequirementLBrace, TT_BracedListLBrace))
CurrentToken->setType(TT_Unknown);
CurrentToken->Role.reset();
CurrentToken->MatchingParen = nullptr;
@@ -1609,7 +1616,8 @@ class AnnotatingParser {
PriorLeadingIdentifier = PriorLeadingIdentifier->Previous;
return (PriorLeadingIdentifier &&
- PriorLeadingIdentifier->is(TT_TemplateCloser) &&
+ (PriorLeadingIdentifier->is(TT_TemplateCloser) ||
+ PriorLeadingIdentifier->ClosesRequiresClause) &&
LeadingIdentifier->TokenText == Current.Next->TokenText);
}
}
@@ -1826,6 +1834,9 @@ class AnnotatingParser {
if (!PreviousNotConst)
return false;
+ if (PreviousNotConst->ClosesRequiresClause)
+ return false;
+
bool IsPPKeyword = PreviousNotConst->is(tok::identifier) &&
PreviousNotConst->Previous &&
PreviousNotConst->Previous->is(tok::hash);
@@ -2164,7 +2175,7 @@ class ExpressionParser {
public:
ExpressionParser(const FormatStyle &Style, const AdditionalKeywords &Keywords,
AnnotatedLine &Line)
- : Style(Style), Keywords(Keywords), Current(Line.First) {}
+ : Style(Style), Keywords(Keywords), Line(Line), Current(Line.First) {}
/// Parse expressions with the given operator precedence.
void parse(int Precedence = 0) {
@@ -2219,7 +2230,11 @@ class ExpressionParser {
break;
// Consume scopes: (), [], <> and {}
- if (Current->opensScope()) {
+ // In addition to that we handle require clauses as scope, so that the
+ // constraints in that are correctly indented.
+ if (Current->opensScope() ||
+ Current->isOneOf(TT_RequiresClause,
+ TT_RequiresClauseInARequiresExpression)) {
// In fragment of a JavaScript template string can look like '}..${' and
// thus close a scope and open a new one at the same time.
while (Current && (!Current->closesScope() || Current->opensScope())) {
@@ -2241,12 +2256,26 @@ class ExpressionParser {
}
if (LatestOperator && (Current || Precedence > 0)) {
- // LatestOperator->LastOperator = true;
+ // The requires clauses do not neccessarily end in a semicolon or a brace,
+ // but just go over to struct/class or a function declaration, we need to
+ // intervene so that the fake right paren is inserted correctly.
+ auto End =
+ (Start->Previous &&
+ Start->Previous->isOneOf(TT_RequiresClause,
+ TT_RequiresClauseInARequiresExpression))
+ ? [this](){
+ auto Ret = Current ? Current : Line.Last;
+ while (!Ret->ClosesRequiresClause && Ret->Previous)
+ Ret = Ret->Previous;
+ return Ret;
+ }()
+ : nullptr;
+
if (Precedence == PrecedenceArrowAndPeriod) {
// Call expressions don't have a binary operator precedence.
- addFakeParenthesis(Start, prec::Unknown);
+ addFakeParenthesis(Start, prec::Unknown, End);
} else {
- addFakeParenthesis(Start, prec::Level(Precedence));
+ addFakeParenthesis(Start, prec::Level(Precedence), End);
}
}
}
@@ -2295,17 +2324,17 @@ class ExpressionParser {
return -1;
}
- void addFakeParenthesis(FormatToken *Start, prec::Level Precedence) {
+ void addFakeParenthesis(FormatToken *Start, prec::Level Precedence,
+ FormatToken *End = nullptr) {
Start->FakeLParens.push_back(Precedence);
if (Precedence > prec::Unknown)
Start->StartsBinaryExpression = true;
- if (Current) {
- FormatToken *Previous = Current->Previous;
- while (Previous->is(tok::comment) && Previous->Previous)
- Previous = Previous->Previous;
- ++Previous->FakeRParens;
+ if (!End && Current)
+ End = Current->getPreviousNonComment();
+ if (End) {
+ ++End->FakeRParens;
if (Precedence > prec::Unknown)
- Previous->EndsBinaryExpression = true;
+ End->EndsBinaryExpression = true;
}
}
@@ -2350,6 +2379,7 @@ class ExpressionParser {
const FormatStyle &Style;
const AdditionalKeywords &Keywords;
+ const AnnotatedLine &Line;
FormatToken *Current;
};
@@ -2920,6 +2950,8 @@ unsigned TokenAnnotator::splitPenalty(const AnnotatedLine &Line,
}
if (Left.ClosesTemplateDeclaration)
return Style.PenaltyBreakTemplateDeclaration;
+ if (Left.ClosesRequiresClause)
+ return 0;
if (Left.is(TT_ConditionalExpr))
return prec::Conditional;
prec::Level Level = Left.getPrecedence();
@@ -2987,9 +3019,6 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
if (Left.isOneOf(tok::kw_co_await, tok::kw_co_yield, tok::kw_co_return) &&
Right.isNot(tok::semi))
return true;
- // requires clause Concept1<T> && Concept2<T>
- if (Left.is(TT_ConstraintJunctions) && Right.is(tok::identifier))
- return true;
if (Left.is(tok::l_paren) || Right.is(tok::r_paren))
return (Right.is(TT_CastRParen) ||
@@ -3892,6 +3921,15 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
if (Right.is(tok::lessless) && Right.Next && Left.is(tok::string_literal) &&
Right.Next->is(tok::string_literal))
return true;
+ if (Right.is(TT_RequiresClause)) {
+ switch (Style.RequiresClausePosition) {
+ case FormatStyle::RCPS_OwnLine:
+ case FormatStyle::RCPS_WithFollowing:
+ return true;
+ default:
+ break;
+ }
+ }
// Can break after template<> declaration
if (Left.ClosesTemplateDeclaration && Left.MatchingParen &&
Left.MatchingParen->NestingLevel == 0) {
@@ -3899,9 +3937,18 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
// template<typename T>
// concept ...
if (Right.is(tok::kw_concept))
- return Style.BreakBeforeConceptDeclarations;
+ return Style.BreakBeforeConceptDeclarations == FormatStyle::BBCDS_Always;
return (Style.AlwaysBreakTemplateDeclarations == FormatStyle::BTDS_Yes);
}
+ if (Left.ClosesRequiresClause) {
+ switch (Style.RequiresClausePosition) {
+ case FormatStyle::RCPS_OwnLine:
+ case FormatStyle::RCPS_WithPreceding:
+ return true;
+ default:
+ break;
+ }
+ }
if (Style.PackConstructorInitializers == FormatStyle::PCIS_Never) {
if (Style.BreakConstructorInitializers == FormatStyle::BCIS_BeforeColon &&
(Left.is(TT_CtorInitializerComma) || Right.is(TT_CtorInitializerColon)))
@@ -4296,8 +4343,14 @@ bool TokenAnnotator::canBreakBefore(const AnnotatedLine &Line,
return Left.isNot(tok::period); // FIXME: Properly parse ObjC calls.
if (Left.is(tok::r_paren) && Line.Type == LT_ObjCProperty)
return true;
+ if (Right.is(tok::kw_concept))
+ return Style.BreakBeforeConceptDeclarations != FormatStyle::BBCDS_Never;
+ if (Right.is(TT_RequiresClause))
+ return true;
if (Left.ClosesTemplateDeclaration || Left.is(TT_FunctionAnnotationRParen))
return true;
+ if (Left.ClosesRequiresClause)
+ return true;
if (Right.isOneOf(TT_RangeBasedForLoopColon, TT_OverloadedOperatorLParen,
TT_OverloadedOperator))
return false;
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index 0686aeb253ad5..e2d5197988be6 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -369,7 +369,7 @@ void UnwrappedLineParser::parseFile() {
if (Style.Language == FormatStyle::LK_TextProto)
parseBracedList();
else
- parseLevel(/*HasOpeningBrace=*/false);
+ parseLevel(/*HasOpeningBrace=*/false, /*CanContainBracedList=*/true);
// Make sure to format the remaining tokens.
//
// LK_TextProto is special since its top-level is parsed as the body of a
@@ -436,10 +436,20 @@ bool UnwrappedLineParser::precededByCommentOrPPDirective() const {
return Previous && Previous->is(tok::comment) &&
(Previous->IsMultiline || Previous->NewlinesBefore > 0);
}
-
-// Returns true if a simple block, or false otherwise. (A simple block has a
-// single statement.)
-bool UnwrappedLineParser::parseLevel(bool HasOpeningBrace, IfStmtKind *IfKind) {
+/// \brief Parses a level, that is ???.
+/// \param HasOpeningBrace If that level is started by an opening brace.
+/// \param CanContainBracedList If the content can contain (at any level) a
+/// braced list.
+/// \param NextLBracesType The type for left brace found in this level.
+/// \returns true if a simple block, or false otherwise. (A simple block has a
+/// single statement.)
+bool UnwrappedLineParser::parseLevel(bool HasOpeningBrace,
+ bool CanContainBracedList,
+ IfStmtKind *IfKind,
+ TokenType NextLBracesType) {
+ auto NextLevelLBracesType = NextLBracesType == TT_CompoundRequirementLBrace
+ ? TT_BracedListLBrace
+ : TT_Unknown;
const bool IsPrecededByCommentOrPPDirective =
!Style.RemoveBracesLLVM || precededByCommentOrPPDirective();
unsigned StatementCount = 0;
@@ -451,17 +461,36 @@ bool UnwrappedLineParser::parseLevel(bool HasOpeningBrace, IfStmtKind *IfKind) {
else if (FormatTok->getType() == TT_MacroBlockEnd)
kind = tok::r_brace;
+ auto ParseDefault = [this, HasOpeningBrace, IfKind, NextLevelLBracesType,
+ &StatementCount] {
+ parseStructuralElement(IfKind, /*IsTopLevel=*/!HasOpeningBrace,
+ /*NextLBracesType=*/NextLevelLBracesType);
+ ++StatementCount;
+ assert(StatementCount > 0 && "StatementCount overflow!");
+ };
+
switch (kind) {
case tok::comment:
nextToken();
addUnwrappedLine();
break;
case tok::l_brace:
- // FIXME: Add parameter whether this can happen - if this happens, we must
- // be in a non-declaration context.
- if (!FormatTok->is(TT_MacroBlockBegin) && tryToParseBracedList())
+ if (NextLBracesType != TT_Unknown)
+ FormatTok->setType(NextLBracesType);
+ else if (FormatTok->Previous &&
+ FormatTok->Previous->ClosesRequiresClause) {
+ // We need the 'default' case here to correctly parse a function
+ // l_brace.
+ ParseDefault();
continue;
- parseBlock();
+ }
+ if (CanContainBracedList && !FormatTok->is(TT_MacroBlockBegin) &&
+ tryToParseBracedList())
+ continue;
+ parseBlock(/*MustBeDeclaration=*/false, /*AddLevels=*/1u,
+ /*MunchSemi=*/true, /*UnindentWhitesmithBraces=*/false,
+ CanContainBracedList,
+ /*NextLBracesType=*/NextLBracesType);
++StatementCount;
assert(StatementCount > 0 && "StatementCount overflow!");
addUnwrappedLine();
@@ -517,9 +546,7 @@ bool UnwrappedLineParser::parseLevel(bool HasOpeningBrace, IfStmtKind *IfKind) {
}
LLVM_FALLTHROUGH;
default:
- parseStructuralElement(IfKind, !HasOpeningBrace);
- ++StatementCount;
- assert(StatementCount > 0 && "StatementCount overflow!");
+ ParseDefault();
break;
}
} while (!eof());
@@ -594,27 +621,46 @@ void UnwrappedLineParser::calculateBraceTypes(bool ExpectClassBody) {
bool NextIsObjCMethod = NextTok->isOneOf(tok::plus, tok::minus) &&
NextTok->OriginalColumn == 0;
+ // Try to detect a braced list. Note that regardless how we mark inner
+ // braces here, we will overwrite the BlockKind later if we parse a
+ // braced list (where all blocks inside are by default braced lists),
+ // or when we explicitly detect blocks (for example while parsing
+ // lambdas).
+
+ // If we already marked the opening brace as braced list, the closing
+ // must also be part of it.
+ ProbablyBracedList = LBraceStack.back()->is(TT_BracedListLBrace);
+
+ ProbablyBracedList = ProbablyBracedList ||
+ (Style.isJavaScript() &&
+ NextTok->isOneOf(Keywords.kw_of, Keywords.kw_in,
+ Keywords.kw_as));
+ ProbablyBracedList = ProbablyBracedList ||
+ (Style.isCpp() && NextTok->is(tok::l_paren));
+
// If there is a comma, semicolon or right paren after the closing
- // brace, we assume this is a braced initializer list. Note that
- // regardless how we mark inner braces here, we will overwrite the
- // BlockKind later if we parse a braced list (where all blocks
- // inside are by default braced lists), or when we explicitly detect
- // blocks (for example while parsing lambdas).
+ // brace, we assume this is a braced initializer list.
// FIXME: Some of these do not apply to JS, e.g. "} {" can never be a
// braced list in JS.
ProbablyBracedList =
- (Style.isJavaScript() &&
- NextTok->isOneOf(Keywords.kw_of, Keywords.kw_in,
- Keywords.kw_as)) ||
- (Style.isCpp() && NextTok->is(tok::l_paren)) ||
+ ProbablyBracedList ||
NextTok->isOneOf(tok::comma, tok::period, tok::colon,
tok::r_paren, tok::r_square, tok::l_brace,
- tok::ellipsis) ||
+ tok::ellipsis);
+
+ ProbablyBracedList =
+ ProbablyBracedList ||
(NextTok->is(tok::identifier) &&
- !PrevTok->isOneOf(tok::semi, tok::r_brace, tok::l_brace)) ||
- (NextTok->is(tok::semi) &&
- (!ExpectClassBody || LBraceStack.size() != 1)) ||
+ !PrevTok->isOneOf(tok::semi, tok::r_brace, tok::l_brace));
+
+ ProbablyBracedList = ProbablyBracedList ||
+ (NextTok->is(tok::semi) &&
+ (!ExpectClassBody || LBraceStack.size() != 1));
+
+ ProbablyBracedList =
+ ProbablyBracedList ||
(NextTok->isBinaryOperator() && !NextIsObjCMethod);
+
if (!Style.isCSharp() && NextTok->is(tok::l_square)) {
// We can have an array subscript after a braced init
// list, but C++11 attributes are expected after blocks.
@@ -680,8 +726,9 @@ size_t UnwrappedLineParser::computePPHash() const {
UnwrappedLineParser::IfStmtKind
UnwrappedLineParser::parseBlock(bool MustBeDeclaration, unsigned AddLevels,
- bool MunchSemi,
- bool UnindentWhitesmithsBraces) {
+ bool MunchSemi, bool UnindentWhitesmithsBraces,
+ bool CanContainBracedList,
+ TokenType NextLBracesType) {
assert(FormatTok->isOneOf(tok::l_brace, TT_MacroBlockBegin) &&
"'{' or macro block token expected");
FormatToken *Tok = FormatTok;
@@ -721,7 +768,8 @@ UnwrappedLineParser::parseBlock(bool MustBeDeclaration, unsigned AddLevels,
Line->Level += AddLevels;
IfStmtKind IfKind = IfStmtKind::NotIf;
- const bool SimpleBlock = parseLevel(/*HasOpeningBrace=*/true, &IfKind);
+ const bool SimpleBlock = parseLevel(
+ /*HasOpeningBrace=*/true, CanContainBracedList, &IfKind, NextLBracesType);
if (eof())
return IfKind;
@@ -751,8 +799,13 @@ UnwrappedLineParser::parseBlock(bool MustBeDeclaration, unsigned AddLevels,
if (MacroBlock && FormatTok->is(tok::l_paren))
parseParens();
+ if (FormatTok->is(tok::kw_noexcept)) {
+ // A noexcept in a requires expression.
+ nextToken();
+ }
+
if (FormatTok->is(tok::arrow)) {
- // Following the } we can find a trailing return type arrow
+ // Following the } or noexcept we can find a trailing return type arrow
// as part of an implicit conversion constraint.
nextToken();
parseStructuralElement();
@@ -826,7 +879,8 @@ static bool ShouldBreakBeforeBrace(const FormatStyle &Style,
return false;
}
-void UnwrappedLineParser::parseChildBlock() {
+void UnwrappedLineParser::parseChildBlock(
+ bool CanContainBracedList, clang::format::TokenType NextLBracesType) {
FormatTok->setBlockKind(BK_Block);
nextToken();
{
@@ -836,7 +890,8 @@ void UnwrappedLineParser::parseChildBlock() {
ScopedDeclarationState DeclarationState(*Line, DeclarationScopeStack,
/*MustBeDeclaration=*/false);
Line->Level += SkipIndent ? 0 : 1;
- parseLevel(/*HasOpeningBrace=*/true);
+ parseLevel(/*HasOpeningBrace=*/true, CanContainBracedList,
+ /*IfKind=*/nullptr, NextLBracesType);
flushComments(isOnNewLine(*FormatTok));
Line->Level -= SkipIndent ? 0 : 1;
}
@@ -1231,7 +1286,8 @@ void UnwrappedLineParser::readTokenWithJavaScriptASI() {
}
void UnwrappedLineParser::parseStructuralElement(IfStmtKind *IfKind,
- bool IsTopLevel) {
+ bool IsTopLevel,
+ TokenType NextLBracesType) {
if (Style.Language == FormatStyle::LK_TableGen &&
FormatTok->is(tok::pp_include)) {
nextToken();
@@ -1482,7 +1538,7 @@ void UnwrappedLineParser::parseStructuralElement(IfStmtKind *IfKind,
parseConcept();
return;
case tok::kw_requires:
- parseRequires();
+ parseRequiresClause();
return;
case tok::kw_enum:
// Ignore if this is part of "template <enum ...".
@@ -1562,6 +1618,8 @@ void UnwrappedLineParser::parseStructuralElement(IfStmtKind *IfKind,
parseChildBlock();
break;
case tok::l_brace:
+ if (NextLBracesType != TT_Unknown)
+ FormatTok->setType(NextLBracesType);
if (!tryToParsePropertyAccessor() && !tryToParseBracedList()) {
// A block outside of parentheses must be the last part of a
// structural element.
@@ -2095,7 +2153,10 @@ bool UnwrappedLineParser::parseBracedList(bool ContinueOnSemicolons,
return false;
}
-void UnwrappedLineParser::parseParens() {
+/// \brief Parses a pair of parentheses (and everything between them).
+/// \param AmpAmpTokenType If
diff erent than TT_Unknown sets this type for all
+/// double ampersands. This only counts for the current parens scope.
+void UnwrappedLineParser::parseParens(TokenType AmpAmpTokenType) {
assert(FormatTok->Tok.is(tok::l_paren) && "'(' expected.");
nextToken();
do {
@@ -2145,6 +2206,13 @@ void UnwrappedLineParser::parseParens() {
else
nextToken();
break;
+ case tok::kw_requires:
+ parseRequiresExpression();
+ break;
+ case tok::ampamp:
+ if (AmpAmpTokenType != TT_Unknown)
+ FormatTok->setType(AmpAmpTokenType);
+ LLVM_FALLTHROUGH;
default:
nextToken();
break;
@@ -2695,6 +2763,11 @@ void UnwrappedLineParser::parseAccessSpecifier() {
}
}
+/// \brief Parses a concept definition.
+/// \pre The current token has to be the concept keyword.
+///
+/// Returns if either the concept has been completely parsed, or if it detects
+/// that the concept definition is incorrect.
void UnwrappedLineParser::parseConcept() {
assert(FormatTok->Tok.is(tok::kw_concept) && "'concept' expected");
nextToken();
@@ -2704,100 +2777,179 @@ void UnwrappedLineParser::parseConcept() {
if (!FormatTok->Tok.is(tok::equal))
return;
nextToken();
- if (FormatTok->Tok.is(tok::kw_requires)) {
+ parseConstraintExpression();
+ if (FormatTok->Tok.is(tok::semi))
nextToken();
- parseRequiresExpression(Line->Level);
- } else {
- parseConstraintExpression(Line->Level);
- }
+ addUnwrappedLine();
}
-void UnwrappedLineParser::parseRequiresExpression(unsigned int OriginalLevel) {
- // requires (R range)
- if (FormatTok->Tok.is(tok::l_paren)) {
+/// \brief Parses a requires clause.
+/// \pre The current token needs to be the requires keyword.
+/// \sa parseRequiresExpression
+///
+/// Returns if it either has finished parsing the clause, or it detects, that
+/// the clause is incorrect.
+void UnwrappedLineParser::parseRequiresClause() {
+ assert(FormatTok->Tok.is(tok::kw_requires) && "'requires' expected");
+ assert(FormatTok->getType() == TT_Unknown);
+
+ // If there is no previous token, we are within a requires expression,
+ // otherwise we will always have the template or function declaration in front
+ // of it.
+ bool InRequiresExpression =
+ !FormatTok->Previous ||
+ FormatTok->Previous->is(TT_RequiresExpressionLBrace);
+
+ FormatTok->setType(InRequiresExpression
+ ? TT_RequiresClauseInARequiresExpression
+ : TT_RequiresClause);
+
+ nextToken();
+ parseConstraintExpression();
+
+ if (!InRequiresExpression)
+ FormatTok->Previous->ClosesRequiresClause = true;
+}
+
+/// \brief Parses a requires expression.
+/// \pre The current token needs to be the requires keyword.
+/// \sa parseRequiresClause
+///
+/// Returns if it either has finished parsing the expression, or it detects,
+/// that the expression is incorrect.
+void UnwrappedLineParser::parseRequiresExpression() {
+ assert(FormatTok->Tok.is(tok::kw_requires) && "'requires' expected");
+ assert(FormatTok->getType() == TT_Unknown);
+
+ FormatTok->setType(TT_RequiresExpression);
+ nextToken();
+
+ if (FormatTok->is(tok::l_paren)) {
+ FormatTok->setType(TT_RequiresExpressionLParen);
parseParens();
- if (Style.IndentRequires && OriginalLevel != Line->Level) {
- addUnwrappedLine();
- --Line->Level;
- }
}
- if (FormatTok->Tok.is(tok::l_brace)) {
- if (Style.BraceWrapping.AfterFunction)
- addUnwrappedLine();
- FormatTok->setType(TT_FunctionLBrace);
- parseBlock();
- addUnwrappedLine();
- } else {
- parseConstraintExpression(OriginalLevel);
+ if (FormatTok->is(tok::l_brace)) {
+ FormatTok->setType(TT_RequiresExpressionLBrace);
+ parseChildBlock(/*CanContainBracedList=*/false,
+ /*NextLBracesType=*/TT_CompoundRequirementLBrace);
}
}
-void UnwrappedLineParser::parseConstraintExpression(
- unsigned int OriginalLevel) {
- // requires Id<T> && Id<T> || Id<T>
- while (
- FormatTok->isOneOf(tok::identifier, tok::kw_requires, tok::coloncolon)) {
- nextToken();
- while (FormatTok->isOneOf(tok::identifier, tok::coloncolon, tok::less,
- tok::greater, tok::comma, tok::ellipsis)) {
- if (FormatTok->Tok.is(tok::less)) {
+/// \brief Parses a constraint expression.
+///
+/// This is either the definition of a concept, or the body of a requires
+/// clause. It returns, when the parsing is complete, or the expression is
+/// incorrect.
+void UnwrappedLineParser::parseConstraintExpression() {
+ do {
+ switch (FormatTok->Tok.getKind()) {
+ case tok::kw_requires:
+ parseRequiresExpression();
+ break;
+
+ case tok::l_paren:
+ parseParens(/*AmpAmpTokenType=*/TT_BinaryOperator);
+ break;
+
+ case tok::l_square:
+ if (!tryToParseLambda())
+ return;
+ break;
+
+ case tok::identifier:
+ // We need to
diff erentiate identifiers for a template deduction guide,
+ // variables, or function return types (the constraint expression has
+ // ended before that), and basically all other cases. But it's easier to
+ // check the other way around.
+ assert(FormatTok->Previous);
+ switch (FormatTok->Previous->Tok.getKind()) {
+ case tok::coloncolon: // Nested identifier.
+ case tok::ampamp: // Start of a function or variable for the
+ case tok::pipepipe: // constraint expression.
+ case tok::kw_requires: // Initial identifier of a requires clause.
+ case tok::equal: // Initial identifier of a concept declaration.
+ break;
+ default:
+ return;
+ }
+
+ // Read identifier with optional template declaration.
+ nextToken();
+ if (FormatTok->Tok.is(tok::less))
parseBracedList(/*ContinueOnSemicolons=*/false, /*IsEnum=*/false,
/*ClosingBraceKind=*/tok::greater);
- continue;
- }
+ break;
+
+ case tok::kw_const:
+ case tok::semi:
+ case tok::kw_class:
+ case tok::kw_struct:
+ case tok::kw_union:
+ return;
+
+ case tok::l_brace:
+ // Potential function body.
+ return;
+
+ case tok::ampamp:
+ case tok::pipepipe:
+ FormatTok->setType(TT_BinaryOperator);
nextToken();
- }
- if (FormatTok->Tok.is(tok::kw_requires))
- parseRequiresExpression(OriginalLevel);
- if (FormatTok->Tok.is(tok::less)) {
- parseBracedList(/*ContinueOnSemicolons=*/false, /*IsEnum=*/false,
- /*ClosingBraceKind=*/tok::greater);
- }
+ break;
- if (FormatTok->Tok.is(tok::l_paren))
- parseParens();
- if (FormatTok->Tok.is(tok::l_brace)) {
- if (Style.BraceWrapping.AfterFunction)
- addUnwrappedLine();
- FormatTok->setType(TT_FunctionLBrace);
- parseBlock();
- }
- if (FormatTok->Tok.is(tok::semi)) {
- // Eat any trailing semi.
+ case tok::kw_true:
+ case tok::kw_false:
+ case tok::kw_sizeof:
+ case tok::greater:
+ case tok::greaterequal:
+ case tok::greatergreater:
+ case tok::less:
+ case tok::lessequal:
+ case tok::lessless:
+ case tok::equalequal:
+ case tok::exclaim:
+ case tok::exclaimequal:
+ case tok::plus:
+ case tok::minus:
+ case tok::star:
+ case tok::slash:
+ case tok::numeric_constant:
+ case tok::kw_decltype:
+ case tok::comment:
+ case tok::comma:
+ case tok::coloncolon:
+ // Just eat them.
nextToken();
- addUnwrappedLine();
- }
- if (FormatTok->Tok.is(tok::colon))
- return;
- if (!FormatTok->Tok.isOneOf(tok::ampamp, tok::pipepipe)) {
- if (FormatTok->Previous &&
- !FormatTok->Previous->isOneOf(tok::identifier, tok::kw_requires,
- tok::coloncolon))
- addUnwrappedLine();
- if (Style.IndentRequires && OriginalLevel != Line->Level)
- --Line->Level;
break;
- } else {
- FormatTok->setType(TT_ConstraintJunctions);
- }
- nextToken();
- }
-}
+ case tok::kw_static_cast:
+ case tok::kw_const_cast:
+ case tok::kw_reinterpret_cast:
+ case tok::kw_dynamic_cast:
+ nextToken();
+ if (!FormatTok->is(tok::less))
+ return;
-void UnwrappedLineParser::parseRequires() {
- assert(FormatTok->Tok.is(tok::kw_requires) && "'requires' expected");
+ parseBracedList(/*ContinueOnSemicolons=*/false, /*IsEnum=*/false,
+ /*ClosingBraceKind=*/tok::greater);
+ break;
- unsigned OriginalLevel = Line->Level;
- if (FormatTok->Previous && FormatTok->Previous->is(tok::greater)) {
- addUnwrappedLine();
- if (Style.IndentRequires)
- ++Line->Level;
- }
- nextToken();
+ case tok::kw_bool:
+ // bool is only allowed if it is directly followed by a paren for a cast:
+ // concept C = bool(...);
+ // and bool is the only type, all other types as cast must be inside a
+ // cast to bool an thus are handled by the other cases.
+ nextToken();
+ if (FormatTok->isNot(tok::l_paren))
+ return;
+ parseParens();
+ break;
- parseRequiresExpression(OriginalLevel);
+ default:
+ return;
+ }
+ } while (!eof());
}
bool UnwrappedLineParser::parseEnum() {
@@ -2993,7 +3145,7 @@ void UnwrappedLineParser::parseJavaEnumBody() {
}
// Parse the class body after the enum's ";" if any.
- parseLevel(/*HasOpeningBrace=*/true);
+ parseLevel(/*HasOpeningBrace=*/true, /*CanContainBracedList=*/true);
nextToken();
--Line->Level;
addUnwrappedLine();
diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h
index f39d76187f440..d49bbaefd1469 100644
--- a/clang/lib/Format/UnwrappedLineParser.h
+++ b/clang/lib/Format/UnwrappedLineParser.h
@@ -92,11 +92,16 @@ class UnwrappedLineParser {
void reset();
void parseFile();
bool precededByCommentOrPPDirective() const;
- bool parseLevel(bool HasOpeningBrace, IfStmtKind *IfKind = nullptr);
+ bool parseLevel(bool HasOpeningBrace, bool CanContainBracedList,
+ IfStmtKind *IfKind = nullptr,
+ TokenType NextLBracesType = TT_Unknown);
IfStmtKind parseBlock(bool MustBeDeclaration = false, unsigned AddLevels = 1u,
bool MunchSemi = true,
- bool UnindentWhitesmithsBraces = false);
- void parseChildBlock();
+ bool UnindentWhitesmithsBraces = false,
+ bool CanContainBracedList = true,
+ TokenType NextLBracesType = TT_Unknown);
+ void parseChildBlock(bool CanContainBracedList = true,
+ TokenType NextLBracesType = TT_Unknown);
void parsePPDirective();
void parsePPDefine();
void parsePPIf(bool IfDef);
@@ -106,11 +111,12 @@ class UnwrappedLineParser {
void parsePPUnknown();
void readTokenWithJavaScriptASI();
void parseStructuralElement(IfStmtKind *IfKind = nullptr,
- bool IsTopLevel = false);
+ bool IsTopLevel = false,
+ TokenType NextLBracesType = TT_Unknown);
bool tryToParseBracedList();
bool parseBracedList(bool ContinueOnSemicolons = false, bool IsEnum = false,
tok::TokenKind ClosingBraceKind = tok::r_brace);
- void parseParens();
+ void parseParens(TokenType AmpAmpTokenType = TT_Unknown);
void parseSquare(bool LambdaIntroducer = false);
void keepAncestorBraces();
FormatToken *parseIfThenElse(IfStmtKind *IfKind, bool KeepBraces = false);
@@ -127,9 +133,9 @@ class UnwrappedLineParser {
bool parseEnum();
bool parseStructLike();
void parseConcept();
- void parseRequires();
- void parseRequiresExpression(unsigned int OriginalLevel);
- void parseConstraintExpression(unsigned int OriginalLevel);
+ void parseRequiresClause();
+ void parseRequiresExpression();
+ void parseConstraintExpression();
void parseJavaEnumBody();
// Parses a record (aka class) as a top level element. If ParseAsExpr is true,
// parses the record as a child block, i.e. if the class declaration is an
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 06b2fe5c650c1..0d315734bc951 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -3826,7 +3826,10 @@ TEST_F(FormatTest, FormatsNamespaces) {
"struct b_struct {};\n"
"} // namespace B\n",
Style);
- verifyFormat("template <int I> constexpr void foo requires(I == 42) {}\n"
+ verifyFormat("template <int I>\n"
+ "constexpr void foo()\n"
+ " requires(I == 42)\n"
+ "{}\n"
"namespace ns {\n"
"void foo() {}\n"
"} // namespace ns\n",
@@ -19254,7 +19257,6 @@ TEST_F(FormatTest, ParsesConfigurationBools) {
CHECK_PARSE_BOOL(BinPackArguments);
CHECK_PARSE_BOOL(BinPackParameters);
CHECK_PARSE_BOOL(BreakAfterJavaFieldAnnotations);
- CHECK_PARSE_BOOL(BreakBeforeConceptDeclarations);
CHECK_PARSE_BOOL(BreakBeforeTernaryOperators);
CHECK_PARSE_BOOL(BreakStringLiterals);
CHECK_PARSE_BOOL(CompactNamespaces);
@@ -19266,7 +19268,8 @@ TEST_F(FormatTest, ParsesConfigurationBools) {
CHECK_PARSE_BOOL(IndentCaseLabels);
CHECK_PARSE_BOOL(IndentCaseBlocks);
CHECK_PARSE_BOOL(IndentGotoLabels);
- CHECK_PARSE_BOOL(IndentRequires);
+ CHECK_PARSE_BOOL_FIELD(IndentRequiresClause, "IndentRequires");
+ CHECK_PARSE_BOOL(IndentRequiresClause);
CHECK_PARSE_BOOL(IndentWrappedFunctionNames);
CHECK_PARSE_BOOL(KeepEmptyLinesAtTheStartOfBlocks);
CHECK_PARSE_BOOL(ObjCSpaceAfterProperty);
@@ -19932,6 +19935,27 @@ TEST_F(FormatTest, ParsesConfiguration) {
// For backward compatibility:
CHECK_PARSE("SpacesInAngles: false", SpacesInAngles, FormatStyle::SIAS_Never);
CHECK_PARSE("SpacesInAngles: true", SpacesInAngles, FormatStyle::SIAS_Always);
+
+ CHECK_PARSE("RequiresClausePosition: WithPreceding", RequiresClausePosition,
+ FormatStyle::RCPS_WithPreceding);
+ CHECK_PARSE("RequiresClausePosition: WithFollowing", RequiresClausePosition,
+ FormatStyle::RCPS_WithFollowing);
+ CHECK_PARSE("RequiresClausePosition: SingleLine", RequiresClausePosition,
+ FormatStyle::RCPS_SingleLine);
+ CHECK_PARSE("RequiresClausePosition: OwnLine", RequiresClausePosition,
+ FormatStyle::RCPS_OwnLine);
+
+ CHECK_PARSE("BreakBeforeConceptDeclarations: Never",
+ BreakBeforeConceptDeclarations, FormatStyle::BBCDS_Never);
+ CHECK_PARSE("BreakBeforeConceptDeclarations: Always",
+ BreakBeforeConceptDeclarations, FormatStyle::BBCDS_Always);
+ CHECK_PARSE("BreakBeforeConceptDeclarations: Allowed",
+ BreakBeforeConceptDeclarations, FormatStyle::BBCDS_Allowed);
+ // For backward compatibility:
+ CHECK_PARSE("BreakBeforeConceptDeclarations: true",
+ BreakBeforeConceptDeclarations, FormatStyle::BBCDS_Always);
+ CHECK_PARSE("BreakBeforeConceptDeclarations: false",
+ BreakBeforeConceptDeclarations, FormatStyle::BBCDS_Allowed);
}
TEST_F(FormatTest, ParsesConfigurationWithLanguages) {
@@ -23200,275 +23224,584 @@ TEST_F(FormatTest, WebKitDefaultStyle) {
Style);
}
-TEST_F(FormatTest, ConceptsAndRequires) {
- FormatStyle Style = getLLVMStyle();
- Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None;
+TEST_F(FormatTest, Concepts) {
+ EXPECT_EQ(getLLVMStyle().BreakBeforeConceptDeclarations,
+ FormatStyle::BBCDS_Always);
+ verifyFormat("template <typename T>\n"
+ "concept True = true;");
verifyFormat("template <typename T>\n"
- "concept Hashable = requires(T a) {\n"
- " { std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;\n"
- "};",
- Style);
+ "concept C = ((false || foo()) && C2<T>) ||\n"
+ " (std::trait<T>::value && Baz) || sizeof(T) >= 6;",
+ getLLVMStyleWithColumns(60));
+
verifyFormat("template <typename T>\n"
- "concept EqualityComparable = requires(T a, T b) {\n"
- " { a == b } -> bool;\n"
- "};",
- Style);
+ "concept DelayedCheck = true && requires(T t) { t.bar(); } && "
+ "sizeof(T) <= 8;");
+
verifyFormat("template <typename T>\n"
- "concept EqualityComparable = requires(T a, T b) {\n"
- " { a == b } -> bool;\n"
- " { a != b } -> bool;\n"
- "};",
- Style);
+ "concept DelayedCheck = true && requires(T t) {\n"
+ " t.bar();\n"
+ " t.baz();\n"
+ " } && sizeof(T) <= 8;");
+
verifyFormat("template <typename T>\n"
- "concept EqualityComparable = requires(T a, T b) {\n"
- " { a == b } -> bool;\n"
- " { a != b } -> bool;\n"
- "};",
- Style);
+ "concept DelayedCheck = true && requires(T t) { // Comment\n"
+ " t.bar();\n"
+ " t.baz();\n"
+ " } && sizeof(T) <= 8;");
- verifyFormat("template <typename It>\n"
- "requires Iterator<It>\n"
- "void sort(It begin, It end) {\n"
- " //....\n"
- "}",
- Style);
+ verifyFormat("template <typename T>\n"
+ "concept DelayedCheck = false || requires(T t) { t.bar(); } && "
+ "sizeof(T) <= 8;");
verifyFormat("template <typename T>\n"
- "concept Large = sizeof(T) > 10;",
- Style);
+ "concept DelayedCheck = !!false || requires(T t) { t.bar(); } "
+ "&& sizeof(T) <= 8;");
+
+ verifyFormat(
+ "template <typename T>\n"
+ "concept DelayedCheck = static_cast<bool>(0) ||\n"
+ " requires(T t) { t.bar(); } && sizeof(T) <= 8;");
- verifyFormat("template <typename T, typename U>\n"
- "concept FooableWith = requires(T t, U u) {\n"
- " typename T::foo_type;\n"
- " { t.foo(u) } -> typename T::foo_type;\n"
- " t++;\n"
- "};\n"
- "void doFoo(FooableWith<int> auto t) {\n"
- " t.foo(3);\n"
- "}",
- Style);
verifyFormat("template <typename T>\n"
- "concept Context = sizeof(T) == 1;",
- Style);
+ "concept DelayedCheck = bool(0) || requires(T t) { t.bar(); } "
+ "&& sizeof(T) <= 8;");
+
+ verifyFormat(
+ "template <typename T>\n"
+ "concept DelayedCheck = (bool)(0) ||\n"
+ " requires(T t) { t.bar(); } && sizeof(T) <= 8;");
+
verifyFormat("template <typename T>\n"
- "concept Context = is_specialization_of_v<context, T>;",
- Style);
+ "concept DelayedCheck = (bool)0 || requires(T t) { t.bar(); } "
+ "&& sizeof(T) <= 8;");
+
verifyFormat("template <typename T>\n"
- "concept Node = std::is_object_v<T>;",
- Style);
+ "concept Size = sizeof(T) >= 5 && requires(T t) { t.bar(); } && "
+ "sizeof(T) <= 8;");
+
verifyFormat("template <typename T>\n"
- "concept Tree = true;",
- Style);
+ "concept Size = 2 < 5 && 2 <= 5 && 8 >= 5 && 8 > 5 &&\n"
+ " requires(T t) {\n"
+ " t.bar();\n"
+ " t.baz();\n"
+ " } && sizeof(T) <= 8 && !(4 < 3);",
+ getLLVMStyleWithColumns(60));
- verifyFormat("template <typename T> int g(T i) requires Concept1<I> {\n"
- " //...\n"
- "}",
- Style);
+ verifyFormat("template <typename T>\n"
+ "concept TrueOrNot = IsAlwaysTrue || IsNeverTrue;");
+
+ verifyFormat("template <typename T>\n"
+ "concept C = foo();");
+
+ verifyFormat("template <typename T>\n"
+ "concept C = foo(T());");
+
+ verifyFormat("template <typename T>\n"
+ "concept C = foo(T{});");
+
+ verifyFormat("template <typename T>\n"
+ "concept Size = V<sizeof(T)>::Value > 5;");
+
+ verifyFormat("template <typename T>\n"
+ "concept True = S<T>::Value;");
verifyFormat(
- "template <typename T> int g(T i) requires Concept1<I> && Concept2<I> {\n"
- " //...\n"
- "}",
- Style);
+ "template <typename T>\n"
+ "concept C = []() { return true; }() && requires(T t) { t.bar(); } &&\n"
+ " sizeof(T) <= 8;");
+
+ // FIXME: This is misformatted because the fake l paren starts at bool, not at
+ // the lambda l square.
+ verifyFormat("template <typename T>\n"
+ "concept C = [] -> bool { return true; }() && requires(T t) { "
+ "t.bar(); } &&\n"
+ " sizeof(T) <= 8;");
verifyFormat(
- "template <typename T> int g(T i) requires Concept1<I> || Concept2<I> {\n"
- " //...\n"
- "}",
- Style);
+ "template <typename T>\n"
+ "concept C = decltype([]() { return std::true_type{}; }())::value &&\n"
+ " requires(T t) { t.bar(); } && sizeof(T) <= 8;");
verifyFormat("template <typename T>\n"
- "veryveryvery_long_return_type g(T i) requires Concept1<I> || "
- "Concept2<I> {\n"
- " //...\n"
- "}",
- Style);
+ "concept C = decltype([]() { return std::true_type{}; "
+ "}())::value && requires(T t) { t.bar(); } && sizeof(T) <= 8;",
+ getLLVMStyleWithColumns(120));
verifyFormat("template <typename T>\n"
- "veryveryvery_long_return_type g(T i) requires Concept1<I> && "
- "Concept2<I> {\n"
- " //...\n"
- "}",
- Style);
+ "concept C = decltype([]() -> std::true_type { return {}; "
+ "}())::value &&\n"
+ " requires(T t) { t.bar(); } && sizeof(T) <= 8;");
+
+ verifyFormat("template <typename T>\n"
+ "concept C = true;\n"
+ "Foo Bar;");
+
+ verifyFormat("template <typename T>\n"
+ "concept Hashable = requires(T a) {\n"
+ " { std::hash<T>{}(a) } -> "
+ "std::convertible_to<std::size_t>;\n"
+ " };");
verifyFormat(
"template <typename T>\n"
- "veryveryvery_long_return_type g(T i) requires Concept1 && Concept2 {\n"
- " //...\n"
- "}",
- Style);
+ "concept EqualityComparable = requires(T a, T b) {\n"
+ " { a == b } -> std::same_as<bool>;\n"
+ " };");
verifyFormat(
"template <typename T>\n"
- "veryveryvery_long_return_type g(T i) requires Concept1 || Concept2 {\n"
- " //...\n"
- "}",
- Style);
+ "concept EqualityComparable = requires(T a, T b) {\n"
+ " { a == b } -> std::same_as<bool>;\n"
+ " { a != b } -> std::same_as<bool>;\n"
+ " };");
- verifyFormat("template <typename It>\n"
- "requires Foo<It>() && Bar<It> {\n"
- " //....\n"
- "}",
- Style);
+ verifyFormat("template <typename T>\n"
+ "concept WeakEqualityComparable = requires(T a, T b) {\n"
+ " { a == b };\n"
+ " { a != b };\n"
+ " };");
- verifyFormat("template <typename It>\n"
- "requires Foo<Bar<It>>() && Bar<Foo<It, It>> {\n"
- " //....\n"
- "}",
- Style);
+ verifyFormat("template <typename T>\n"
+ "concept HasSizeT = requires { typename T::size_t; };");
- verifyFormat("template <typename It>\n"
- "requires Foo<Bar<It, It>>() && Bar<Foo<It, It>> {\n"
- " //....\n"
- "}",
- Style);
+ verifyFormat("template <typename T>\n"
+ "concept Semiregular =\n"
+ " DefaultConstructible<T> && CopyConstructible<T> && "
+ "CopyAssignable<T> &&\n"
+ " requires(T a, std::size_t n) {\n"
+ " requires Same<T *, decltype(&a)>;\n"
+ " { a.~T() } noexcept;\n"
+ " requires Same<T *, decltype(new T)>;\n"
+ " requires Same<T *, decltype(new T[n])>;\n"
+ " { delete new T; };\n"
+ " { delete new T[n]; };\n"
+ " };");
+
+ verifyFormat("template <typename T>\n"
+ "concept Semiregular =\n"
+ " requires(T a, std::size_t n) {\n"
+ " requires Same<T *, decltype(&a)>;\n"
+ " { a.~T() } noexcept;\n"
+ " requires Same<T *, decltype(new T)>;\n"
+ " requires Same<T *, decltype(new T[n])>;\n"
+ " { delete new T; };\n"
+ " { delete new T[n]; };\n"
+ " { new T } -> std::same_as<T *>;\n"
+ " } && DefaultConstructible<T> && CopyConstructible<T> && "
+ "CopyAssignable<T>;");
verifyFormat(
- "template <typename It>\n"
- "requires Foo<Bar<It>, Baz<It>>() && Bar<Foo<It>, Baz<It, It>> {\n"
- " //....\n"
- "}",
+ "template <typename T>\n"
+ "concept Semiregular =\n"
+ " DefaultConstructible<T> && requires(T a, std::size_t n) {\n"
+ " requires Same<T *, decltype(&a)>;\n"
+ " { a.~T() } noexcept;\n"
+ " requires Same<T *, decltype(new T)>;\n"
+ " requires Same<T *, decltype(new "
+ "T[n])>;\n"
+ " { delete new T; };\n"
+ " { delete new T[n]; };\n"
+ " } && CopyConstructible<T> && "
+ "CopyAssignable<T>;");
+
+ verifyFormat("template <typename T>\n"
+ "concept Two = requires(T t) {\n"
+ " { t.foo() } -> std::same_as<Bar>;\n"
+ " } && requires(T &&t) {\n"
+ " { t.foo() } -> std::same_as<Bar &&>;\n"
+ " };");
+
+ verifyFormat(
+ "template <typename T>\n"
+ "concept C = requires(T x) {\n"
+ " { *x } -> std::convertible_to<typename T::inner>;\n"
+ " { x + 1 } noexcept -> std::same_as<int>;\n"
+ " { x * 1 } -> std::convertible_to<T>;\n"
+ " };");
+
+ verifyFormat(
+ "template <typename T, typename U = T>\n"
+ "concept Swappable = requires(T &&t, U &&u) {\n"
+ " swap(std::forward<T>(t), std::forward<U>(u));\n"
+ " swap(std::forward<U>(u), std::forward<T>(t));\n"
+ " };");
+
+ verifyFormat("template <typename T, typename U>\n"
+ "concept Common = requires(T &&t, U &&u) {\n"
+ " typename CommonType<T, U>;\n"
+ " { CommonType<T, U>(std::forward<T>(t)) };\n"
+ " };");
+
+ verifyFormat("template <typename T, typename U>\n"
+ "concept Common = requires(T &&t, U &&u) {\n"
+ " typename CommonType<T, U>;\n"
+ " { CommonType<T, U>{std::forward<T>(t)} };\n"
+ " };");
+
+ verifyFormat(
+ "template <typename T>\n"
+ "concept C = requires(T t) {\n"
+ " requires Bar<T> && Foo<T>;\n"
+ " requires((trait<T> && Baz) || (T2<T> && Foo<T>));\n"
+ " };");
+
+ verifyFormat("template <typename T>\n"
+ "concept HasFoo = requires(T t) {\n"
+ " { t.foo() };\n"
+ " t.foo();\n"
+ " };\n"
+ "template <typename T>\n"
+ "concept HasBar = requires(T t) {\n"
+ " { t.bar() };\n"
+ " t.bar();\n"
+ " };");
+
+ verifyFormat("template <typename T>\n"
+ "concept Large = sizeof(T) > 10;");
+
+ verifyFormat("template <typename T, typename U>\n"
+ "concept FooableWith = requires(T t, U u) {\n"
+ " typename T::foo_type;\n"
+ " { t.foo(u) } -> typename T::foo_type;\n"
+ " t++;\n"
+ " };\n"
+ "void doFoo(FooableWith<int> auto t) { t.foo(3); }");
+
+ verifyFormat("template <typename T>\n"
+ "concept Context = is_specialization_of_v<context, T>;");
+
+ verifyFormat("template <typename T>\n"
+ "concept Node = std::is_object_v<T>;");
+
+ auto Style = getLLVMStyle();
+ Style.BreakBeforeConceptDeclarations = FormatStyle::BBCDS_Allowed;
+
+ verifyFormat(
+ "template <typename T>\n"
+ "concept C = requires(T t) {\n"
+ " requires Bar<T> && Foo<T>;\n"
+ " requires((trait<T> && Baz) || (T2<T> && Foo<T>));\n"
+ " };",
Style);
- Style.IndentRequires = true;
- verifyFormat("template <typename It>\n"
- " requires Iterator<It>\n"
- "void sort(It begin, It end) {\n"
- " //....\n"
- "}",
+ verifyFormat("template <typename T>\n"
+ "concept HasFoo = requires(T t) {\n"
+ " { t.foo() };\n"
+ " t.foo();\n"
+ " };\n"
+ "template <typename T>\n"
+ "concept HasBar = requires(T t) {\n"
+ " { t.bar() };\n"
+ " t.bar();\n"
+ " };",
Style);
- verifyFormat("template <std::size index_>\n"
- " requires(index_ < sizeof...(Children_))\n"
- "Tree auto &child() {\n"
- " // ...\n"
- "}",
+
+ verifyFormat("template <typename T> concept True = true;", Style);
+
+ verifyFormat("template <typename T>\n"
+ "concept C = decltype([]() -> std::true_type { return {}; "
+ "}())::value &&\n"
+ " requires(T t) { t.bar(); } && sizeof(T) <= 8;",
Style);
- Style.SpaceBeforeParens = FormatStyle::SBPO_Always;
verifyFormat("template <typename T>\n"
- "concept Hashable = requires (T a) {\n"
- " { std::hash<T>{}(a) } -> std::convertible_to<std::size_t>;\n"
- "};",
+ "concept Semiregular =\n"
+ " DefaultConstructible<T> && CopyConstructible<T> && "
+ "CopyAssignable<T> &&\n"
+ " requires(T a, std::size_t n) {\n"
+ " requires Same<T *, decltype(&a)>;\n"
+ " { a.~T() } noexcept;\n"
+ " requires Same<T *, decltype(new T)>;\n"
+ " requires Same<T *, decltype(new T[n])>;\n"
+ " { delete new T; };\n"
+ " { delete new T[n]; };\n"
+ " };",
Style);
- verifyFormat("template <class T = void>\n"
- " requires EqualityComparable<T> || Same<T, void>\n"
- "struct equal_to;",
+ Style.BreakBeforeConceptDeclarations = FormatStyle::BBCDS_Never;
+
+ verifyFormat("template <typename T> concept C =\n"
+ " requires(T t) {\n"
+ " requires Bar<T> && Foo<T>;\n"
+ " requires((trait<T> && Baz) || (T2<T> && Foo<T>));\n"
+ " };",
Style);
- verifyFormat("template <class T>\n"
- " requires requires {\n"
- " T{};\n"
- " T (int);\n"
- " }\n",
+ verifyFormat("template <typename T> concept HasFoo = requires(T t) {\n"
+ " { t.foo() };\n"
+ " t.foo();\n"
+ " };\n"
+ "template <typename T> concept HasBar = requires(T t) {\n"
+ " { t.bar() };\n"
+ " t.bar();\n"
+ " };",
Style);
- Style.ColumnLimit = 78;
+ verifyFormat("template <typename T> concept True = true;", Style);
+
+ verifyFormat(
+ "template <typename T> concept C = decltype([]() -> std::true_type {\n"
+ " return {};\n"
+ " }())::value\n"
+ " && requires(T t) { t.bar(); } &&\n"
+ " sizeof(T) <= 8;",
+ Style);
+
+ verifyFormat("template <typename T> concept Semiregular =\n"
+ " DefaultConstructible<T> && CopyConstructible<T> && "
+ "CopyAssignable<T> &&\n"
+ " requires(T a, std::size_t n) {\n"
+ " requires Same<T *, decltype(&a)>;\n"
+ " { a.~T() } noexcept;\n"
+ " requires Same<T *, decltype(new T)>;\n"
+ " requires Same<T *, decltype(new T[n])>;\n"
+ " { delete new T; };\n"
+ " { delete new T[n]; };\n"
+ " };",
+ Style);
+
+ // The following tests are invalid C++, we just want to make sure we don't
+ // assert.
verifyFormat("template <typename T>\n"
- "concept Context = Traits<typename T::traits_type> and\n"
- " Interface<typename T::interface_type> and\n"
- " Request<typename T::request_type> and\n"
- " Response<typename T::response_type> and\n"
- " ContextExtension<typename T::extension_type> and\n"
- " ::std::is_copy_constructable<T> and "
- "::std::is_move_constructable<T> and\n"
- " requires (T c) {\n"
- " { c.response; } -> Response;\n"
- "} and requires (T c) {\n"
- " { c.request; } -> Request;\n"
- "}\n",
+ "concept C = requires C2<T>;");
+
+ verifyFormat("template <typename T>\n"
+ "concept C = 5 + 4;");
+
+ verifyFormat("template <typename T>\n"
+ "concept C =\n"
+ "class X;");
+
+ verifyFormat("template <typename T>\n"
+ "concept C = [] && true;");
+
+ verifyFormat("template <typename T>\n"
+ "concept C = [] && requires(T t) { typename T::size_type; };");
+}
+
+TEST_F(FormatTest, RequiresClauses) {
+ auto Style = getLLVMStyle();
+ EXPECT_EQ(Style.RequiresClausePosition, FormatStyle::RCPS_OwnLine);
+ EXPECT_EQ(Style.IndentRequiresClause, true);
+
+ verifyFormat("template <typename T>\n"
+ " requires(Foo<T> && std::trait<T>)\n"
+ "struct Bar;",
Style);
verifyFormat("template <typename T>\n"
- "concept Context = Traits<typename T::traits_type> or\n"
- " Interface<typename T::interface_type> or\n"
- " Request<typename T::request_type> or\n"
- " Response<typename T::response_type> or\n"
- " ContextExtension<typename T::extension_type> or\n"
- " ::std::is_copy_constructable<T> or "
- "::std::is_move_constructable<T> or\n"
- " requires (T c) {\n"
- " { c.response; } -> Response;\n"
- "} or requires (T c) {\n"
- " { c.request; } -> Request;\n"
- "}\n",
+ " requires(Foo<T> && std::trait<T>)\n"
+ "class Bar {\n"
+ "public:\n"
+ " Bar(T t);\n"
+ " bool baz();\n"
+ "};",
Style);
+ verifyFormat(
+ "template <typename T>\n"
+ " requires requires(T &&t) {\n"
+ " typename T::I;\n"
+ " requires(F<typename T::I> && std::trait<typename T::I>);\n"
+ " }\n"
+ "Bar(T) -> Bar<typename T::I>;",
+ Style);
+
verifyFormat("template <typename T>\n"
- "concept Context = Traits<typename T::traits_type> &&\n"
- " Interface<typename T::interface_type> &&\n"
- " Request<typename T::request_type> &&\n"
- " Response<typename T::response_type> &&\n"
- " ContextExtension<typename T::extension_type> &&\n"
- " ::std::is_copy_constructable<T> && "
- "::std::is_move_constructable<T> &&\n"
- " requires (T c) {\n"
- " { c.response; } -> Response;\n"
- "} && requires (T c) {\n"
- " { c.request; } -> Request;\n"
- "}\n",
+ " requires(Foo<T> && std::trait<T>)\n"
+ "constexpr T MyGlobal;",
Style);
- verifyFormat("template <typename T>\nconcept someConcept = Constraint1<T> && "
- "Constraint2<T>;");
+ verifyFormat("template <typename T>\n"
+ " requires Foo<T> && requires(T t) {\n"
+ " { t.baz() } -> std::same_as<bool>;\n"
+ " requires std::same_as<T::Factor, int>;\n"
+ " }\n"
+ "inline int bar(T t) {\n"
+ " return t.baz() ? T::Factor : 5;\n"
+ "}",
+ Style);
- Style.BreakBeforeBraces = FormatStyle::BS_Custom;
- Style.BraceWrapping.AfterFunction = true;
- Style.BraceWrapping.AfterClass = true;
- Style.AlwaysBreakTemplateDeclarations = FormatStyle::BTDS_Yes;
- Style.BreakConstructorInitializers = FormatStyle::BCIS_BeforeColon;
- verifyFormat("void Foo () requires (std::copyable<T>)\n"
+ verifyFormat("template <typename T>\n"
+ "inline int bar(T t)\n"
+ " requires Foo<T> && requires(T t) {\n"
+ " { t.baz() } -> std::same_as<bool>;\n"
+ " requires std::same_as<T::Factor, int>;\n"
+ " }\n"
"{\n"
- " return\n"
- "}\n",
+ " return t.baz() ? T::Factor : 5;\n"
+ "}",
Style);
- verifyFormat("void Foo () requires std::copyable<T>\n"
- "{\n"
- " return\n"
- "}\n",
+ verifyFormat("template <typename T>\n"
+ " requires F<T>\n"
+ "int bar(T t) {\n"
+ " return 5;\n"
+ "}",
Style);
- verifyFormat("template <std::semiregular F, std::semiregular... Args>\n"
- " requires (std::invocable<F, std::invoke_result_t<Args>...>)\n"
- "struct constant;",
+ verifyFormat("template <typename T>\n"
+ "int bar(T t)\n"
+ " requires F<T>\n"
+ "{\n"
+ " return 5;\n"
+ "}",
Style);
- verifyFormat("template <std::semiregular F, std::semiregular... Args>\n"
- " requires std::invocable<F, std::invoke_result_t<Args>...>\n"
- "struct constant;",
+ Style.IndentRequiresClause = false;
+ verifyFormat("template <typename T>\n"
+ "requires F<T>\n"
+ "int bar(T t) {\n"
+ " return 5;\n"
+ "}",
Style);
- verifyFormat("template <class T>\n"
- "class plane_with_very_very_very_long_name\n"
+ verifyFormat("template <typename T>\n"
+ "int bar(T t)\n"
+ "requires F<T>\n"
"{\n"
- " constexpr plane_with_very_very_very_long_name () requires "
- "std::copyable<T>\n"
- " : plane_with_very_very_very_long_name (1)\n"
- " {\n"
- " }\n"
- "}\n",
+ " return 5;\n"
+ "}",
Style);
- verifyFormat("template <class T>\n"
- "class plane_with_long_name\n"
- "{\n"
- " constexpr plane_with_long_name () requires std::copyable<T>\n"
- " : plane_with_long_name (1)\n"
- " {\n"
- " }\n"
- "}\n",
+ Style.RequiresClausePosition = FormatStyle::RCPS_SingleLine;
+ verifyFormat("template <typename T> requires Foo<T> struct Bar {};\n"
+ "template <typename T> requires Foo<T> void bar() {}\n"
+ "template <typename T> void bar() requires Foo<T> {}\n"
+ "template <typename T> requires Foo<T> Bar(T) -> Bar<T>;",
+ Style);
+
+ auto ColumnStyle = Style;
+ ColumnStyle.ColumnLimit = 40;
+ verifyFormat("template <typename AAAAAAA>\n"
+ "requires Foo<T> struct Bar {};\n"
+ "template <typename AAAAAAA>\n"
+ "requires Foo<T> void bar() {}\n"
+ "template <typename AAAAAAA>\n"
+ "void bar() requires Foo<T> {}\n"
+ "template <typename AAAAAAA>\n"
+ "requires Foo<T> Baz(T) -> Baz<T>;",
+ ColumnStyle);
+
+ verifyFormat("template <typename T>\n"
+ "requires Foo<AAAAAAA> struct Bar {};\n"
+ "template <typename T>\n"
+ "requires Foo<AAAAAAA> void bar() {}\n"
+ "template <typename T>\n"
+ "void bar() requires Foo<AAAAAAA> {}\n"
+ "template <typename T>\n"
+ "requires Foo<AAAAAAA> Bar(T) -> Bar<T>;",
+ ColumnStyle);
+
+ verifyFormat("template <typename AAAAAAA>\n"
+ "requires Foo<AAAAAAAAAAAAAAAA>\n"
+ "struct Bar {};\n"
+ "template <typename AAAAAAA>\n"
+ "requires Foo<AAAAAAAAAAAAAAAA>\n"
+ "void bar() {}\n"
+ "template <typename AAAAAAA>\n"
+ "void bar()\n"
+ " requires Foo<AAAAAAAAAAAAAAAA> {}\n"
+ "template <typename AAAAAAA>\n"
+ "requires Foo<AAAAAAAA> Bar(T) -> Bar<T>;\n"
+ "template <typename AAAAAAA>\n"
+ "requires Foo<AAAAAAAAAAAAAAAA>\n"
+ "Bar(T) -> Bar<T>;",
+ ColumnStyle);
+
+ Style.RequiresClausePosition = FormatStyle::RCPS_WithFollowing;
+ ColumnStyle.RequiresClausePosition = FormatStyle::RCPS_WithFollowing;
+
+ verifyFormat("template <typename T>\n"
+ "requires Foo<T> struct Bar {};\n"
+ "template <typename T>\n"
+ "requires Foo<T> void bar() {}\n"
+ "template <typename T>\n"
+ "void bar()\n"
+ "requires Foo<T> {}\n"
+ "template <typename T>\n"
+ "requires Foo<T> Bar(T) -> Bar<T>;",
Style);
- Style.BreakBeforeConceptDeclarations = false;
- verifyFormat("template <typename T> concept Tree = true;", Style);
+ verifyFormat("template <typename AAAAAAA>\n"
+ "requires Foo<AAAAAAAAAAAAAAAA>\n"
+ "struct Bar {};\n"
+ "template <typename AAAAAAA>\n"
+ "requires Foo<AAAAAAAAAAAAAAAA>\n"
+ "void bar() {}\n"
+ "template <typename AAAAAAA>\n"
+ "void bar()\n"
+ "requires Foo<AAAAAAAAAAAAAAAA> {}\n"
+ "template <typename AAAAAAA>\n"
+ "requires Foo<AAAAAAAA> Bar(T) -> Bar<T>;\n"
+ "template <typename AAAAAAA>\n"
+ "requires Foo<AAAAAAAAAAAAAAAA>\n"
+ "Bar(T) -> Bar<T>;",
+ ColumnStyle);
- Style.IndentRequires = false;
- verifyFormat("template <std::semiregular F, std::semiregular... Args>\n"
- "requires (std::invocable<F, std::invoke_result_t<Args>...>) "
- "struct constant;",
+ Style.IndentRequiresClause = true;
+ ColumnStyle.IndentRequiresClause = true;
+
+ verifyFormat("template <typename T>\n"
+ " requires Foo<T> struct Bar {};\n"
+ "template <typename T>\n"
+ " requires Foo<T> void bar() {}\n"
+ "template <typename T>\n"
+ "void bar()\n"
+ " requires Foo<T> {}\n"
+ "template <typename T>\n"
+ " requires Foo<T> Bar(T) -> Bar<T>;",
Style);
+
+ verifyFormat("template <typename AAAAAAA>\n"
+ " requires Foo<AAAAAAAAAAAAAAAA>\n"
+ "struct Bar {};\n"
+ "template <typename AAAAAAA>\n"
+ " requires Foo<AAAAAAAAAAAAAAAA>\n"
+ "void bar() {}\n"
+ "template <typename AAAAAAA>\n"
+ "void bar()\n"
+ " requires Foo<AAAAAAAAAAAAAAAA> {}\n"
+ "template <typename AAAAAAA>\n"
+ " requires Foo<AAAAAA> Bar(T) -> Bar<T>;\n"
+ "template <typename AAAAAAA>\n"
+ " requires Foo<AAAAAAAAAAAAAAAA>\n"
+ "Bar(T) -> Bar<T>;",
+ ColumnStyle);
+
+ Style.RequiresClausePosition = FormatStyle::RCPS_WithPreceding;
+ ColumnStyle.RequiresClausePosition = FormatStyle::RCPS_WithPreceding;
+
+ verifyFormat("template <typename T> requires Foo<T>\n"
+ "struct Bar {};\n"
+ "template <typename T> requires Foo<T>\n"
+ "void bar() {}\n"
+ "template <typename T>\n"
+ "void bar() requires Foo<T>\n"
+ "{}\n"
+ "template <typename T> requires Foo<T>\n"
+ "Bar(T) -> Bar<T>;",
+ Style);
+
+ verifyFormat("template <typename AAAAAAA>\n"
+ "requires Foo<AAAAAAAAAAAAAAAA>\n"
+ "struct Bar {};\n"
+ "template <typename AAAAAAA>\n"
+ "requires Foo<AAAAAAAAAAAAAAAA>\n"
+ "void bar() {}\n"
+ "template <typename AAAAAAA>\n"
+ "void bar()\n"
+ " requires Foo<AAAAAAAAAAAAAAAA>\n"
+ "{}\n"
+ "template <typename AAAAAAA>\n"
+ "requires Foo<AAAAAAAA>\n"
+ "Bar(T) -> Bar<T>;\n"
+ "template <typename AAAAAAA>\n"
+ "requires Foo<AAAAAAAAAAAAAAAA>\n"
+ "Bar(T) -> Bar<T>;",
+ ColumnStyle);
}
TEST_F(FormatTest, StatementAttributeLikeMacros) {
diff --git a/clang/unittests/Format/TokenAnnotatorTest.cpp b/clang/unittests/Format/TokenAnnotatorTest.cpp
index acb7386a89df9..0d4be8d788533 100644
--- a/clang/unittests/Format/TokenAnnotatorTest.cpp
+++ b/clang/unittests/Format/TokenAnnotatorTest.cpp
@@ -14,6 +14,14 @@
namespace clang {
namespace format {
+
+// Not really the equality, but everything we need.
+static bool operator==(const FormatToken &LHS,
+ const FormatToken &RHS) noexcept {
+ return LHS.Tok.getKind() == RHS.Tok.getKind() &&
+ LHS.getType() == RHS.getType();
+}
+
namespace {
class TokenAnnotatorTest : public ::testing::Test {
@@ -119,6 +127,261 @@ TEST_F(TokenAnnotatorTest, UnderstandsDelete) {
EXPECT_TOKEN(Tokens[7], tok::r_paren, TT_CastRParen);
}
+TEST_F(TokenAnnotatorTest, UnderstandsRequiresClausesAndConcepts) {
+ auto Tokens = annotate("template <typename T>\n"
+ "concept C = (Foo && Bar) && (Bar && Baz);");
+
+ ASSERT_EQ(Tokens.size(), 21u) << Tokens;
+ EXPECT_TOKEN(Tokens[10], tok::ampamp, TT_BinaryOperator);
+ EXPECT_TOKEN(Tokens[13], tok::ampamp, TT_BinaryOperator);
+ EXPECT_TOKEN(Tokens[16], tok::ampamp, TT_BinaryOperator);
+
+ Tokens = annotate("template <typename T>\n"
+ "concept C = requires(T t) {\n"
+ " { t.foo() };\n"
+ "} && Bar<T> && Baz<T>;");
+ ASSERT_EQ(Tokens.size(), 35u) << Tokens;
+ EXPECT_TOKEN(Tokens[23], tok::ampamp, TT_BinaryOperator);
+ EXPECT_TOKEN(Tokens[28], tok::ampamp, TT_BinaryOperator);
+
+ Tokens = annotate("template<typename T>\n"
+ "requires C1<T> && (C21<T> || C22<T> && C2e<T>) && C3<T>\n"
+ "struct Foo;");
+ ASSERT_EQ(Tokens.size(), 36u) << Tokens;
+ EXPECT_TOKEN(Tokens[6], tok::identifier, TT_Unknown);
+ EXPECT_EQ(Tokens[6]->FakeLParens.size(), 1u);
+ EXPECT_TOKEN(Tokens[10], tok::ampamp, TT_BinaryOperator);
+ EXPECT_TOKEN(Tokens[16], tok::pipepipe, TT_BinaryOperator);
+ EXPECT_TOKEN(Tokens[21], tok::ampamp, TT_BinaryOperator);
+ EXPECT_TOKEN(Tokens[27], tok::ampamp, TT_BinaryOperator);
+ EXPECT_TOKEN(Tokens[31], tok::greater, TT_TemplateCloser);
+ EXPECT_EQ(Tokens[31]->FakeRParens, 1u);
+ EXPECT_TRUE(Tokens[31]->ClosesRequiresClause);
+
+ Tokens =
+ annotate("template<typename T>\n"
+ "requires (C1<T> && (C21<T> || C22<T> && C2e<T>) && C3<T>)\n"
+ "struct Foo;");
+ ASSERT_EQ(Tokens.size(), 38u) << Tokens;
+ EXPECT_TOKEN(Tokens[7], tok::identifier, TT_Unknown);
+ EXPECT_EQ(Tokens[7]->FakeLParens.size(), 1u);
+ EXPECT_TOKEN(Tokens[11], tok::ampamp, TT_BinaryOperator);
+ EXPECT_TOKEN(Tokens[17], tok::pipepipe, TT_BinaryOperator);
+ EXPECT_TOKEN(Tokens[22], tok::ampamp, TT_BinaryOperator);
+ EXPECT_TOKEN(Tokens[28], tok::ampamp, TT_BinaryOperator);
+ EXPECT_TOKEN(Tokens[32], tok::greater, TT_TemplateCloser);
+ EXPECT_EQ(Tokens[32]->FakeRParens, 1u);
+ EXPECT_TOKEN(Tokens[33], tok::r_paren, TT_Unknown);
+ EXPECT_TRUE(Tokens[33]->ClosesRequiresClause);
+}
+
+TEST_F(TokenAnnotatorTest, RequiresDoesNotChangeParsingOfTheRest) {
+ auto NumberOfAdditionalRequiresClauseTokens = 5u;
+ auto NumberOfTokensBeforeRequires = 5u;
+
+ auto BaseTokens = annotate("template<typename T>\n"
+ "T Pi = 3.14;");
+ auto ConstrainedTokens = annotate("template<typename T>\n"
+ " requires Foo<T>\n"
+ "T Pi = 3.14;");
+
+ auto NumberOfBaseTokens = 11u;
+
+ ASSERT_EQ(BaseTokens.size(), NumberOfBaseTokens) << BaseTokens;
+ ASSERT_EQ(ConstrainedTokens.size(),
+ NumberOfBaseTokens + NumberOfAdditionalRequiresClauseTokens)
+ << ConstrainedTokens;
+
+ for (auto I = 0u; I < NumberOfBaseTokens; ++I)
+ if (I < NumberOfTokensBeforeRequires)
+ EXPECT_EQ(*BaseTokens[I], *ConstrainedTokens[I]) << I;
+ else
+ EXPECT_EQ(*BaseTokens[I],
+ *ConstrainedTokens[I + NumberOfAdditionalRequiresClauseTokens])
+ << I;
+
+ BaseTokens = annotate("template<typename T>\n"
+ "struct Bar;");
+ ConstrainedTokens = annotate("template<typename T>\n"
+ " requires Foo<T>\n"
+ "struct Bar;");
+ NumberOfBaseTokens = 9u;
+
+ ASSERT_EQ(BaseTokens.size(), NumberOfBaseTokens) << BaseTokens;
+ ASSERT_EQ(ConstrainedTokens.size(),
+ NumberOfBaseTokens + NumberOfAdditionalRequiresClauseTokens)
+ << ConstrainedTokens;
+
+ for (auto I = 0u; I < NumberOfBaseTokens; ++I)
+ if (I < NumberOfTokensBeforeRequires)
+ EXPECT_EQ(*BaseTokens[I], *ConstrainedTokens[I]) << I;
+ else
+ EXPECT_EQ(*BaseTokens[I],
+ *ConstrainedTokens[I + NumberOfAdditionalRequiresClauseTokens])
+ << I;
+
+ BaseTokens = annotate("template<typename T>\n"
+ "struct Bar {"
+ " T foo();\n"
+ " T bar();\n"
+ "};");
+ ConstrainedTokens = annotate("template<typename T>\n"
+ " requires Foo<T>\n"
+ "struct Bar {"
+ " T foo();\n"
+ " T bar();\n"
+ "};");
+ NumberOfBaseTokens = 21u;
+
+ ASSERT_EQ(BaseTokens.size(), NumberOfBaseTokens) << BaseTokens;
+ ASSERT_EQ(ConstrainedTokens.size(),
+ NumberOfBaseTokens + NumberOfAdditionalRequiresClauseTokens)
+ << ConstrainedTokens;
+
+ for (auto I = 0u; I < NumberOfBaseTokens; ++I)
+ if (I < NumberOfTokensBeforeRequires)
+ EXPECT_EQ(*BaseTokens[I], *ConstrainedTokens[I]) << I;
+ else
+ EXPECT_EQ(*BaseTokens[I],
+ *ConstrainedTokens[I + NumberOfAdditionalRequiresClauseTokens])
+ << I;
+
+ BaseTokens = annotate("template<typename T>\n"
+ "Bar(T) -> Bar<T>;");
+ ConstrainedTokens = annotate("template<typename T>\n"
+ " requires Foo<T>\n"
+ "Bar(T) -> Bar<T>;");
+ NumberOfBaseTokens = 16u;
+
+ ASSERT_EQ(BaseTokens.size(), NumberOfBaseTokens) << BaseTokens;
+ ASSERT_EQ(ConstrainedTokens.size(),
+ NumberOfBaseTokens + NumberOfAdditionalRequiresClauseTokens)
+ << ConstrainedTokens;
+
+ for (auto I = 0u; I < NumberOfBaseTokens; ++I)
+ if (I < NumberOfTokensBeforeRequires)
+ EXPECT_EQ(*BaseTokens[I], *ConstrainedTokens[I]) << I;
+ else
+ EXPECT_EQ(*BaseTokens[I],
+ *ConstrainedTokens[I + NumberOfAdditionalRequiresClauseTokens])
+ << I;
+
+ BaseTokens = annotate("template<typename T>\n"
+ "T foo();");
+ ConstrainedTokens = annotate("template<typename T>\n"
+ " requires Foo<T>\n"
+ "T foo();");
+ NumberOfBaseTokens = 11u;
+
+ ASSERT_EQ(BaseTokens.size(), NumberOfBaseTokens) << BaseTokens;
+ ASSERT_EQ(ConstrainedTokens.size(),
+ NumberOfBaseTokens + NumberOfAdditionalRequiresClauseTokens)
+ << ConstrainedTokens;
+
+ for (auto I = 0u; I < NumberOfBaseTokens; ++I)
+ if (I < NumberOfTokensBeforeRequires)
+ EXPECT_EQ(*BaseTokens[I], *ConstrainedTokens[I]) << I;
+ else
+ EXPECT_EQ(*BaseTokens[I],
+ *ConstrainedTokens[I + NumberOfAdditionalRequiresClauseTokens])
+ << I;
+
+ BaseTokens = annotate("template<typename T>\n"
+ "T foo() {\n"
+ " auto bar = baz();\n"
+ " return bar + T{};\n"
+ "}");
+ ConstrainedTokens = annotate("template<typename T>\n"
+ " requires Foo<T>\n"
+ "T foo() {\n"
+ " auto bar = baz();\n"
+ " return bar + T{};\n"
+ "}");
+ NumberOfBaseTokens = 26u;
+
+ ASSERT_EQ(BaseTokens.size(), NumberOfBaseTokens) << BaseTokens;
+ ASSERT_EQ(ConstrainedTokens.size(),
+ NumberOfBaseTokens + NumberOfAdditionalRequiresClauseTokens)
+ << ConstrainedTokens;
+
+ for (auto I = 0u; I < NumberOfBaseTokens; ++I)
+ if (I < NumberOfTokensBeforeRequires)
+ EXPECT_EQ(*BaseTokens[I], *ConstrainedTokens[I]) << I;
+ else
+ EXPECT_EQ(*BaseTokens[I],
+ *ConstrainedTokens[I + NumberOfAdditionalRequiresClauseTokens])
+ << I;
+
+ BaseTokens = annotate("template<typename T>\n"
+ "T foo();");
+ ConstrainedTokens = annotate("template<typename T>\n"
+ "T foo() requires Foo<T>;");
+ NumberOfBaseTokens = 11u;
+ NumberOfTokensBeforeRequires = 9u;
+
+ ASSERT_EQ(BaseTokens.size(), NumberOfBaseTokens) << BaseTokens;
+ ASSERT_EQ(ConstrainedTokens.size(),
+ NumberOfBaseTokens + NumberOfAdditionalRequiresClauseTokens)
+ << ConstrainedTokens;
+
+ for (auto I = 0u; I < NumberOfBaseTokens; ++I)
+ if (I < NumberOfTokensBeforeRequires)
+ EXPECT_EQ(*BaseTokens[I], *ConstrainedTokens[I]) << I;
+ else
+ EXPECT_EQ(*BaseTokens[I],
+ *ConstrainedTokens[I + NumberOfAdditionalRequiresClauseTokens])
+ << I;
+
+ BaseTokens = annotate("template<typename T>\n"
+ "T foo() {\n"
+ " auto bar = baz();\n"
+ " return bar + T{};\n"
+ "}");
+ ConstrainedTokens = annotate("template<typename T>\n"
+ "T foo() requires Foo<T> {\n"
+ " auto bar = baz();\n"
+ " return bar + T{};\n"
+ "}");
+ NumberOfBaseTokens = 26u;
+
+ ASSERT_EQ(BaseTokens.size(), NumberOfBaseTokens) << BaseTokens;
+ ASSERT_EQ(ConstrainedTokens.size(),
+ NumberOfBaseTokens + NumberOfAdditionalRequiresClauseTokens)
+ << ConstrainedTokens;
+
+ for (auto I = 0u; I < NumberOfBaseTokens; ++I)
+ if (I < NumberOfTokensBeforeRequires)
+ EXPECT_EQ(*BaseTokens[I], *ConstrainedTokens[I]) << I;
+ else
+ EXPECT_EQ(*BaseTokens[I],
+ *ConstrainedTokens[I + NumberOfAdditionalRequiresClauseTokens])
+ << I;
+
+ BaseTokens = annotate("template<typename T>\n"
+ "Bar(T) -> Bar<typename T::I>;");
+ ConstrainedTokens = annotate("template<typename T>\n"
+ " requires requires(T &&t) {\n"
+ " typename T::I;\n"
+ " }\n"
+ "Bar(T) -> Bar<typename T::I>;");
+ NumberOfBaseTokens = 19u;
+ NumberOfAdditionalRequiresClauseTokens = 14u;
+ NumberOfTokensBeforeRequires = 5u;
+
+ ASSERT_EQ(BaseTokens.size(), NumberOfBaseTokens) << BaseTokens;
+ ASSERT_EQ(ConstrainedTokens.size(),
+ NumberOfBaseTokens + NumberOfAdditionalRequiresClauseTokens)
+ << ConstrainedTokens;
+
+ for (auto I = 0u; I < NumberOfBaseTokens; ++I)
+ if (I < NumberOfTokensBeforeRequires)
+ EXPECT_EQ(*BaseTokens[I], *ConstrainedTokens[I]) << I;
+ else
+ EXPECT_EQ(*BaseTokens[I],
+ *ConstrainedTokens[I + NumberOfAdditionalRequiresClauseTokens])
+ << I;
+}
+
} // namespace
} // namespace format
} // namespace clang
More information about the cfe-commits
mailing list