Index: lib/Format/TokenAnnotator.cpp =================================================================== --- lib/Format/TokenAnnotator.cpp +++ lib/Format/TokenAnnotator.cpp @@ -543,7 +543,8 @@ return false; } } - return true; + // There are no top-level unbalanced braces in text protos. + return Style.Language != FormatStyle::LK_TextProto; } void updateParameterCount(FormatToken *Left, FormatToken *Current) { @@ -661,6 +662,13 @@ } else if (Contexts.back().ContextKind == tok::l_paren) { Tok->Type = TT_InlineASMColon; } + if ((Style.Language == FormatStyle::LK_Proto || + Style.Language == FormatStyle::LK_TextProto) && + (!CurrentToken || + CurrentToken->is(tok::r_brace) || + CurrentToken->is(tok::r_square) || + CurrentToken->is(tok::greater))) + return false; break; case tok::pipe: case tok::amp: @@ -747,6 +755,10 @@ if (Previous && Previous->Type != TT_DictLiteral) Previous->Type = TT_SelectorName; } + } else if (Style.Language == FormatStyle::LK_TextProto || + Style.Language == FormatStyle::LK_Proto) { + // In TT_Proto and TT_TextProto, tok::less cannot be a binary operator. + return false; } else { Tok->Type = TT_BinaryOperator; NonTemplateLess.insert(Tok); @@ -758,13 +770,16 @@ case tok::r_square: return false; case tok::r_brace: - // Lines can start with '}'. - if (Tok->Previous) + // Lines can start with '}' except in text protos. + if (Tok->Previous || Style.Language == FormatStyle::LK_TextProto) return false; break; case tok::greater: - if (Style.Language != FormatStyle::LK_TextProto) - Tok->Type = TT_BinaryOperator; + // In protos and text protos tok::greater cannot be a binary operator. + if (Style.Language == FormatStyle::LK_Proto || + Style.Language == FormatStyle::LK_TextProto) + return false; + Tok->Type = TT_BinaryOperator; break; case tok::kw_operator: if (Style.Language == FormatStyle::LK_TextProto || Index: unittests/Format/FormatTestProto.cpp =================================================================== --- unittests/Format/FormatTestProto.cpp +++ unittests/Format/FormatTestProto.cpp @@ -18,13 +18,23 @@ namespace format { class FormatTestProto : public ::testing::Test { + enum StatusCheck { SC_ExpectComplete, SC_ExpectIncomplete, SC_DoNotCheck }; + protected: static std::string format(llvm::StringRef Code, unsigned Offset, - unsigned Length, const FormatStyle &Style) { + unsigned Length, const FormatStyle &Style, + StatusCheck CheckComplete = SC_ExpectComplete) { DEBUG(llvm::errs() << "---\n"); DEBUG(llvm::errs() << Code << "\n\n"); std::vector Ranges(1, tooling::Range(Offset, Length)); - tooling::Replacements Replaces = reformat(Style, Code, Ranges); + FormattingAttemptStatus Status; + tooling::Replacements Replaces = + reformat(Style, Code, Ranges, "", &Status); + if (CheckComplete != SC_DoNotCheck) { + bool ExpectedCompleteFormat = CheckComplete == SC_ExpectComplete; + EXPECT_EQ(ExpectedCompleteFormat, Status.FormatComplete) + << Code << "\n\n"; + } auto Result = applyAllReplacements(Code, Replaces); EXPECT_TRUE(static_cast(Result)); DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); @@ -40,6 +50,12 @@ static void verifyFormat(llvm::StringRef Code) { EXPECT_EQ(Code.str(), format(test::messUp(Code))); } + + static void verifyIncompleteFormat(llvm::StringRef Code) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_Proto); + EXPECT_EQ(Code.str(), + format(Code, 0, Code.size(), Style, SC_ExpectIncomplete)); + } }; TEST_F(FormatTestProto, FormatsMessages) { @@ -490,5 +506,13 @@ "};"); } +TEST_F(FormatTestProto, IncompleteFormat) { + verifyIncompleteFormat("option ("); + verifyIncompleteFormat("option (MyProto.options) = { bbbbbbbbb: <\n"); + verifyIncompleteFormat("option (MyProto.options) = { bbbbbbbbb: }\n"); + verifyIncompleteFormat("option (MyProto.options) = { bbbbbbbbb: ]\n"); + verifyIncompleteFormat("option (MyProto.options) = { bbbbbbbbb: >\n"); +} + } // end namespace tooling } // end namespace clang Index: unittests/Format/FormatTestTextProto.cpp =================================================================== --- unittests/Format/FormatTestTextProto.cpp +++ unittests/Format/FormatTestTextProto.cpp @@ -19,12 +19,22 @@ class FormatTestTextProto : public ::testing::Test { protected: + enum StatusCheck { SC_ExpectComplete, SC_ExpectIncomplete, SC_DoNotCheck }; + static std::string format(llvm::StringRef Code, unsigned Offset, - unsigned Length, const FormatStyle &Style) { + unsigned Length, const FormatStyle &Style, + StatusCheck CheckComplete = SC_ExpectComplete) { DEBUG(llvm::errs() << "---\n"); DEBUG(llvm::errs() << Code << "\n\n"); std::vector Ranges(1, tooling::Range(Offset, Length)); - tooling::Replacements Replaces = reformat(Style, Code, Ranges); + FormattingAttemptStatus Status; + tooling::Replacements Replaces = + reformat(Style, Code, Ranges, "", &Status); + if (CheckComplete != SC_DoNotCheck) { + bool ExpectedCompleteFormat = CheckComplete == SC_ExpectComplete; + EXPECT_EQ(ExpectedCompleteFormat, Status.FormatComplete) + << Code << "\n\n"; + } auto Result = applyAllReplacements(Code, Replaces); EXPECT_TRUE(static_cast(Result)); DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); @@ -44,6 +54,12 @@ Style.ColumnLimit = 60; // To make writing tests easier. verifyFormat(Code, Style); } + + static void verifyIncompleteFormat(llvm::StringRef Code) { + FormatStyle Style = getGoogleStyle(FormatStyle::LK_TextProto); + EXPECT_EQ(Code.str(), + format(Code, 0, Code.size(), Style, SC_ExpectIncomplete)); + } }; TEST_F(FormatTestTextProto, KeepsTopLevelEntriesFittingALine) { @@ -452,5 +468,43 @@ " >\n" ">"); } + +TEST_F(FormatTestTextProto, IncompleteFormat) { + verifyIncompleteFormat("data {"); + verifyIncompleteFormat("data <"); + verifyIncompleteFormat("data ["); + verifyIncompleteFormat("data: {"); + verifyIncompleteFormat("data: <"); + verifyIncompleteFormat("data: ["); + verifyIncompleteFormat("key:"); + verifyIncompleteFormat("key:}"); + verifyIncompleteFormat("key: ]"); + verifyIncompleteFormat("key: >"); + verifyIncompleteFormat("data { key: }"); + verifyIncompleteFormat("data < key: >"); + verifyIncompleteFormat("data\n" + "[key: ]"); + verifyIncompleteFormat(": value"); + verifyIncompleteFormat(": {}"); + verifyIncompleteFormat(": <>"); + verifyIncompleteFormat(": []"); + verifyIncompleteFormat("}\n" + "key: value"); + verifyIncompleteFormat("]\n" + "key: value"); + verifyIncompleteFormat("> key: value"); + verifyIncompleteFormat("data { key: {"); + verifyIncompleteFormat("data < key: ["); + verifyIncompleteFormat("data [ key: {"); + verifyIncompleteFormat("> key: value {"); + verifyIncompleteFormat("> key: ["); + verifyIncompleteFormat("}\n" + "key: {"); + verifyIncompleteFormat("data { key: 1 id:"); + verifyIncompleteFormat("}\n" + "key {"); + verifyIncompleteFormat("> <"); +} + } // end namespace tooling } // end namespace clang