Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -330,8 +330,15 @@ (Contexts.back().CanBeExpression || Contexts.back().IsExpression || Contexts.back().InTemplateArgument); + bool CppStructuredBindingDecl = + !CppArrayTemplates && Style.isCpp() && Parent && + (Parent->is(tok::kw_auto) || + (Parent->isOneOf(tok::amp, tok::ampamp) && + Parent->getPreviousNonComment() && + Parent->getPreviousNonComment()->is(tok::kw_auto))); + bool StartsObjCMethodExpr = - !CppArrayTemplates && Style.isCpp() && + !CppStructuredBindingDecl && !CppArrayTemplates && Style.isCpp() && Contexts.back().CanBeExpression && Left->isNot(TT_LambdaLSquare) && CurrentToken->isNot(tok::l_brace) && (!Parent || @@ -344,7 +351,9 @@ unsigned BindingIncrease = 1; if (Left->is(TT_Unknown)) { - if (StartsObjCMethodExpr) { + if (CppStructuredBindingDecl) { + Left->Type = TT_ArraySubscriptLSquare; + } else if (StartsObjCMethodExpr) { Left->Type = TT_ObjCMethodExpr; } else if (Style.Language == FormatStyle::LK_JavaScript && Parent && Contexts.back().ContextKind == tok::l_brace && @@ -2150,6 +2159,8 @@ bool TokenAnnotator::spaceRequiredBetween(const AnnotatedLine &Line, const FormatToken &Left, const FormatToken &Right) { + if (Left.is(tok::kw_auto) && Right.is(TT_ArraySubscriptLSquare)) + return true; if (Left.is(tok::kw_return) && Right.isNot(tok::semi)) return true; if (Style.ObjCSpaceAfterProperty && Line.Type == LT_ObjCProperty && Index: lib/Format/UnwrappedLineParser.h =================================================================== --- lib/Format/UnwrappedLineParser.h +++ lib/Format/UnwrappedLineParser.h @@ -125,6 +125,7 @@ bool eof() const; void nextToken(); const FormatToken *getPreviousToken(); + const FormatToken *getPrePreviousToken(); void readToken(); // Decides which comment tokens should be added to the current line and which Index: lib/Format/UnwrappedLineParser.cpp =================================================================== --- lib/Format/UnwrappedLineParser.cpp +++ lib/Format/UnwrappedLineParser.cpp @@ -1211,9 +1211,12 @@ return false; } const FormatToken* Previous = getPreviousToken(); + const FormatToken* PrePrevious = getPrePreviousToken(); if (Previous && (Previous->isOneOf(tok::identifier, tok::kw_operator, tok::kw_new, - tok::kw_delete) || + tok::kw_delete, tok::kw_auto) || + (PrePrevious && Previous->isOneOf(tok::amp, tok::ampamp) && + PrePrevious->is(tok::kw_auto)) || Previous->closesScope() || Previous->isSimpleTypeSpecifier())) { nextToken(); return false; @@ -2306,6 +2309,12 @@ return Line->Tokens.back().Tok; } +const FormatToken *UnwrappedLineParser::getPrePreviousToken() { + if (!Line || Line->Tokens.size() < 2) + return nullptr; + return std::prev(Line->Tokens.end(), 2)->Tok; +} + void UnwrappedLineParser::distributeComments( const SmallVectorImpl &Comments, const FormatToken *NextTok) { Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -11085,6 +11085,41 @@ EXPECT_EQ("auto c = u8'a';", format("auto c = u8'a';")); } +TEST_F(FormatTest, StructuredBindingDeclaration) { + // structured-binding-declaration is a C++17 feature. + EXPECT_EQ("auto [x, y] = a;", format("auto[x, y] = a;")); + EXPECT_EQ("auto &[x, y] = a;", format("auto & [x, y] = a;")); + EXPECT_EQ("auto &&[x, y] = a;", format("auto && [x, y] = a;")); + EXPECT_EQ("auto &&[x, y] = a;", format("auto && [x, y] = a;")); + EXPECT_EQ("auto [x] = a;", format("auto[x] = a;")); + EXPECT_EQ("auto &[x] = a;", format("auto & [x] = a;")); + EXPECT_EQ("auto &&[x] = a;", format("auto && [x] = a;")); + EXPECT_EQ("const auto [x, y] = f();", format("const auto[x, y] = f();")); + EXPECT_EQ("const auto &[x, y] = f();", format("const auto & [x, y] = f();")); + EXPECT_EQ("const auto &&[x, y] = f();", format("const auto && [x, y] = f();")); + EXPECT_EQ("auto [x, y] = A{};", format("auto[x,y] = A{};")); + EXPECT_EQ("auto &[x, y] = A{};", format("auto & [x,y] = A{};")); + EXPECT_EQ("auto &&[x, y] = A{};", format("auto && [x,y] = A{};")); + EXPECT_EQ("for (const auto &&[a, b] : some_range) {\n" + "}", + format("for (const auto && [a, b] : some_range) {\n" + "}")); + EXPECT_EQ("for (const auto &[a, b] : some_range) {\n" + "}", + format("for (const auto & [a, b] : some_range) {\n" + "}")); + EXPECT_EQ("for (const auto [a, b] : some_range) {\n" + "}", + format("for (const auto[a, b] : some_range) {\n" + "}")); + EXPECT_EQ("auto [x, y](expr);", format("auto[x,y] (expr);")); + EXPECT_EQ("auto &[x, y](expr);", format("auto & [x,y] (expr);")); + EXPECT_EQ("auto &&[x, y](expr);", format("auto && [x,y] (expr);")); + EXPECT_EQ("auto [x, y]{expr};", format("auto[x,y] {expr};")); + EXPECT_EQ("auto &[x, y]{expr};", format("auto & [x,y] {expr};")); + EXPECT_EQ("auto &&[x, y]{expr};", format("auto && [x,y] {expr};")); +} + } // end namespace } // end namespace format } // end namespace clang