Index: lib/Format/FormatToken.h =================================================================== --- lib/Format/FormatToken.h +++ lib/Format/FormatToken.h @@ -84,6 +84,7 @@ TYPE(RegexLiteral) \ TYPE(SelectorName) \ TYPE(StartOfName) \ + TYPE(StructuredBindingLSquare) \ TYPE(TemplateCloser) \ TYPE(TemplateOpener) \ TYPE(TemplateString) \ Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -310,6 +310,16 @@ return false; } + bool isCppStructuredBinding(const FormatToken *Tok) { + if (!Style.isCpp() || !Tok->is(tok::l_square)) + return false; + while (Tok && Tok->isOneOf(tok::kw_const, tok::kw_volatile, tok::amp, + tok::ampamp)) { + Tok = Tok->getPreviousNonComment(); + } + return Tok && Tok->is(tok::kw_auto); + } + bool parseSquare() { if (!CurrentToken) return false; @@ -344,7 +354,9 @@ unsigned BindingIncrease = 1; if (Left->is(TT_Unknown)) { - if (StartsObjCMethodExpr) { + if (isCppStructuredBinding(Left)) { + Left->Type = TT_StructuredBindingLSquare; + } else if (StartsObjCMethodExpr) { Left->Type = TT_ObjCMethodExpr; } else if (Style.Language == FormatStyle::LK_JavaScript && Parent && Contexts.back().ContextKind == tok::l_brace && @@ -2256,17 +2268,20 @@ if (Left.is(tok::l_square)) return (Left.is(TT_ArrayInitializerLSquare) && Style.SpacesInContainerLiterals && Right.isNot(tok::r_square)) || - (Left.is(TT_ArraySubscriptLSquare) && Style.SpacesInSquareBrackets && - Right.isNot(tok::r_square)); + (Left.isOneOf(TT_ArraySubscriptLSquare, + TT_StructuredBindingLSquare) && + Style.SpacesInSquareBrackets && Right.isNot(tok::r_square)); if (Right.is(tok::r_square)) return Right.MatchingParen && ((Style.SpacesInContainerLiterals && Right.MatchingParen->is(TT_ArrayInitializerLSquare)) || (Style.SpacesInSquareBrackets && - Right.MatchingParen->is(TT_ArraySubscriptLSquare))); + Right.MatchingParen->isOneOf(TT_ArraySubscriptLSquare, + TT_StructuredBindingLSquare))); if (Right.is(tok::l_square) && !Right.isOneOf(TT_ObjCMethodExpr, TT_LambdaLSquare, - TT_DesignatedInitializerLSquare) && + TT_DesignatedInitializerLSquare, + TT_StructuredBindingLSquare) && !Left.isOneOf(tok::numeric_constant, TT_DictLiteral)) return false; if (Left.is(tok::l_brace) && Right.is(tok::r_brace)) Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -11217,6 +11217,31 @@ EXPECT_EQ("auto c = u8'a';", format("auto c = u8'a';")); } +TEST_F(FormatTest, StructuredBindings) { + // Structured bindings is a C++17 feature. + // all modes, including C++11, C++14 and C++17 + verifyFormat("auto [a, b] = f();"); + EXPECT_EQ("auto [a, b] = f();", format("auto[a, b] = f();")); + EXPECT_EQ("const auto [a, b] = f();", format("const auto[a, b] = f();")); + EXPECT_EQ("auto const [a, b] = f();", format("auto const[a, b] = f();")); + EXPECT_EQ("auto const volatile [a, b] = f();", + format("auto const volatile[a, b] = f();")); + EXPECT_EQ("auto [a, b, c] = f();", format("auto [ a , b,c ] = f();")); + EXPECT_EQ("auto & [a, b, c] = f();", + format("auto &[ a , b,c ] = f();")); + EXPECT_EQ("auto && [a, b, c] = f();", + format("auto &&[ a , b,c ] = f();")); + EXPECT_EQ("auto const & [a, b] = f();", format("auto const&[a, b] = f();")); + EXPECT_EQ("auto const volatile && [a, b] = f();", + format("auto const volatile &&[a, b] = f();")); + EXPECT_EQ("auto && [a, b] = f();", format("auto &&[a, b] = f();")); + + format::FormatStyle Spaces = format::getLLVMStyle(); + Spaces.SpacesInSquareBrackets = true; + verifyFormat("auto [ a, b ] = f();", Spaces); + verifyFormat("auto && [ a, b ] = f();", Spaces); +} + } // end namespace } // end namespace format } // end namespace clang