[clang-format] Improve clang-formats handling of concepts

This is a starting point to improve the handling of concepts in clang-format. There is currently no real formatting of concepts and this can lead to some odd formatting, e.g.

Reviewed By: mitchell-stellar, miscco, curdeius

Differential Revision: https://reviews.llvm.org/D79773
This commit is contained in:
mydeveloperday 2020-12-04 17:44:25 +00:00
parent c36801ecd5
commit 840e651dc6
9 changed files with 507 additions and 10 deletions

View File

@ -1382,6 +1382,18 @@ the configuration (without a prefix: ``Auto``).
**BreakBeforeConceptDeclarations** (``bool``)
If ``true``, concept will be placed on a new line.
.. code-block:: c++
true:
template<typename T>
concept ...
false:
template<typename T> concept ...
**BreakBeforeTernaryOperators** (``bool``)
If ``true``, ternary operators will be placed after line breaks.
@ -1901,6 +1913,25 @@ the configuration (without a prefix: ``Auto``).
**IndentRequires** (``bool``)
Indent the requires clause in a template
.. code-block:: c++
true:
template <typename It>
requires Iterator<It>
void sort(It begin, It end) {
//....
}
false:
template <typename It>
requires Iterator<It>
void sort(It begin, It end) {
//....
}
**IndentWidth** (``unsigned``)
The number of columns to use for indentation.

View File

@ -271,6 +271,14 @@ clang-format
};
- Experimental Support in clang-format for concepts has been improved, to
aid this the follow options have been added
- Option ``IndentRequires`` has been added to indent the ``requires`` keyword
in templates.
- Option ``BreakBeforeConceptDeclarations`` has been added to aid the formatting of concepts.
libclang
--------

View File

@ -1160,6 +1160,17 @@ struct FormatStyle {
/// \endcode
BraceWrappingFlags BraceWrapping;
/// If ``true``, concept will be placed on a new line.
/// \code
/// true:
/// template<typename T>
/// concept ...
///
/// false:
/// template<typename T> concept ...
/// \endcode
bool BreakBeforeConceptDeclarations;
/// If ``true``, ternary operators will be placed after line breaks.
/// \code
/// true:
@ -1590,6 +1601,24 @@ struct FormatStyle {
/// IndentExternBlockStyle is the type of indenting of extern blocks.
IndentExternBlockStyle IndentExternBlock;
/// Indent the requires clause in a template
/// \code
/// true:
/// template <typename It>
/// requires Iterator<It>
/// void sort(It begin, It end) {
/// //....
/// }
///
/// false:
/// template <typename It>
/// requires Iterator<It>
/// void sort(It begin, It end) {
/// //....
/// }
/// \endcode
bool IndentRequires;
/// The number of columns to use for indentation.
/// \code
/// IndentWidth: 3
@ -2435,6 +2464,7 @@ struct FormatStyle {
BinPackParameters == R.BinPackParameters &&
BreakBeforeBinaryOperators == R.BreakBeforeBinaryOperators &&
BreakBeforeBraces == R.BreakBeforeBraces &&
BreakBeforeConceptDeclarations == R.BreakBeforeConceptDeclarations &&
BreakBeforeTernaryOperators == R.BreakBeforeTernaryOperators &&
BreakConstructorInitializers == R.BreakConstructorInitializers &&
CompactNamespaces == R.CompactNamespaces &&
@ -2466,7 +2496,8 @@ struct FormatStyle {
IndentGotoLabels == R.IndentGotoLabels &&
IndentPPDirectives == R.IndentPPDirectives &&
IndentExternBlock == R.IndentExternBlock &&
IndentWidth == R.IndentWidth && Language == R.Language &&
IndentRequires == R.IndentRequires && IndentWidth == R.IndentWidth &&
Language == R.Language &&
IndentWrappedFunctionNames == R.IndentWrappedFunctionNames &&
JavaImportGroups == R.JavaImportGroups &&
JavaScriptQuotes == R.JavaScriptQuotes &&

View File

@ -501,6 +501,8 @@ template <> struct MappingTraits<FormatStyle> {
IO.mapOptional("BraceWrapping", Style.BraceWrapping);
IO.mapOptional("BreakBeforeBinaryOperators",
Style.BreakBeforeBinaryOperators);
IO.mapOptional("BreakBeforeConceptDeclarations",
Style.BreakBeforeConceptDeclarations);
IO.mapOptional("BreakBeforeBraces", Style.BreakBeforeBraces);
bool BreakBeforeInheritanceComma = false;
@ -557,6 +559,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("IndentWidth", Style.IndentWidth);
IO.mapOptional("IndentWrappedFunctionNames",
Style.IndentWrappedFunctionNames);
@ -872,6 +875,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.BinPackArguments = true;
LLVMStyle.BinPackParameters = true;
LLVMStyle.BreakBeforeBinaryOperators = FormatStyle::BOS_None;
LLVMStyle.BreakBeforeConceptDeclarations = true;
LLVMStyle.BreakBeforeTernaryOperators = true;
LLVMStyle.BreakBeforeBraces = FormatStyle::BS_Attach;
LLVMStyle.BraceWrapping = {/*AfterCaseLabel=*/false,
@ -921,6 +925,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
LLVMStyle.IndentCaseBlocks = false;
LLVMStyle.IndentGotoLabels = true;
LLVMStyle.IndentPPDirectives = FormatStyle::PPDIS_None;
LLVMStyle.IndentRequires = false;
LLVMStyle.IndentWrappedFunctionNames = false;
LLVMStyle.IndentWidth = 2;
LLVMStyle.InsertTrailingCommas = FormatStyle::TCS_None;

View File

@ -40,6 +40,7 @@ namespace format {
TYPE(ConflictAlternative) \
TYPE(ConflictEnd) \
TYPE(ConflictStart) \
TYPE(ConstraintJunctions) \
TYPE(CtorInitializerColon) \
TYPE(CtorInitializerComma) \
TYPE(DesignatedInitializerLSquare) \
@ -590,6 +591,7 @@ public:
case tok::kw__Atomic:
case tok::kw___attribute:
case tok::kw___underlying_type:
case tok::kw_requires:
return true;
default:
return false;

View File

@ -1367,7 +1367,7 @@ private:
TT_ImplicitStringLiteral, TT_InlineASMBrace, TT_JsFatArrow,
TT_LambdaArrow, TT_NamespaceMacro, TT_OverloadedOperator,
TT_RegexLiteral, TT_TemplateString, TT_ObjCStringLiteral,
TT_UntouchableMacroFunc))
TT_UntouchableMacroFunc, TT_ConstraintJunctions))
CurrentToken->setType(TT_Unknown);
CurrentToken->Role.reset();
CurrentToken->MatchingParen = nullptr;
@ -1621,7 +1621,11 @@ private:
!Current.Previous->is(tok::kw_operator)) {
// not auto operator->() -> xxx;
Current.setType(TT_TrailingReturnArrow);
} else if (Current.is(tok::arrow) && Current.Previous &&
Current.Previous->is(tok::r_brace)) {
// Concept implicit conversion contraint needs to be treated like
// a trailing return type ... } -> <type>.
Current.setType(TT_TrailingReturnArrow);
} else if (isDeductionGuide(Current)) {
// Deduction guides trailing arrow " A(...) -> A<T>;".
Current.setType(TT_TrailingReturnArrow);
@ -1722,8 +1726,8 @@ private:
// colon after this, this is the only place which annotates the identifier
// as a selector.)
Current.setType(TT_SelectorName);
} else if (Current.isOneOf(tok::identifier, tok::kw_const,
tok::kw_noexcept) &&
} else if (Current.isOneOf(tok::identifier, tok::kw_const, tok::kw_noexcept,
tok::kw_requires) &&
Current.Previous &&
!Current.Previous->isOneOf(tok::equal, tok::at) &&
Line.MightBeFunctionDecl && Contexts.size() == 1) {
@ -1839,8 +1843,8 @@ private:
// Functions which end with decorations like volatile, noexcept are unlikely
// to be casts.
if (Tok.Next->isOneOf(tok::kw_noexcept, tok::kw_volatile, tok::kw_const,
tok::kw_throw, tok::arrow, Keywords.kw_override,
Keywords.kw_final) ||
tok::kw_requires, tok::kw_throw, tok::arrow,
Keywords.kw_override, Keywords.kw_final) ||
isCpp11AttributeSpecifier(*Tok.Next))
return false;
@ -2817,6 +2821,14 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
isKeywordWithCondition(*Right.MatchingParen->Previous))
return true;
}
// requires ( or requires(
if (Right.is(tok::l_paren) && Left.is(tok::kw_requires))
return spaceRequiredBeforeParens(Right);
// 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) ||
(Left.MatchingParen && Left.MatchingParen->is(TT_CastRParen)))
@ -3594,11 +3606,17 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
Right.Previous->is(tok::string_literal) &&
Right.Next->is(tok::string_literal))
return true;
// Can break after template<> declaration
if (Right.Previous->ClosesTemplateDeclaration &&
Right.Previous->MatchingParen &&
Right.Previous->MatchingParen->NestingLevel == 0 &&
Style.AlwaysBreakTemplateDeclarations == FormatStyle::BTDS_Yes)
return true;
Right.Previous->MatchingParen->NestingLevel == 0) {
// Put concepts on the next line e.g.
// template<typename T>
// concept ...
if (Right.is(tok::kw_concept))
return Style.BreakBeforeConceptDeclarations;
return (Style.AlwaysBreakTemplateDeclarations == FormatStyle::BTDS_Yes);
}
if (Right.is(TT_CtorInitializerComma) &&
Style.BreakConstructorInitializers == FormatStyle::BCIS_BeforeComma &&
!Style.ConstructorInitializerAllOnOneLineOrOnePerLine)

View File

@ -626,8 +626,16 @@ void UnwrappedLineParser::parseBlock(bool MustBeDeclaration, bool AddLevel,
if (MacroBlock && FormatTok->is(tok::l_paren))
parseParens();
if (FormatTok->is(tok::arrow)) {
// Following the } we can find a trailing return type arrow
// as part of an implicit conversion constraint.
nextToken();
parseStructuralElement();
}
if (MunchSemi && FormatTok->Tok.is(tok::semi))
nextToken();
Line->Level = InitialLevel;
if (PPStartHash == PPEndHash) {
@ -1262,6 +1270,12 @@ void UnwrappedLineParser::parseStructuralElement() {
break;
}
break;
case tok::kw_concept:
parseConcept();
break;
case tok::kw_requires:
parseRequires();
break;
case tok::kw_enum:
// Ignore if this is part of "template <enum ...".
if (Previous && Previous->is(tok::less)) {
@ -2279,6 +2293,117 @@ void UnwrappedLineParser::parseAccessSpecifier() {
addUnwrappedLine();
}
void UnwrappedLineParser::parseConcept() {
assert(FormatTok->Tok.is(tok::kw_concept) && "'concept' expected");
nextToken();
if (!FormatTok->Tok.is(tok::identifier))
return;
nextToken();
if (!FormatTok->Tok.is(tok::equal))
return;
nextToken();
if (FormatTok->Tok.is(tok::kw_requires)) {
nextToken();
parseRequiresExpression(Line->Level);
} else {
parseConstraintExpression(Line->Level);
}
}
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(/*MustBeDeclaration=*/false);
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(/*MustBeDeclaration=*/false);
}
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() {
assert(FormatTok->Tok.is(tok::kw_requires) && "'requires' expected");
unsigned OriginalLevel = Line->Level;
if (FormatTok->Previous && FormatTok->Previous->is(tok::greater)) {
addUnwrappedLine();
if (Style.IndentRequires) {
Line->Level++;
}
}
nextToken();
parseRequiresExpression(OriginalLevel);
}
bool UnwrappedLineParser::parseEnum() {
// Won't be 'enum' for NS_ENUMs.
if (FormatTok->Tok.is(tok::kw_enum))

View File

@ -113,6 +113,10 @@ private:
void parseNew();
void parseAccessSpecifier();
bool parseEnum();
void parseConcept();
void parseRequires();
void parseRequiresExpression(unsigned int OriginalLevel);
void parseConstraintExpression(unsigned int OriginalLevel);
void parseJavaEnumBody();
// Parses a record (aka class) as a top level element. If ParseAsExpr is true,
// parses the record as a child block, i.e. if the class declaration is an

View File

@ -14104,6 +14104,7 @@ 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);
@ -14115,6 +14116,7 @@ TEST_F(FormatTest, ParsesConfigurationBools) {
CHECK_PARSE_BOOL(IndentCaseLabels);
CHECK_PARSE_BOOL(IndentCaseBlocks);
CHECK_PARSE_BOOL(IndentGotoLabels);
CHECK_PARSE_BOOL(IndentRequires);
CHECK_PARSE_BOOL(IndentWrappedFunctionNames);
CHECK_PARSE_BOOL(KeepEmptyLinesAtTheStartOfBlocks);
CHECK_PARSE_BOOL(ObjCSpaceAfterProperty);
@ -17288,6 +17290,277 @@ TEST_F(FormatTest, WebKitDefaultStyle) {
"}",
Style);
}
TEST_F(FormatTest, ConceptsAndRequires) {
FormatStyle Style = getLLVMStyle();
Style.AllowShortFunctionsOnASingleLine = FormatStyle::SFS_None;
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);
verifyFormat("template <typename It>\n"
"requires Iterator<It>\n"
"void sort(It begin, It end) {\n"
" //....\n"
"}",
Style);
verifyFormat("template <typename T>\n"
"concept Large = sizeof(T) > 10;",
Style);
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);
verifyFormat("template <typename T>\n"
"veryveryvery_long_return_type g(T i) requires Concept1<I> || "
"Concept2<I> {\n"
" //...\n"
"}",
Style);
verifyFormat("template <typename T>\n"
"veryveryvery_long_return_type g(T i) requires Concept1<I> && "
"Concept2<I> {\n"
" //...\n"
"}",
Style);
verifyFormat(
"template <typename T>\n"
"veryveryvery_long_return_type g(T i) requires Concept1 && Concept2 {\n"
" //...\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"
"};",
Style);
verifyFormat("template <class T = void>\n"
" requires EqualityComparable<T> || Same<T, void>\n"
"struct equal_to;",
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",
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",
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"
"{\n"
" return\n"
"}\n",
Style);
verifyFormat("void Foo () requires std::copyable<T>\n"
"{\n"
" return\n"
"}\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 <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"
"{\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",
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);
Style.BreakBeforeConceptDeclarations = false;
verifyFormat("template <typename T> concept Tree = true;", Style);
Style.IndentRequires = false;
verifyFormat("template <std::semiregular F, std::semiregular... Args>\n"
"requires (std::invocable<F, std::invoke_result_t<Args>...>) "
"struct constant;",
Style);
}
} // namespace
} // namespace format
} // namespace clang