Index: clang/lib/Format/TokenAnnotator.cpp =================================================================== --- clang/lib/Format/TokenAnnotator.cpp +++ clang/lib/Format/TokenAnnotator.cpp @@ -1563,7 +1563,11 @@ !Current.Previous->is(tok::kw_operator)) { // not auto operator->() -> xxx; Current.Type = TT_TrailingReturnArrow; - + } else if (Current.is(tok::arrow) && Current.Previous && + Current.Previous->is(tok::r_brace)) { + // Concept implicit conversion contrain needs to be treated like + // a trailing return type ... } -> . + Current.Type = TT_TrailingReturnArrow; } else if (isDeductionGuide(Current)) { // Deduction guides trailing arrow " A(...) -> A;". Current.Type = TT_TrailingReturnArrow; @@ -3466,6 +3470,12 @@ return true; } + // Put concepts on the next line e.g. + // template + // concept ... + if (Left.is(TT_TemplateCloser) && Right.is(tok::kw_concept)) + return true; + if (Right.is(tok::comment)) return Left.BlockKind != BK_BracedInit && Left.isNot(TT_CtorInitializerColon) && Index: clang/lib/Format/UnwrappedLineParser.cpp =================================================================== --- clang/lib/Format/UnwrappedLineParser.cpp +++ clang/lib/Format/UnwrappedLineParser.cpp @@ -628,6 +628,13 @@ if (MunchSemi && FormatTok->Tok.is(tok::semi)) nextToken(); + else if (FormatTok->is(tok::arrow)) { + // Following the } we can find a trailing return type arrow + // as part of an implicit conversion constraint. + nextToken(); + parseStructuralElement(); + } + Line->Level = InitialLevel; if (PPStartHash == PPEndHash) { Index: clang/unittests/Format/FormatTest.cpp =================================================================== --- clang/unittests/Format/FormatTest.cpp +++ clang/unittests/Format/FormatTest.cpp @@ -15849,6 +15849,41 @@ verifyFormat("operator&&(int(&&)(), class Foo);", Style); } +TEST_F(FormatTest, ConceptsImplicitConversionConstraint) { + FormatStyle Style = getLLVMStyle(); + + verifyFormat("template \n" + "concept Hashable = requires(T a) {\n" + " { std::hash{}(a) } -> std::convertible_to;\n" + "};", + Style); + verifyFormat("template \n" + "concept bool EqualityComparable = requires(T a, T b) {\n" + " { a == b } -> bool;\n" + "};", + Style); + verifyFormat("template \n" + "concept bool EqualityComparable = requires(T a, T b) {\n" + " { a == b } -> bool;\n" + " { a != b } -> bool;\n" + "};", + Style); + verifyFormat("template \n" + "concept bool EqualityComparable = requires(T a, T b) {\n" + " { a == b } -> bool;\n" + " { a != b } -> bool;\n" + "};", + Style); + + verifyFormat("requires(R range) {\n" + " typename Iterator_type;\n" + " { begin(range) } -> Iterator_type;\n" + " { end(range) } -> Iterator_type;\n" + " requires Input_iterator>();\n" + "};\n", + Style); +} + } // namespace } // namespace format } // namespace clang