diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp --- a/clang/lib/Format/UnwrappedLineParser.cpp +++ b/clang/lib/Format/UnwrappedLineParser.cpp @@ -3132,30 +3132,6 @@ 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->is(tok::less)) - parseBracedList(/*ContinueOnSemicolons=*/false, /*IsEnum=*/false, - /*ClosingBraceKind=*/tok::greater); - break; - case tok::kw_const: case tok::semi: case tok::kw_class: @@ -3232,7 +3208,34 @@ break; default: - return; + if (!FormatTok->Tok.getIdentifierInfo()) { + // Identifiers are part of the default case, we check for more then + // tok::identifier to handle builtin type traits. + return; + } + + // 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->is(tok::less)) + parseBracedList(/*ContinueOnSemicolons=*/false, /*IsEnum=*/false, + /*ClosingBraceKind=*/tok::greater); + break; } } while (!eof()); } diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -23810,6 +23810,18 @@ verifyFormat("template \n" "concept Node = std::is_object_v;"); + verifyFormat("template \n" + "concept integral = __is_integral(T);"); + + verifyFormat("template \n" + "concept is2D = __array_extent(T, 1) == 2;"); + + verifyFormat("template \n" + "concept isRhs = __is_rvalue_expr(std::declval() + 2)"); + + verifyFormat("template \n" + "concept Same = __is_same_as;"); + auto Style = getLLVMStyle(); Style.BreakBeforeConceptDeclarations = FormatStyle::BBCDS_Allowed;