Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit 840e651

Browse files
[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
1 parent c36801e commit 840e651

File tree

9 files changed

+507
-10
lines changed

9 files changed

+507
-10
lines changed

clang/docs/ClangFormatStyleOptions.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1382,6 +1382,18 @@ the configuration (without a prefix: ``Auto``).
13821382

13831383

13841384

1385+
**BreakBeforeConceptDeclarations** (``bool``)
1386+
If ``true``, concept will be placed on a new line.
1387+
1388+
.. code-block:: c++
1389+
1390+
true:
1391+
template<typename T>
1392+
concept ...
1393+
1394+
false:
1395+
template<typename T> concept ...
1396+
13851397
**BreakBeforeTernaryOperators** (``bool``)
13861398
If ``true``, ternary operators will be placed after line breaks.
13871399

@@ -1901,6 +1913,25 @@ the configuration (without a prefix: ``Auto``).
19011913

19021914

19031915

1916+
**IndentRequires** (``bool``)
1917+
Indent the requires clause in a template
1918+
1919+
.. code-block:: c++
1920+
1921+
true:
1922+
template <typename It>
1923+
requires Iterator<It>
1924+
void sort(It begin, It end) {
1925+
//....
1926+
}
1927+
1928+
false:
1929+
template <typename It>
1930+
requires Iterator<It>
1931+
void sort(It begin, It end) {
1932+
//....
1933+
}
1934+
19041935
**IndentWidth** (``unsigned``)
19051936
The number of columns to use for indentation.
19061937

clang/docs/ReleaseNotes.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,14 @@ clang-format
271271
};
272272

273273

274+
- Experimental Support in clang-format for concepts has been improved, to
275+
aid this the follow options have been added
276+
277+
- Option ``IndentRequires`` has been added to indent the ``requires`` keyword
278+
in templates.
279+
- Option ``BreakBeforeConceptDeclarations`` has been added to aid the formatting of concepts.
280+
281+
274282
libclang
275283
--------
276284

clang/include/clang/Format/Format.h

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1160,6 +1160,17 @@ struct FormatStyle {
11601160
/// \endcode
11611161
BraceWrappingFlags BraceWrapping;
11621162

1163+
/// If ``true``, concept will be placed on a new line.
1164+
/// \code
1165+
/// true:
1166+
/// template<typename T>
1167+
/// concept ...
1168+
///
1169+
/// false:
1170+
/// template<typename T> concept ...
1171+
/// \endcode
1172+
bool BreakBeforeConceptDeclarations;
1173+
11631174
/// If ``true``, ternary operators will be placed after line breaks.
11641175
/// \code
11651176
/// true:
@@ -1590,6 +1601,24 @@ struct FormatStyle {
15901601
/// IndentExternBlockStyle is the type of indenting of extern blocks.
15911602
IndentExternBlockStyle IndentExternBlock;
15921603

1604+
/// Indent the requires clause in a template
1605+
/// \code
1606+
/// true:
1607+
/// template <typename It>
1608+
/// requires Iterator<It>
1609+
/// void sort(It begin, It end) {
1610+
/// //....
1611+
/// }
1612+
///
1613+
/// false:
1614+
/// template <typename It>
1615+
/// requires Iterator<It>
1616+
/// void sort(It begin, It end) {
1617+
/// //....
1618+
/// }
1619+
/// \endcode
1620+
bool IndentRequires;
1621+
15931622
/// The number of columns to use for indentation.
15941623
/// \code
15951624
/// IndentWidth: 3
@@ -2435,6 +2464,7 @@ struct FormatStyle {
24352464
BinPackParameters == R.BinPackParameters &&
24362465
BreakBeforeBinaryOperators == R.BreakBeforeBinaryOperators &&
24372466
BreakBeforeBraces == R.BreakBeforeBraces &&
2467+
BreakBeforeConceptDeclarations == R.BreakBeforeConceptDeclarations &&
24382468
BreakBeforeTernaryOperators == R.BreakBeforeTernaryOperators &&
24392469
BreakConstructorInitializers == R.BreakConstructorInitializers &&
24402470
CompactNamespaces == R.CompactNamespaces &&
@@ -2466,7 +2496,8 @@ struct FormatStyle {
24662496
IndentGotoLabels == R.IndentGotoLabels &&
24672497
IndentPPDirectives == R.IndentPPDirectives &&
24682498
IndentExternBlock == R.IndentExternBlock &&
2469-
IndentWidth == R.IndentWidth && Language == R.Language &&
2499+
IndentRequires == R.IndentRequires && IndentWidth == R.IndentWidth &&
2500+
Language == R.Language &&
24702501
IndentWrappedFunctionNames == R.IndentWrappedFunctionNames &&
24712502
JavaImportGroups == R.JavaImportGroups &&
24722503
JavaScriptQuotes == R.JavaScriptQuotes &&

clang/lib/Format/Format.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -501,6 +501,8 @@ template <> struct MappingTraits<FormatStyle> {
501501
IO.mapOptional("BraceWrapping", Style.BraceWrapping);
502502
IO.mapOptional("BreakBeforeBinaryOperators",
503503
Style.BreakBeforeBinaryOperators);
504+
IO.mapOptional("BreakBeforeConceptDeclarations",
505+
Style.BreakBeforeConceptDeclarations);
504506
IO.mapOptional("BreakBeforeBraces", Style.BreakBeforeBraces);
505507

506508
bool BreakBeforeInheritanceComma = false;
@@ -557,6 +559,7 @@ template <> struct MappingTraits<FormatStyle> {
557559
IO.mapOptional("IndentGotoLabels", Style.IndentGotoLabels);
558560
IO.mapOptional("IndentPPDirectives", Style.IndentPPDirectives);
559561
IO.mapOptional("IndentExternBlock", Style.IndentExternBlock);
562+
IO.mapOptional("IndentRequires", Style.IndentRequires);
560563
IO.mapOptional("IndentWidth", Style.IndentWidth);
561564
IO.mapOptional("IndentWrappedFunctionNames",
562565
Style.IndentWrappedFunctionNames);
@@ -872,6 +875,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
872875
LLVMStyle.BinPackArguments = true;
873876
LLVMStyle.BinPackParameters = true;
874877
LLVMStyle.BreakBeforeBinaryOperators = FormatStyle::BOS_None;
878+
LLVMStyle.BreakBeforeConceptDeclarations = true;
875879
LLVMStyle.BreakBeforeTernaryOperators = true;
876880
LLVMStyle.BreakBeforeBraces = FormatStyle::BS_Attach;
877881
LLVMStyle.BraceWrapping = {/*AfterCaseLabel=*/false,
@@ -921,6 +925,7 @@ FormatStyle getLLVMStyle(FormatStyle::LanguageKind Language) {
921925
LLVMStyle.IndentCaseBlocks = false;
922926
LLVMStyle.IndentGotoLabels = true;
923927
LLVMStyle.IndentPPDirectives = FormatStyle::PPDIS_None;
928+
LLVMStyle.IndentRequires = false;
924929
LLVMStyle.IndentWrappedFunctionNames = false;
925930
LLVMStyle.IndentWidth = 2;
926931
LLVMStyle.InsertTrailingCommas = FormatStyle::TCS_None;

clang/lib/Format/FormatToken.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ namespace format {
4040
TYPE(ConflictAlternative) \
4141
TYPE(ConflictEnd) \
4242
TYPE(ConflictStart) \
43+
TYPE(ConstraintJunctions) \
4344
TYPE(CtorInitializerColon) \
4445
TYPE(CtorInitializerComma) \
4546
TYPE(DesignatedInitializerLSquare) \
@@ -590,6 +591,7 @@ struct FormatToken {
590591
case tok::kw__Atomic:
591592
case tok::kw___attribute:
592593
case tok::kw___underlying_type:
594+
case tok::kw_requires:
593595
return true;
594596
default:
595597
return false;

clang/lib/Format/TokenAnnotator.cpp

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1367,7 +1367,7 @@ class AnnotatingParser {
13671367
TT_ImplicitStringLiteral, TT_InlineASMBrace, TT_JsFatArrow,
13681368
TT_LambdaArrow, TT_NamespaceMacro, TT_OverloadedOperator,
13691369
TT_RegexLiteral, TT_TemplateString, TT_ObjCStringLiteral,
1370-
TT_UntouchableMacroFunc))
1370+
TT_UntouchableMacroFunc, TT_ConstraintJunctions))
13711371
CurrentToken->setType(TT_Unknown);
13721372
CurrentToken->Role.reset();
13731373
CurrentToken->MatchingParen = nullptr;
@@ -1621,7 +1621,11 @@ class AnnotatingParser {
16211621
!Current.Previous->is(tok::kw_operator)) {
16221622
// not auto operator->() -> xxx;
16231623
Current.setType(TT_TrailingReturnArrow);
1624-
1624+
} else if (Current.is(tok::arrow) && Current.Previous &&
1625+
Current.Previous->is(tok::r_brace)) {
1626+
// Concept implicit conversion contraint needs to be treated like
1627+
// a trailing return type ... } -> <type>.
1628+
Current.setType(TT_TrailingReturnArrow);
16251629
} else if (isDeductionGuide(Current)) {
16261630
// Deduction guides trailing arrow " A(...) -> A<T>;".
16271631
Current.setType(TT_TrailingReturnArrow);
@@ -1722,8 +1726,8 @@ class AnnotatingParser {
17221726
// colon after this, this is the only place which annotates the identifier
17231727
// as a selector.)
17241728
Current.setType(TT_SelectorName);
1725-
} else if (Current.isOneOf(tok::identifier, tok::kw_const,
1726-
tok::kw_noexcept) &&
1729+
} else if (Current.isOneOf(tok::identifier, tok::kw_const, tok::kw_noexcept,
1730+
tok::kw_requires) &&
17271731
Current.Previous &&
17281732
!Current.Previous->isOneOf(tok::equal, tok::at) &&
17291733
Line.MightBeFunctionDecl && Contexts.size() == 1) {
@@ -1839,8 +1843,8 @@ class AnnotatingParser {
18391843
// Functions which end with decorations like volatile, noexcept are unlikely
18401844
// to be casts.
18411845
if (Tok.Next->isOneOf(tok::kw_noexcept, tok::kw_volatile, tok::kw_const,
1842-
tok::kw_throw, tok::arrow, Keywords.kw_override,
1843-
Keywords.kw_final) ||
1846+
tok::kw_requires, tok::kw_throw, tok::arrow,
1847+
Keywords.kw_override, Keywords.kw_final) ||
18441848
isCpp11AttributeSpecifier(*Tok.Next))
18451849
return false;
18461850

@@ -2817,6 +2821,14 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line,
28172821
isKeywordWithCondition(*Right.MatchingParen->Previous))
28182822
return true;
28192823
}
2824+
2825+
// requires ( or requires(
2826+
if (Right.is(tok::l_paren) && Left.is(tok::kw_requires))
2827+
return spaceRequiredBeforeParens(Right);
2828+
// requires clause Concept1<T> && Concept2<T>
2829+
if (Left.is(TT_ConstraintJunctions) && Right.is(tok::identifier))
2830+
return true;
2831+
28202832
if (Left.is(tok::l_paren) || Right.is(tok::r_paren))
28212833
return (Right.is(TT_CastRParen) ||
28222834
(Left.MatchingParen && Left.MatchingParen->is(TT_CastRParen)))
@@ -3594,11 +3606,17 @@ bool TokenAnnotator::mustBreakBefore(const AnnotatedLine &Line,
35943606
Right.Previous->is(tok::string_literal) &&
35953607
Right.Next->is(tok::string_literal))
35963608
return true;
3609+
// Can break after template<> declaration
35973610
if (Right.Previous->ClosesTemplateDeclaration &&
35983611
Right.Previous->MatchingParen &&
3599-
Right.Previous->MatchingParen->NestingLevel == 0 &&
3600-
Style.AlwaysBreakTemplateDeclarations == FormatStyle::BTDS_Yes)
3601-
return true;
3612+
Right.Previous->MatchingParen->NestingLevel == 0) {
3613+
// Put concepts on the next line e.g.
3614+
// template<typename T>
3615+
// concept ...
3616+
if (Right.is(tok::kw_concept))
3617+
return Style.BreakBeforeConceptDeclarations;
3618+
return (Style.AlwaysBreakTemplateDeclarations == FormatStyle::BTDS_Yes);
3619+
}
36023620
if (Right.is(TT_CtorInitializerComma) &&
36033621
Style.BreakConstructorInitializers == FormatStyle::BCIS_BeforeComma &&
36043622
!Style.ConstructorInitializerAllOnOneLineOrOnePerLine)

clang/lib/Format/UnwrappedLineParser.cpp

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,8 +626,16 @@ void UnwrappedLineParser::parseBlock(bool MustBeDeclaration, bool AddLevel,
626626
if (MacroBlock && FormatTok->is(tok::l_paren))
627627
parseParens();
628628

629+
if (FormatTok->is(tok::arrow)) {
630+
// Following the } we can find a trailing return type arrow
631+
// as part of an implicit conversion constraint.
632+
nextToken();
633+
parseStructuralElement();
634+
}
635+
629636
if (MunchSemi && FormatTok->Tok.is(tok::semi))
630637
nextToken();
638+
631639
Line->Level = InitialLevel;
632640

633641
if (PPStartHash == PPEndHash) {
@@ -1262,6 +1270,12 @@ void UnwrappedLineParser::parseStructuralElement() {
12621270
break;
12631271
}
12641272
break;
1273+
case tok::kw_concept:
1274+
parseConcept();
1275+
break;
1276+
case tok::kw_requires:
1277+
parseRequires();
1278+
break;
12651279
case tok::kw_enum:
12661280
// Ignore if this is part of "template <enum ...".
12671281
if (Previous && Previous->is(tok::less)) {
@@ -2279,6 +2293,117 @@ void UnwrappedLineParser::parseAccessSpecifier() {
22792293
addUnwrappedLine();
22802294
}
22812295

2296+
void UnwrappedLineParser::parseConcept() {
2297+
assert(FormatTok->Tok.is(tok::kw_concept) && "'concept' expected");
2298+
nextToken();
2299+
if (!FormatTok->Tok.is(tok::identifier))
2300+
return;
2301+
nextToken();
2302+
if (!FormatTok->Tok.is(tok::equal))
2303+
return;
2304+
nextToken();
2305+
if (FormatTok->Tok.is(tok::kw_requires)) {
2306+
nextToken();
2307+
parseRequiresExpression(Line->Level);
2308+
} else {
2309+
parseConstraintExpression(Line->Level);
2310+
}
2311+
}
2312+
2313+
void UnwrappedLineParser::parseRequiresExpression(unsigned int OriginalLevel) {
2314+
// requires (R range)
2315+
if (FormatTok->Tok.is(tok::l_paren)) {
2316+
parseParens();
2317+
if (Style.IndentRequires && OriginalLevel != Line->Level) {
2318+
addUnwrappedLine();
2319+
--Line->Level;
2320+
}
2321+
}
2322+
2323+
if (FormatTok->Tok.is(tok::l_brace)) {
2324+
if (Style.BraceWrapping.AfterFunction)
2325+
addUnwrappedLine();
2326+
FormatTok->setType(TT_FunctionLBrace);
2327+
parseBlock(/*MustBeDeclaration=*/false);
2328+
addUnwrappedLine();
2329+
} else {
2330+
parseConstraintExpression(OriginalLevel);
2331+
}
2332+
}
2333+
2334+
void UnwrappedLineParser::parseConstraintExpression(
2335+
unsigned int OriginalLevel) {
2336+
// requires Id<T> && Id<T> || Id<T>
2337+
while (
2338+
FormatTok->isOneOf(tok::identifier, tok::kw_requires, tok::coloncolon)) {
2339+
nextToken();
2340+
while (FormatTok->isOneOf(tok::identifier, tok::coloncolon, tok::less,
2341+
tok::greater, tok::comma, tok::ellipsis)) {
2342+
if (FormatTok->Tok.is(tok::less)) {
2343+
parseBracedList(/*ContinueOnSemicolons=*/false, /*IsEnum=*/false,
2344+
/*ClosingBraceKind=*/tok::greater);
2345+
continue;
2346+
}
2347+
nextToken();
2348+
}
2349+
if (FormatTok->Tok.is(tok::kw_requires)) {
2350+
parseRequiresExpression(OriginalLevel);
2351+
}
2352+
if (FormatTok->Tok.is(tok::less)) {
2353+
parseBracedList(/*ContinueOnSemicolons=*/false, /*IsEnum=*/false,
2354+
/*ClosingBraceKind=*/tok::greater);
2355+
}
2356+
2357+
if (FormatTok->Tok.is(tok::l_paren)) {
2358+
parseParens();
2359+
}
2360+
if (FormatTok->Tok.is(tok::l_brace)) {
2361+
if (Style.BraceWrapping.AfterFunction)
2362+
addUnwrappedLine();
2363+
FormatTok->setType(TT_FunctionLBrace);
2364+
parseBlock(/*MustBeDeclaration=*/false);
2365+
}
2366+
if (FormatTok->Tok.is(tok::semi)) {
2367+
// Eat any trailing semi.
2368+
nextToken();
2369+
addUnwrappedLine();
2370+
}
2371+
if (FormatTok->Tok.is(tok::colon)) {
2372+
return;
2373+
}
2374+
if (!FormatTok->Tok.isOneOf(tok::ampamp, tok::pipepipe)) {
2375+
if (FormatTok->Previous &&
2376+
!FormatTok->Previous->isOneOf(tok::identifier, tok::kw_requires,
2377+
tok::coloncolon)) {
2378+
addUnwrappedLine();
2379+
}
2380+
if (Style.IndentRequires && OriginalLevel != Line->Level) {
2381+
--Line->Level;
2382+
}
2383+
break;
2384+
} else {
2385+
FormatTok->setType(TT_ConstraintJunctions);
2386+
}
2387+
2388+
nextToken();
2389+
}
2390+
}
2391+
2392+
void UnwrappedLineParser::parseRequires() {
2393+
assert(FormatTok->Tok.is(tok::kw_requires) && "'requires' expected");
2394+
2395+
unsigned OriginalLevel = Line->Level;
2396+
if (FormatTok->Previous && FormatTok->Previous->is(tok::greater)) {
2397+
addUnwrappedLine();
2398+
if (Style.IndentRequires) {
2399+
Line->Level++;
2400+
}
2401+
}
2402+
nextToken();
2403+
2404+
parseRequiresExpression(OriginalLevel);
2405+
}
2406+
22822407
bool UnwrappedLineParser::parseEnum() {
22832408
// Won't be 'enum' for NS_ENUMs.
22842409
if (FormatTok->Tok.is(tok::kw_enum))

0 commit comments

Comments
 (0)