Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "TokenAnnotator.h" +#include "FormatToken.h" #include "clang/Basic/SourceManager.h" #include "llvm/ADT/SmallPtrSet.h" #include "llvm/Support/Debug.h" @@ -440,10 +441,11 @@ Contexts.back().InCSharpAttributeSpecifier; bool InsideInlineASM = Line.startsWith(tok::kw_asm); + bool IsCppStructuredBinding = Left->isCppStructuredBinding(Style); bool StartsObjCMethodExpr = - !InsideInlineASM && !CppArrayTemplates && Style.isCpp() && - !IsCpp11AttributeSpecifier && Contexts.back().CanBeExpression && - Left->isNot(TT_LambdaLSquare) && + !IsCppStructuredBinding && !InsideInlineASM && !CppArrayTemplates && + Style.isCpp() && !IsCpp11AttributeSpecifier && + Contexts.back().CanBeExpression && Left->isNot(TT_LambdaLSquare) && !CurrentToken->isOneOf(tok::l_brace, tok::r_square) && (!Parent || Parent->isOneOf(tok::colon, tok::l_square, tok::l_paren, @@ -451,14 +453,12 @@ 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) && - getBinOpPrecedence(Parent->Tok.getKind(), true, true) > - prec::Unknown)); + (getBinOpPrecedence(Parent->Tok.getKind(), true, true) > + prec::Unknown)); bool ColonFound = false; unsigned BindingIncrease = 1; - if (Left->isCppStructuredBinding(Style)) { + if (IsCppStructuredBinding) { Left->Type = TT_StructuredBindingLSquare; } else if (Left->is(TT_Unknown)) { if (StartsObjCMethodExpr) { @@ -2026,6 +2026,7 @@ Line.First->SpacesRequiredBefore = 1; Line.First->CanBreakBefore = Line.First->MustBreakBefore; + LLVM_DEBUG({ printDebugInfo(Line); }); } // This function heuristically determines whether 'Current' starts the name of a Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -13218,7 +13218,7 @@ guessLanguage("foo.h", "[[using gsl: suppress(\"type\")]];")); EXPECT_EQ( FormatStyle::LK_Cpp, - guessLanguage("foo.h", "for (auto &&[endpoint, stream] : streams_)")); + guessLanguage("foo.h", "for (auto &&[endpoint, stream] : streams_);")); EXPECT_EQ( FormatStyle::LK_Cpp, guessLanguage("foo.h", @@ -13226,6 +13226,12 @@ EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", "[[foo::bar, ...]]")); } +TEST_F(FormatTest, TODO) { + EXPECT_EQ( + FormatStyle::LK_Cpp, + guessLanguage("foo.h", "for (auto &&[endpoint, stream] : streams_);")); +} + TEST_F(FormatTest, GuessLanguageWithCaret) { EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", "FOO(^);")); EXPECT_EQ(FormatStyle::LK_Cpp, guessLanguage("foo.h", "FOO(^, Bar);")); 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