MyDeveloperDay created this revision. MyDeveloperDay added reviewers: krasimir, sammccall, owenpan, jbcoe, mitchell-stellar. MyDeveloperDay added projects: clang, clang-format.
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. requires(R range) { typename Iterator_type<R>; { begin(range) } ->Iterator_type<R>; { end(range) } ->Iterator_type<R>; requires Input_iterator<Iterator_type<R>>(); }; The revision starts by resolving the additional newline added before the implicit conversion constraint and ensures that the concept keyword is always on the line below template<> template <typename T> concept bool EqualityComparable = requires(T a, T b) { { a == b } -> bool; }; Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D79773 Files: clang/lib/Format/TokenAnnotator.cpp clang/lib/Format/UnwrappedLineParser.cpp clang/unittests/Format/FormatTest.cpp 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 <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 bool EqualityComparable = requires(T a, T b) {\n" + " { a == b } -> bool;\n" + "};", + Style); + verifyFormat("template <typename T>\n" + "concept bool EqualityComparable = requires(T a, T b) {\n" + " { a == b } -> bool;\n" + " { a != b } -> bool;\n" + "};", + Style); + verifyFormat("template <typename T>\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<R>;\n" + " { begin(range) } -> Iterator_type<R>;\n" + " { end(range) } -> Iterator_type<R>;\n" + " requires Input_iterator<Iterator_type<R>>();\n" + "};\n", + Style); +} + } // namespace } // namespace format } // namespace clang 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/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 ... } -> <type>. + Current.Type = TT_TrailingReturnArrow; } else if (isDeductionGuide(Current)) { // Deduction guides trailing arrow " A(...) -> A<T>;". Current.Type = TT_TrailingReturnArrow; @@ -3466,6 +3470,12 @@ return true; } + // Put concepts on the next line e.g. + // template<typename T> + // 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/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 <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 bool EqualityComparable = requires(T a, T b) {\n" + " { a == b } -> bool;\n" + "};", + Style); + verifyFormat("template <typename T>\n" + "concept bool EqualityComparable = requires(T a, T b) {\n" + " { a == b } -> bool;\n" + " { a != b } -> bool;\n" + "};", + Style); + verifyFormat("template <typename T>\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<R>;\n" + " { begin(range) } -> Iterator_type<R>;\n" + " { end(range) } -> Iterator_type<R>;\n" + " requires Input_iterator<Iterator_type<R>>();\n" + "};\n", + Style); +} + } // namespace } // namespace format } // namespace clang 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/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 ... } -> <type>. + Current.Type = TT_TrailingReturnArrow; } else if (isDeductionGuide(Current)) { // Deduction guides trailing arrow " A(...) -> A<T>;". Current.Type = TT_TrailingReturnArrow; @@ -3466,6 +3470,12 @@ return true; } + // Put concepts on the next line e.g. + // template<typename T> + // 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) &&
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits