Author: paulhoad Date: 2019-11-06T09:34:48Z New Revision: 76ec6b1ef69fcbd27cb0d587a5eb2d51a135a6bb
URL: https://github.com/llvm/llvm-project/commit/76ec6b1ef69fcbd27cb0d587a5eb2d51a135a6bb DIFF: https://github.com/llvm/llvm-project/commit/76ec6b1ef69fcbd27cb0d587a5eb2d51a135a6bb.diff LOG: [clang-format] [PR35518] C++17 deduction guides are wrongly formatted Summary: see https://bugs.llvm.org/show_bug.cgi?id=35518 clang-format removes spaces around deduction guides but not trailing return types, make the consistent ``` template <typename T> S(T)->S<T>; auto f(int, int) -> double; ``` becomes ``` template <typename T> S(T) -> S<T>; auto f(int, int) -> double; ``` Reviewers: klimek, mitchell-stellar, owenpan, sammccall, lichray, curdeius, KyrBoh Reviewed By: curdeius Subscribers: merge_guards_bot, hans, lichray, cfe-commits Tags: #clang-format, #clang-tools-extra, #clang Differential Revision: https://reviews.llvm.org/D69577 Added: Modified: clang/lib/Format/TokenAnnotator.cpp clang/unittests/Format/FormatTest.cpp Removed: ################################################################################ diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 98b87c0f5a25..3bd415ebd699 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -1350,6 +1350,70 @@ class AnnotatingParser { } } + static FormatToken *untilMatchingParen(FormatToken *Current) { + // Used when `MatchingParen` is not yet established. + int ParenLevel = 0; + while (Current) { + if (Current->is(tok::l_paren)) + ParenLevel++; + if (Current->is(tok::r_paren)) + ParenLevel--; + if (ParenLevel < 1) + break; + Current = Current->Next; + } + return Current; + } + + static bool isDeductionGuide(FormatToken &Current) { + // Look for a deduction guide template<T> A(...) -> A<...>; + if (Current.Previous && Current.Previous->is(tok::r_paren) && + Current.startsSequence(tok::arrow, tok::identifier, tok::less)) { + // Find the TemplateCloser. + FormatToken *TemplateCloser = Current.Next->Next; + int NestingLevel = 0; + while (TemplateCloser) { + // Skip over an expressions in parens A<(3 < 2)>; + if (TemplateCloser->is(tok::l_paren)) { + // No Matching Paren yet so skip to matching paren + TemplateCloser = untilMatchingParen(TemplateCloser); + } + if (TemplateCloser->is(tok::less)) + NestingLevel++; + if (TemplateCloser->is(tok::greater)) + NestingLevel--; + if (NestingLevel < 1) + break; + TemplateCloser = TemplateCloser->Next; + } + // Assuming we have found the end of the template ensure its followed + // with a semi-colon. + if (TemplateCloser && TemplateCloser->Next && + TemplateCloser->Next->is(tok::semi) && + Current.Previous->MatchingParen) { + // Determine if the identifier `A` prior to the A<..>; is the same as + // prior to the A(..) + FormatToken *LeadingIdentifier = + Current.Previous->MatchingParen->Previous; + + // Differentiate a deduction guide by seeing the + // > of the template prior to the leading identifier. + if (LeadingIdentifier) { + FormatToken *PriorLeadingIdentifier = LeadingIdentifier->Previous; + // Skip back past explicit decoration + if (PriorLeadingIdentifier && + PriorLeadingIdentifier->is(tok::kw_explicit)) + PriorLeadingIdentifier = PriorLeadingIdentifier->Previous; + + return (PriorLeadingIdentifier && + PriorLeadingIdentifier->is(TT_TemplateCloser) && + LeadingIdentifier->TokenText == Current.Next->TokenText); + } + } + } + return false; + } + void determineTokenType(FormatToken &Current) { if (!Current.is(TT_Unknown)) // The token type is already known. @@ -1397,6 +1461,10 @@ class AnnotatingParser { !Current.Previous->is(tok::kw_operator)) { // not auto operator->() -> xxx; Current.Type = TT_TrailingReturnArrow; + + } else if (isDeductionGuide(Current)) { + // Deduction guides trailing arrow " A(...) -> A<T>;". + Current.Type = TT_TrailingReturnArrow; } else if (Current.isOneOf(tok::star, tok::amp, tok::ampamp)) { Current.Type = determineStarAmpUsage(Current, Contexts.back().CanBeExpression && diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index ea03ee1c3cc9..eacb389400b4 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -4977,6 +4977,29 @@ TEST_F(FormatTest, TrailingReturnType) { verifyFormat("void f() { auto a = b->c(); }"); } +TEST_F(FormatTest, DeductionGuides) { + verifyFormat("template <class T> A(const T &, const T &) -> A<T &>;"); + verifyFormat("template <class T> explicit A(T &, T &&) -> A<T>;"); + verifyFormat("template <class... Ts> S(Ts...) -> S<Ts...>;"); + verifyFormat( + "template <class... T>\n" + "array(T &&... t) -> array<std::common_type_t<T...>, sizeof...(T)>;"); + verifyFormat("template <class T> A() -> A<decltype(p->foo<3>())>;"); + verifyFormat("template <class T> A() -> A<decltype(foo<traits<1>>)>;"); + verifyFormat("template <class T> A() -> A<sizeof(p->foo<1>)>;"); + verifyFormat("template <class T> A() -> A<(3 < 2)>;"); + verifyFormat("template <class T> A() -> A<((3) < (2))>;"); + verifyFormat("template <class T> x() -> x<1>;"); + verifyFormat("template <class T> explicit x(T &) -> x<1>;"); + + // Ensure not deduction guides. + verifyFormat("c()->f<int>();"); + verifyFormat("x()->foo<1>;"); + verifyFormat("x = p->foo<3>();"); + verifyFormat("x()->x<1>();"); + verifyFormat("x()->x<1>;"); +} + TEST_F(FormatTest, BreaksFunctionDeclarationsWithTrailingTokens) { // Avoid breaking before trailing 'const' or other trailing annotations, if // they are not function-like. _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits