From 6291795332ac706402980c9d4fe9ef68f64cd802 Mon Sep 17 00:00:00 2001 From: Birunthan Mohanathas Date: Wed, 15 Jul 2015 19:11:58 +0000 Subject: [PATCH] clang-format: Fix return type breaking with overloaded operator functions Differential Revision: http://reviews.llvm.org/D11177 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@242316 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Format/ContinuationIndenter.cpp | 3 +- lib/Format/TokenAnnotator.cpp | 72 +++++++++++++++++++++++------ unittests/Format/FormatTest.cpp | 26 +++++++++++ 3 files changed, 86 insertions(+), 15 deletions(-) diff --git a/lib/Format/ContinuationIndenter.cpp b/lib/Format/ContinuationIndenter.cpp index dd56831a3b..9d73bf6c01 100644 --- a/lib/Format/ContinuationIndenter.cpp +++ b/lib/Format/ContinuationIndenter.cpp @@ -225,7 +225,8 @@ bool ContinuationIndenter::mustBreak(const LineState &State) { } // If the return type spans multiple lines, wrap before the function name. - if (Current.isOneOf(TT_FunctionDeclarationName, tok::kw_operator) && + if ((Current.is(TT_FunctionDeclarationName) || + (Current.is(tok::kw_operator) && !Previous.is(tok::coloncolon))) && State.Stack.back().BreakBeforeParameter) return true; diff --git a/lib/Format/TokenAnnotator.cpp b/lib/Format/TokenAnnotator.cpp index ea8b30de8d..55f5b103f9 100644 --- a/lib/Format/TokenAnnotator.cpp +++ b/lib/Format/TokenAnnotator.cpp @@ -500,6 +500,19 @@ private: return false; break; case tok::l_paren: + // When faced with 'operator()()', the kw_operator handler incorrectly + // marks the first l_paren as a OverloadedOperatorLParen. Here, we make + // the first two parens OverloadedOperators and the second l_paren an + // OverloadedOperatorLParen. + if (Tok->Previous && + Tok->Previous->is(tok::r_paren) && + Tok->Previous->MatchingParen && + Tok->Previous->MatchingParen->is(TT_OverloadedOperatorLParen)) { + Tok->Previous->Type = TT_OverloadedOperator; + Tok->Previous->MatchingParen->Type = TT_OverloadedOperator; + Tok->Type = TT_OverloadedOperatorLParen; + } + if (!parseParens()) return false; if (Line.MustBeDeclaration && Contexts.size() == 1 && @@ -1460,25 +1473,56 @@ void TokenAnnotator::annotate(AnnotatedLine &Line) { // This function heuristically determines whether 'Current' starts the name of a // function declaration. static bool isFunctionDeclarationName(const FormatToken &Current) { - if (!Current.is(TT_StartOfName) || Current.NestingLevel != 0) - return false; - const FormatToken *Next = Current.Next; - for (; Next; Next = Next->Next) { - if (Next->is(TT_TemplateOpener)) { - Next = Next->MatchingParen; - } else if (Next->is(tok::coloncolon)) { - Next = Next->Next; - if (!Next || !Next->is(tok::identifier)) - return false; - } else if (Next->is(tok::l_paren)) { + auto skipOperatorName = [](const FormatToken* Next) -> const FormatToken* { + for (; Next; Next = Next->Next) { + if (Next->is(TT_OverloadedOperatorLParen)) + return Next; + if (Next->is(TT_OverloadedOperator)) + continue; + if (Next->isOneOf(tok::kw_new, tok::kw_delete)) { + // For 'new[]' and 'delete[]'. + if (Next->Next && Next->Next->is(tok::l_square) && + Next->Next->Next && Next->Next->Next->is(tok::r_square)) + Next = Next->Next->Next; + continue; + } + break; - } else { + } + return nullptr; + }; + + const FormatToken *Next = Current.Next; + if (Current.is(tok::kw_operator)) { + if (Current.Previous && Current.Previous->is(tok::coloncolon)) + return false; + Next = skipOperatorName(Next); + } else { + if (!Current.is(TT_StartOfName) || Current.NestingLevel != 0) return false; + for (; Next; Next = Next->Next) { + if (Next->is(TT_TemplateOpener)) { + Next = Next->MatchingParen; + } else if (Next->is(tok::coloncolon)) { + Next = Next->Next; + if (!Next) + return false; + if (Next->is(tok::kw_operator)) { + Next = skipOperatorName(Next->Next); + break; + } + if (!Next->is(tok::identifier)) + return false; + } else if (Next->is(tok::l_paren)) { + break; + } else { + return false; + } } } - if (!Next) + + if (!Next || !Next->is(tok::l_paren)) return false; - assert(Next->is(tok::l_paren)); if (Next->Next == Next->MatchingParen) return true; for (const FormatToken *Tok = Next->Next; Tok && Tok != Next->MatchingParen; diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 886f58093d..1846aa6849 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -4678,6 +4678,32 @@ TEST_F(FormatTest, DefinitionReturnTypeBreakingStyle) { "}\n" "template T *f(T &c);\n", // No break here. Style); + verifyFormat("class C {\n" + " int\n" + " operator+() {\n" + " return 1;\n" + " }\n" + " int\n" + " operator()() {\n" + " return 1;\n" + " }\n" + "};\n", + Style); + verifyFormat("void\n" + "A::operator()() {}\n" + "void\n" + "A::operator>>() {}\n" + "void\n" + "A::operator+() {}\n", + Style); + verifyFormat("void *operator new(std::size_t s);", // No break here. + Style); + verifyFormat("void *\n" + "operator new(std::size_t s) {}", + Style); + verifyFormat("void *\n" + "operator delete[](void *ptr) {}", + Style); Style.BreakBeforeBraces = FormatStyle::BS_Stroustrup; verifyFormat("const char *\n" "f(void)\n" // Break here. -- 2.40.0