From 76ec6b1ef69fcbd27cb0d587a5eb2d51a135a6bb Mon Sep 17 00:00:00 2001 From: paulhoad Date: Wed, 6 Nov 2019 09:34:01 +0000 Subject: [PATCH] [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 S(T)->S; auto f(int, int) -> double; ``` becomes ``` template S(T) -> S; 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 --- clang/lib/Format/TokenAnnotator.cpp | 68 +++++++++++++++++++++++++++++++++++ clang/unittests/Format/FormatTest.cpp | 23 ++++++++++++ 2 files changed, 91 insertions(+) diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp index 98b87c0..3bd415e 100644 --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -1350,6 +1350,70 @@ private: } } + 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 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 @@ private: !Current.Previous->is(tok::kw_operator)) { // not auto operator->() -> xxx; Current.Type = TT_TrailingReturnArrow; + + } else if (isDeductionGuide(Current)) { + // Deduction guides trailing arrow " A(...) -> A;". + 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 ea03ee1..eacb389 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 A(const T &, const T &) -> A;"); + verifyFormat("template explicit A(T &, T &&) -> A;"); + verifyFormat("template S(Ts...) -> S;"); + verifyFormat( + "template \n" + "array(T &&... t) -> array, sizeof...(T)>;"); + verifyFormat("template A() -> Afoo<3>())>;"); + verifyFormat("template A() -> A>)>;"); + verifyFormat("template A() -> Afoo<1>)>;"); + verifyFormat("template A() -> A<(3 < 2)>;"); + verifyFormat("template A() -> A<((3) < (2))>;"); + verifyFormat("template x() -> x<1>;"); + verifyFormat("template explicit x(T &) -> x<1>;"); + + // Ensure not deduction guides. + verifyFormat("c()->f();"); + 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. -- 2.7.4