[llvm-branch-commits] [clang] 840e651 - [clang-format] Improve clang-formats handling of concepts
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Dec 4 09:50:15 PST 2020
Author: mydeveloperday
Date: 2020-12-04T17:45:50Z
New Revision: 840e651dc6d7fe652667eb8b4d04ef4daf4769df
URL: https://github.com/llvm/llvm-project/commit/840e651dc6d7fe652667eb8b4d04ef4daf4769df
DIFF: https://github.com/llvm/llvm-project/commit/840e651dc6d7fe652667eb8b4d04ef4daf4769df.diff
LOG: [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
Added:
Modified:
clang/docs/ClangFormatStyleOptions.rst
clang/docs/ReleaseNotes.rst
clang/include/clang/Format/Format.h
clang/lib/Format/Format.cpp
clang/lib/Format/FormatToken.h
clang/lib/Format/TokenAnnotator.cpp
clang/lib/Format/UnwrappedLineParser.cpp
clang/lib/Format/UnwrappedLineParser.h
clang/unittests/Format/FormatTest.cpp
Removed:
################################################################################
diff --git a/clang/docs/ClangFormatStyleOptions.rst b/clang/docs/ClangFormatStyleOptions.rst
index 6c5556a94391..d6d437a0a05e 100644
--- a/clang/docs/ClangFormatStyleOptions.rst
+++ b/clang/docs/ClangFormatStyleOptions.rst
@@ -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.
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index d62c62dad3d2..d90d059e0659 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -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
--------
diff --git a/clang/include/clang/Format/Format.h b/clang/include/clang/Format/Format.h
index dab4cbbbdfe1..6e304630fbdc 100644
--- a/clang/include/clang/Format/Format.h
+++ b/clang/include/clang/Format/Format.h
@@ -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 &&
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp
index 6f96395f608d..f5ae35131ace 100644
--- a/clang/lib/Format/Format.cpp
+++ b/clang/lib/Format/Format.cpp
@@ -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;
diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h
index d53c016fd4db..a6d7102f0ac3 100644
--- a/clang/lib/Format/FormatToken.h
+++ b/clang/lib/Format/FormatToken.h
@@ -40,6 +40,7 @@ namespace format {
TYPE(ConflictAlternative) \
TYPE(ConflictEnd) \
TYPE(ConflictStart) \
+ TYPE(ConstraintJunctions) \
TYPE(CtorInitializerColon) \
TYPE(CtorInitializerComma) \
TYPE(DesignatedInitializerLSquare) \
@@ -590,6 +591,7 @@ struct FormatToken {
case tok::kw__Atomic:
case tok::kw___attribute:
case tok::kw___underlying_type:
+ case tok::kw_requires:
return true;
default:
return false;
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 0fdcca867e3d..b1c9c37a8ed9 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -1367,7 +1367,7 @@ class AnnotatingParser {
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 @@ class AnnotatingParser {
!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 @@ class AnnotatingParser {
// 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 @@ class AnnotatingParser {
// 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)
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index 044f034013dc..3803119a2096 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -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))
diff --git a/clang/lib/Format/UnwrappedLineParser.h b/clang/lib/Format/UnwrappedLineParser.h
index d682b4d6acd8..02b328cb72de 100644
--- a/clang/lib/Format/UnwrappedLineParser.h
+++ b/clang/lib/Format/UnwrappedLineParser.h
@@ -113,6 +113,10 @@ class UnwrappedLineParser {
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
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 7addb5220ae7..8390fea3f417 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -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
More information about the llvm-branch-commits
mailing list