Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -109,10 +109,8 @@ } }; -template <> -struct ScalarEnumerationTraits { - static void enumeration(IO &IO, - FormatStyle::PointerAlignmentStyle &Value) { +template <> struct ScalarEnumerationTraits { + static void enumeration(IO &IO, FormatStyle::PointerAlignmentStyle &Value) { IO.enumCase(Value, "Middle", FormatStyle::PAS_Middle); IO.enumCase(Value, "Left", FormatStyle::PAS_Left); IO.enumCase(Value, "Right", FormatStyle::PAS_Right); @@ -144,8 +142,8 @@ IO.mapOptional("Language", Style.Language); if (IO.outputting()) { - StringRef StylesArray[] = { "LLVM", "Google", "Chromium", - "Mozilla", "WebKit", "GNU" }; + StringRef StylesArray[] = {"LLVM", "Google", "Chromium", + "Mozilla", "WebKit", "GNU"}; ArrayRef Styles(StylesArray); for (size_t i = 0, e = Styles.size(); i < e; ++i) { StringRef StyleName(Styles[i]); @@ -273,7 +271,7 @@ // will be used to get default values for missing keys. // If the first element has no Language specified, it will be treated as the // default one for the following elements. -template <> struct DocumentListTraits > { +template <> struct DocumentListTraits> { static size_t size(IO &IO, std::vector &Seq) { return Seq.size(); } @@ -600,10 +598,10 @@ FormatTokenLexer(SourceManager &SourceMgr, FileID ID, FormatStyle &Style, encoding::Encoding Encoding) : FormatTok(nullptr), IsFirstToken(true), GreaterStashed(false), - Column(0), TrailingWhitespace(0), SourceMgr(SourceMgr), ID(ID), - Style(Style), IdentTable(getFormattingLangOpts(Style)), - Keywords(IdentTable), Encoding(Encoding), FirstInLineIndex(0), - FormattingDisabled(false) { + LessStashed(false), Column(0), TrailingWhitespace(0), + SourceMgr(SourceMgr), ID(ID), Style(Style), + IdentTable(getFormattingLangOpts(Style)), Keywords(IdentTable), + Encoding(Encoding), FirstInLineIndex(0), FormattingDisabled(false) { Lex.reset(new Lexer(ID, SourceMgr.getBuffer(ID), SourceMgr, getFormattingLangOpts(Style))); Lex->SetKeepWhitespaceMode(true); @@ -633,6 +631,8 @@ return; if (tryMergeConflictMarkers()) return; + if (tryMergeLessLess()) + return; if (Style.Language == FormatStyle::LK_JavaScript) { if (tryMergeJSRegexLiteral()) @@ -640,11 +640,11 @@ if (tryMergeEscapeSequence()) return; - static tok::TokenKind JSIdentity[] = { tok::equalequal, tok::equal }; - static tok::TokenKind JSNotIdentity[] = { tok::exclaimequal, tok::equal }; - static tok::TokenKind JSShiftEqual[] = { tok::greater, tok::greater, - tok::greaterequal }; - static tok::TokenKind JSRightArrow[] = { tok::equal, tok::greater }; + static tok::TokenKind JSIdentity[] = {tok::equalequal, tok::equal}; + static tok::TokenKind JSNotIdentity[] = {tok::exclaimequal, tok::equal}; + static tok::TokenKind JSShiftEqual[] = {tok::greater, tok::greater, + tok::greaterequal}; + static tok::TokenKind JSRightArrow[] = {tok::equal, tok::greater}; // FIXME: We probably need to change token type to mimic operator with the // correct priority. if (tryMergeTokens(JSIdentity)) @@ -658,6 +658,39 @@ } } + bool tryMergeLessLess() { + // Merge X,less,less,Y into X,lessless,Y unless X or Y is less. + if (Tokens.size() < 4) { + // Merge <,<,eof to <<,eof + if (Tokens.back()->Tok.isNot(tok::eof)) + return false; + + auto &eof = Tokens.back(); + Tokens.pop_back(); + bool LessLessMerged; + if ((LessLessMerged = tryMergeTokens({tok::less, tok::less}))) + Tokens.back()->Tok.setKind(tok::lessless); + Tokens.push_back(eof); + return LessLessMerged; + } + + auto First = Tokens.end() - 4; + if (First[3]->is(tok::less) || First[2]->isNot(tok::less) || + First[1]->isNot(tok::less) || First[0]->is(tok::less)) + return false; + + // Only merge if there currently is no whitespace between the two "<". + if (First[2]->WhitespaceRange.getBegin() != + First[2]->WhitespaceRange.getEnd()) + return false; + + First[1]->Tok.setKind(tok::lessless); + First[1]->TokenText = "<<"; + First[1]->ColumnWidth += 1; + Tokens.erase(Tokens.end() - 2); + return true; + } + bool tryMergeTokens(ArrayRef Kinds) { if (Tokens.size() < Kinds.size()) return false; @@ -668,8 +701,9 @@ return false; unsigned AddLength = 0; for (unsigned i = 1; i < Kinds.size(); ++i) { - if (!First[i]->is(Kinds[i]) || First[i]->WhitespaceRange.getBegin() != - First[i]->WhitespaceRange.getEnd()) + if (!First[i]->is(Kinds[i]) || + First[i]->WhitespaceRange.getBegin() != + First[i]->WhitespaceRange.getEnd()) return false; AddLength += First[i]->TokenText.size(); } @@ -842,22 +876,31 @@ return false; } + FormatToken *getStashedToken() { + // Create a synthesized second '>' or '<' token. + Token Tok = FormatTok->Tok; + StringRef TokenText = FormatTok->TokenText; + + unsigned OriginalColumn = FormatTok->OriginalColumn; + FormatTok = new (Allocator.Allocate()) FormatToken; + FormatTok->Tok = Tok; + SourceLocation TokLocation = + FormatTok->Tok.getLocation().getLocWithOffset(1); + FormatTok->WhitespaceRange = SourceRange(TokLocation, TokLocation); + FormatTok->TokenText = TokenText; + FormatTok->ColumnWidth = 1; + FormatTok->OriginalColumn = OriginalColumn; + return FormatTok; + } + FormatToken *getNextToken() { if (GreaterStashed) { - // Create a synthesized second '>' token. - Token Greater = FormatTok->Tok; - unsigned OriginalColumn = FormatTok->OriginalColumn; - FormatTok = new (Allocator.Allocate()) FormatToken; - FormatTok->Tok = Greater; - SourceLocation GreaterLocation = - FormatTok->Tok.getLocation().getLocWithOffset(1); - FormatTok->WhitespaceRange = - SourceRange(GreaterLocation, GreaterLocation); - FormatTok->TokenText = ">"; - FormatTok->ColumnWidth = 1; - FormatTok->OriginalColumn = OriginalColumn; GreaterStashed = false; - return FormatTok; + return getStashedToken(); + } + if (LessStashed) { + LessStashed = false; + return getStashedToken(); } FormatTok = new (Allocator.Allocate()) FormatToken; @@ -952,6 +995,10 @@ FormatTok->Tok.setKind(tok::greater); FormatTok->TokenText = FormatTok->TokenText.substr(0, 1); GreaterStashed = true; + } else if (FormatTok->Tok.is(tok::lessless)) { + FormatTok->Tok.setKind(tok::less); + FormatTok->TokenText = FormatTok->TokenText.substr(0, 1); + LessStashed = true; } // Now FormatTok is the next non-whitespace token. @@ -988,7 +1035,7 @@ FormatToken *FormatTok; bool IsFirstToken; - bool GreaterStashed; + bool GreaterStashed, LessStashed; unsigned Column; unsigned TrailingWhitespace; std::unique_ptr Lex; Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -902,7 +902,7 @@ return false; if (Tok.Previous->is(TT_LeadingJavaAnnotation)) - return false; + return false; // Skip "const" as it does not have an influence on whether this is a name. FormatToken *PreviousNotConst = Tok.Previous; @@ -2024,8 +2024,8 @@ if (Right.is(TT_CtorInitializerComma) && Style.BreakConstructorInitializersBeforeComma) return true; - if (Left.is(tok::greater) && Right.is(tok::greater) && - Left.isNot(TT_TemplateCloser)) + if ((Left.is(tok::greater) && Right.is(tok::greater)) || + (Left.is(tok::less) && Right.is(tok::less))) return false; if (Right.is(TT_BinaryOperator) && Style.BreakBeforeBinaryOperators != FormatStyle::BOS_None && Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -9737,6 +9737,27 @@ verifyFormat("A>();", Spaces); } +TEST_F(FormatTest, TripleAngleBrackets) { + verifyFormat("f<<<1, 1>>>();"); + verifyFormat("f<<<1, 1, 1, s>>>();"); + verifyFormat("f<<>>();"); + EXPECT_EQ("f<<<1, 1>>>();", + format("f <<< 1, 1 >>> ();")); + verifyFormat("f<<<1, 1>>>();"); + verifyFormat("f<1><<<1, 1>>>();"); + EXPECT_EQ("f<<<1, 1>>>();", + format("f< param > <<< 1, 1 >>> ();")); + verifyFormat("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaaaaaaaaa<<<\n 1, 1>>>();"); +} + +TEST_F(FormatTest, MergeLessLessAtEnd) { + verifyFormat("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaallvm::outs() <<"); + verifyFormat("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaallvm::outs()\n <<"); +} + TEST_F(FormatTest, HandleUnbalancedImplicitBracesAcrossPPBranches) { std::string code = "#if A\n" "#if B\n"