Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -13,7 +13,9 @@ //===----------------------------------------------------------------------===// #include "TokenAnnotator.h" +#include "FormatToken.h" #include "clang/Basic/SourceManager.h" +#include "clang/Basic/TokenKinds.def" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/Support/Debug.h" @@ -39,6 +41,28 @@ return Tok.Tok.getIdentifierInfo() != nullptr; } +/// Returns \c true if \c Tok is the left square in a C++ structured binding. +static bool isStructutedBindingLSquare(const FormatToken &Tok) { + assert(Tok.is(tok::l_square)); + // A structured binding has the structure: + // attr(optional) cv-auto ref-operator(optional) [identifier-list] ... + // where: + // - cv-auto is a possibly cv-qualified specifier auto + // - ref-operator is either & or &&. + const FormatToken *Prev = Tok.getPreviousNonComment(); + if (!Prev) + return false; + // Consume the ref-operator. + if (Prev->isOneOf(tok::amp, tok::ampamp)) { + Prev = Prev->getPreviousNonComment(); + } + // Consume the cv-qualifiers. + while (Prev && Prev->isOneOf(tok::kw_const, tok::kw_volatile)) { + Prev = Prev->getPreviousNonComment(); + } + return Prev && Prev->is(tok::kw_auto); +} + /// A parser that gathers additional information about tokens. /// /// The \c TokenAnnotator tries to match parenthesis and square brakets and @@ -451,8 +475,9 @@ Parent->isUnaryOperator() || // FIXME(bug 36976): ObjC return types shouldn't use TT_CastRParen. Parent->isOneOf(TT_ObjCForIn, TT_CastRParen) || - // for (auto && [A,B] : C) && structure binding seen as ObjCMethodExpr - (Parent->isNot(tok::ampamp) && + // Structured bindings and Objective-C call could look alike. + ((!Parent->is(tok::l_square) || + !isStructutedBindingLSquare(*Parent)) && getBinOpPrecedence(Parent->Tok.getKind(), true, true) > prec::Unknown)); bool ColonFound = false; Index: unittests/Format/FormatTestObjC.cpp =================================================================== --- unittests/Format/FormatTestObjC.cpp +++ unittests/Format/FormatTestObjC.cpp @@ -1357,6 +1357,30 @@ // verifyFormat("x = ([a foo:bar] >> b->c == 'd');"); } +TEST_F(FormatTestObjC, DisambiguatesCallsFromStructuredBindings) { + verifyFormat("int f() {\n" + " if (a && [f arg])\n" + " return 0;\n" + "}"); + verifyFormat("int f() {\n" + " if (a & [f arg])\n" + " return 0;\n" + "}"); + verifyFormat("int f() {\n" + " for (auto &[elem] : list)\n" + " return 0;\n" + "}"); + verifyFormat("int f() {\n" + " for (auto &&[elem] : list)\n" + " return 0;\n" + "}"); + verifyFormat( + "int f() {\n" + " for (auto /**/ const /**/ volatile /**/ && /**/ [elem] : list)\n" + " return 0;\n" + "}"); +} + } // end namespace } // end namespace format } // end namespace clang