[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
This commit is contained in:
Björn Schäpers 2021-11-05 21:10:09 +01:00
parent ccc740353d
commit 9aab0db13f
11 changed files with 1534 additions and 423 deletions

View File

@ -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.

View File

@ -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
--------

View File

@ -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 &&

View File

@ -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
// different 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,

View File

@ -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))

View File

@ -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;

View File

@ -1019,7 +1019,7 @@ private:
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 @@ private:
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 @@ private:
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 @@ private:
PriorLeadingIdentifier = PriorLeadingIdentifier->Previous;
return (PriorLeadingIdentifier &&
PriorLeadingIdentifier->is(TT_TemplateCloser) &&
(PriorLeadingIdentifier->is(TT_TemplateCloser) ||
PriorLeadingIdentifier->ClosesRequiresClause) &&
LeadingIdentifier->TokenText == Current.Next->TokenText);
}
}
@ -1826,6 +1834,9 @@ private:
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 @@ public:
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 @@ public:
}
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 @@ private:
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 @@ private:
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;

View File

@ -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 different 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)) {
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);
}
}
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)) {
parseBracedList(/*ContinueOnSemicolons=*/false, /*IsEnum=*/false,
/*ClosingBraceKind=*/tok::greater);
continue;
}
nextToken();
}
if (FormatTok->Tok.is(tok::kw_requires))
parseRequiresExpression(OriginalLevel);
if (FormatTok->Tok.is(tok::less)) {
parseBracedList(/*ContinueOnSemicolons=*/false, /*IsEnum=*/false,
/*ClosingBraceKind=*/tok::greater);
}
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.
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();
}
}
void UnwrappedLineParser::parseRequires() {
/// \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);
unsigned OriginalLevel = Line->Level;
if (FormatTok->Previous && FormatTok->Previous->is(tok::greater)) {
addUnwrappedLine();
if (Style.IndentRequires)
++Line->Level;
}
// 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();
parseRequiresExpression(OriginalLevel);
if (FormatTok->is(tok::l_paren)) {
FormatTok->setType(TT_RequiresExpressionLParen);
parseParens();
}
if (FormatTok->is(tok::l_brace)) {
FormatTok->setType(TT_RequiresExpressionLBrace);
parseChildBlock(/*CanContainBracedList=*/false,
/*NextLBracesType=*/TT_CompoundRequirementLBrace);
}
}
/// \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 differentiate 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);
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();
break;
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();
break;
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;
parseBracedList(/*ContinueOnSemicolons=*/false, /*IsEnum=*/false,
/*ClosingBraceKind=*/tok::greater);
break;
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;
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();

View File

@ -92,11 +92,16 @@ private:
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 @@ private:
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 @@ private:
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

View File

@ -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 C = ((false || foo()) && C2<T>) ||\n"
" (std::trait<T>::value && Baz) || sizeof(T) >= 6;",
getLLVMStyleWithColumns(60));
verifyFormat("template <typename T>\n"
"concept DelayedCheck = true && requires(T t) { t.bar(); } && "
"sizeof(T) <= 8;");
verifyFormat("template <typename T>\n"
"concept DelayedCheck = true && requires(T t) {\n"
" t.bar();\n"
" t.baz();\n"
" } && sizeof(T) <= 8;");
verifyFormat("template <typename T>\n"
"concept DelayedCheck = true && requires(T t) { // Comment\n"
" t.bar();\n"
" t.baz();\n"
" } && sizeof(T) <= 8;");
verifyFormat("template <typename T>\n"
"concept DelayedCheck = false || requires(T t) { t.bar(); } && "
"sizeof(T) <= 8;");
verifyFormat("template <typename T>\n"
"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>\n"
"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 DelayedCheck = (bool)0 || requires(T t) { t.bar(); } "
"&& sizeof(T) <= 8;");
verifyFormat("template <typename T>\n"
"concept Size = sizeof(T) >= 5 && requires(T t) { t.bar(); } && "
"sizeof(T) <= 8;");
verifyFormat("template <typename T>\n"
"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>\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>\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>\n"
"concept C = decltype([]() { return std::true_type{}; }())::value &&\n"
" requires(T t) { t.bar(); } && sizeof(T) <= 8;");
verifyFormat("template <typename T>\n"
"concept C = decltype([]() { return std::true_type{}; "
"}())::value && requires(T t) { t.bar(); } && sizeof(T) <= 8;",
getLLVMStyleWithColumns(120));
verifyFormat("template <typename T>\n"
"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"
"};",
Style);
verifyFormat("template <typename T>\n"
"concept EqualityComparable = requires(T a, T b) {\n"
" { a == b } -> bool;\n"
"};",
Style);
verifyFormat("template <typename T>\n"
"concept EqualityComparable = requires(T a, T b) {\n"
" { a == b } -> bool;\n"
" { a != b } -> bool;\n"
"};",
Style);
verifyFormat("template <typename T>\n"
"concept EqualityComparable = requires(T a, T b) {\n"
" { a == b } -> bool;\n"
" { a != b } -> bool;\n"
"};",
Style);
" { std::hash<T>{}(a) } -> "
"std::convertible_to<std::size_t>;\n"
" };");
verifyFormat("template <typename It>\n"
"requires Iterator<It>\n"
"void sort(It begin, It end) {\n"
" //....\n"
"}",
Style);
verifyFormat(
"template <typename T>\n"
"concept EqualityComparable = requires(T a, T b) {\n"
" { a == b } -> std::same_as<bool>;\n"
" };");
verifyFormat(
"template <typename T>\n"
"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 T>\n"
"concept Large = sizeof(T) > 10;",
Style);
"concept WeakEqualityComparable = requires(T a, T b) {\n"
" { a == b };\n"
" { a != b };\n"
" };");
verifyFormat("template <typename T>\n"
"concept HasSizeT = requires { typename T::size_t; };");
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 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) {\n"
" t.foo(3);\n"
"}",
Style);
verifyFormat("template <typename T>\n"
"concept Context = sizeof(T) == 1;",
Style);
verifyFormat("template <typename T>\n"
"concept Context = is_specialization_of_v<context, T>;",
Style);
verifyFormat("template <typename T>\n"
"concept Node = std::is_object_v<T>;",
Style);
verifyFormat("template <typename T>\n"
"concept Tree = true;",
Style);
verifyFormat("template <typename T> int g(T i) requires Concept1<I> {\n"
" //...\n"
"}",
Style);
verifyFormat(
"template <typename T> int g(T i) requires Concept1<I> && Concept2<I> {\n"
" //...\n"
"}",
Style);
verifyFormat(
"template <typename T> int g(T i) requires Concept1<I> || Concept2<I> {\n"
" //...\n"
"}",
Style);
" 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"
"veryveryvery_long_return_type g(T i) requires Concept1<I> || "
"Concept2<I> {\n"
" //...\n"
"}",
Style);
"concept Context = is_specialization_of_v<context, T>;");
verifyFormat("template <typename T>\n"
"veryveryvery_long_return_type g(T i) requires Concept1<I> && "
"Concept2<I> {\n"
" //...\n"
"}",
Style);
"concept Node = std::is_object_v<T>;");
auto Style = getLLVMStyle();
Style.BreakBeforeConceptDeclarations = FormatStyle::BBCDS_Allowed;
verifyFormat(
"template <typename T>\n"
"veryveryvery_long_return_type g(T i) requires Concept1 && Concept2 {\n"
" //...\n"
"}",
"concept C = requires(T t) {\n"
" requires Bar<T> && Foo<T>;\n"
" requires((trait<T> && Baz) || (T2<T> && Foo<T>));\n"
" };",
Style);
verifyFormat(
"template <typename T>\n"
"veryveryvery_long_return_type g(T i) requires Concept1 || Concept2 {\n"
" //...\n"
"}",
Style);
verifyFormat("template <typename It>\n"
"requires Foo<It>() && Bar<It> {\n"
" //....\n"
"}",
Style);
verifyFormat("template <typename It>\n"
"requires Foo<Bar<It>>() && Bar<Foo<It, It>> {\n"
" //....\n"
"}",
Style);
verifyFormat("template <typename It>\n"
"requires Foo<Bar<It, It>>() && Bar<Foo<It, It>> {\n"
" //....\n"
"}",
Style);
verifyFormat(
"template <typename It>\n"
"requires Foo<Bar<It>, Baz<It>>() && Bar<Foo<It>, Baz<It, It>> {\n"
" //....\n"
"}",
Style);
Style.IndentRequires = true;
verifyFormat("template <typename It>\n"
" requires Iterator<It>\n"
"void sort(It begin, It end) {\n"
" //....\n"
"}",
Style);
verifyFormat("template <std::size index_>\n"
" requires(index_ < sizeof...(Children_))\n"
"Tree auto &child() {\n"
" // ...\n"
"}",
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 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 <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);
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"
" };",
Style);
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 <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);
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 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"
" requires(Foo<T> && std::trait<T>)\n"
"class Bar {\n"
"public:\n"
" Bar(T t);\n"
" bool baz();\n"
"};",
Style);
verifyFormat("template <class T = void>\n"
" requires EqualityComparable<T> || Same<T, void>\n"
"struct equal_to;",
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 <class T>\n"
" requires requires {\n"
" T{};\n"
" T (int);\n"
" }\n",
Style);
Style.ColumnLimit = 78;
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",
" requires(Foo<T> && std::trait<T>)\n"
"constexpr T MyGlobal;",
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> && 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);
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",
Style);
verifyFormat("template <typename T>\nconcept someConcept = Constraint1<T> && "
"Constraint2<T>;");
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"
"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"
verifyFormat("template <typename T>\n"
" requires F<T>\n"
"int bar(T t) {\n"
" return 5;\n"
"}",
Style);
verifyFormat("template <typename T>\n"
"int bar(T t)\n"
" requires F<T>\n"
"{\n"
" return\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 <std::semiregular F, std::semiregular... Args>\n"
" requires std::invocable<F, std::invoke_result_t<Args>...>\n"
"struct constant;",
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);
Style.BreakBeforeConceptDeclarations = false;
verifyFormat("template <typename T> concept Tree = true;", 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);
Style.IndentRequires = false;
verifyFormat("template <std::semiregular F, std::semiregular... Args>\n"
"requires (std::invocable<F, std::invoke_result_t<Args>...>) "
"struct constant;",
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);
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.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) {

View File

@ -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