mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-03-07 10:00:27 +00:00
[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:
parent
ccc740353d
commit
9aab0db13f
@ -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.
|
||||
|
@ -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
|
||||
--------
|
||||
|
||||
|
@ -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 &&
|
||||
|
@ -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,
|
||||
|
@ -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))
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user