Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ include/clang/Format/Format.h @@ -546,16 +546,22 @@ /// /// Returns the \c Replacements necessary to make all \p Ranges comply with /// \p Style. +/// +/// If \c IncompleteFormat is non-null, its value will be set to true if any +/// of the affected ranges were not formatted due to a non-recoverable syntax +/// error. tooling::Replacements reformat(const FormatStyle &Style, SourceManager &SourceMgr, FileID ID, - ArrayRef Ranges); + ArrayRef Ranges, + bool *IncompleteFormat = nullptr); /// \brief Reformats the given \p Ranges in \p Code. /// -/// Otherwise identical to the reformat() function consuming a \c Lexer. +/// Otherwise identical to the reformat() function using a file ID. tooling::Replacements reformat(const FormatStyle &Style, StringRef Code, ArrayRef Ranges, - StringRef FileName = ""); + StringRef FileName = "", + bool *IncompleteFormat = nullptr); /// \brief Returns the \c LangOpts that the formatter expects you to set. /// Index: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ lib/Format/Format.cpp @@ -1219,7 +1219,7 @@ << "\n"); } - tooling::Replacements format() { + tooling::Replacements format(bool *IncompleteFormat) { tooling::Replacements Result; FormatTokenLexer Tokens(SourceMgr, ID, Style, Encoding); @@ -1234,7 +1234,8 @@ for (unsigned i = 0, e = UnwrappedLines[Run].size(); i != e; ++i) { AnnotatedLines.push_back(new AnnotatedLine(UnwrappedLines[Run][i])); } - tooling::Replacements RunResult = format(AnnotatedLines, Tokens); + tooling::Replacements RunResult = + format(AnnotatedLines, Tokens, IncompleteFormat); DEBUG({ llvm::dbgs() << "Replacements for run " << Run << ":\n"; for (tooling::Replacements::iterator I = RunResult.begin(), @@ -1253,7 +1254,7 @@ } tooling::Replacements format(SmallVectorImpl &AnnotatedLines, - FormatTokenLexer &Tokens) { + FormatTokenLexer &Tokens, bool *IncompleteFormat) { TokenAnnotator Annotator(Style, Tokens.getKeywords()); for (unsigned i = 0, e = AnnotatedLines.size(); i != e; ++i) { Annotator.annotate(*AnnotatedLines[i]); @@ -1269,7 +1270,7 @@ Whitespaces, Encoding, BinPackInconclusiveFunctions); UnwrappedLineFormatter Formatter(&Indenter, &Whitespaces, Style, - Tokens.getKeywords()); + Tokens.getKeywords(), IncompleteFormat); Formatter.format(AnnotatedLines, /*DryRun=*/false); return Whitespaces.generateReplacements(); } @@ -1489,16 +1490,18 @@ tooling::Replacements reformat(const FormatStyle &Style, SourceManager &SourceMgr, FileID ID, - ArrayRef Ranges) { + ArrayRef Ranges, + bool *IncompleteFormat) { if (Style.DisableFormat) return tooling::Replacements(); Formatter formatter(Style, SourceMgr, ID, Ranges); - return formatter.format(); + return formatter.format(IncompleteFormat); } tooling::Replacements reformat(const FormatStyle &Style, StringRef Code, ArrayRef Ranges, - StringRef FileName) { + StringRef FileName, + bool *IncompleteFormat) { if (Style.DisableFormat) return tooling::Replacements(); @@ -1521,7 +1524,7 @@ SourceLocation End = Start.getLocWithOffset(Range.getLength()); CharRanges.push_back(CharSourceRange::getCharRange(Start, End)); } - return reformat(Style, SourceMgr, ID, CharRanges); + return reformat(Style, SourceMgr, ID, CharRanges, IncompleteFormat); } LangOptions getFormattingLangOpts(const FormatStyle &Style) { Index: lib/Format/UnwrappedLineFormatter.h =================================================================== --- lib/Format/UnwrappedLineFormatter.h +++ lib/Format/UnwrappedLineFormatter.h @@ -33,9 +33,10 @@ UnwrappedLineFormatter(ContinuationIndenter *Indenter, WhitespaceManager *Whitespaces, const FormatStyle &Style, - const AdditionalKeywords &Keywords) + const AdditionalKeywords &Keywords, + bool *IncompleteFormat) : Indenter(Indenter), Whitespaces(Whitespaces), Style(Style), - Keywords(Keywords) {} + Keywords(Keywords), IncompleteFormat(IncompleteFormat) {} unsigned format(const SmallVectorImpl &Lines, bool DryRun, int AdditionalIndent = 0, bool FixBadIndentation = false); @@ -169,6 +170,8 @@ // are many nested blocks. std::map *, unsigned>, unsigned> PenaltyCache; + + bool *IncompleteFormat; }; } // end namespace format } // end namespace clang Index: lib/Format/UnwrappedLineFormatter.cpp =================================================================== --- lib/Format/UnwrappedLineFormatter.cpp +++ lib/Format/UnwrappedLineFormatter.cpp @@ -403,6 +403,7 @@ bool FixIndentation = FixBadIndentation && (LevelIndent != FirstTok->OriginalColumn); + bool ShouldFormat = TheLine.Affected || FixIndentation; if (TheLine.First->is(tok::eof)) { if (PreviousLine && PreviousLine->Affected && !DryRun) { // Remove the file's trailing whitespace. @@ -411,8 +412,7 @@ /*IndentLevel=*/0, /*Spaces=*/0, /*TargetColumn=*/0); } - } else if (TheLine.Type != LT_Invalid && - (TheLine.Affected || FixIndentation)) { + } else if (TheLine.Type != LT_Invalid && ShouldFormat) { if (FirstTok->WhitespaceRange.isValid()) { if (!DryRun) formatFirstToken(*TheLine.First, PreviousLine, TheLine.Level, Indent, @@ -476,6 +476,9 @@ } } } + if (TheLine.Type == LT_Invalid && ShouldFormat && IncompleteFormat) { + *IncompleteFormat = true; + } if (!DryRun) markFinalized(TheLine.First); PreviousLine = *I; Index: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -21,12 +21,24 @@ class FormatTest : public ::testing::Test { protected: + enum IncompleteCheck { + IFA_ExpectComplete, + IFA_ExpectIncomplete, + IFA_DoNotCheck + }; + std::string format(llvm::StringRef Code, unsigned Offset, unsigned Length, - const FormatStyle &Style) { + const FormatStyle &Style, + IncompleteCheck CheckIncomplete = IFA_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); + bool IncompleteFormat = false; + tooling::Replacements Replaces = + reformat(Style, Code, Ranges, "", &IncompleteFormat); + if (CheckIncomplete != IFA_DoNotCheck) + EXPECT_EQ(CheckIncomplete == IFA_ExpectIncomplete, IncompleteFormat) + << Code << "\n\n"; ReplacementCount = Replaces.size(); std::string Result = applyAllReplacements(Code, Replaces); EXPECT_NE("", Result); @@ -35,8 +47,9 @@ } std::string format(llvm::StringRef Code, - const FormatStyle &Style = getLLVMStyle()) { - return format(Code, 0, Code.size(), Style); + const FormatStyle &Style = getLLVMStyle(), + IncompleteCheck CheckIncomplete = IFA_ExpectComplete) { + return format(Code, 0, Code.size(), Style, CheckIncomplete); } FormatStyle getLLVMStyleWithColumns(unsigned ColumnLimit) { @@ -56,6 +69,12 @@ EXPECT_EQ(Code.str(), format(test::messUp(Code), Style)); } + void verifyIncompleteFormat(llvm::StringRef Code, + const FormatStyle &Style = getLLVMStyle()) { + EXPECT_EQ(Code.str(), + format(test::messUp(Code), Style, IFA_ExpectIncomplete)); + } + void verifyGoogleFormat(llvm::StringRef Code) { verifyFormat(Code, getGoogleStyle()); } @@ -68,7 +87,7 @@ /// \brief Verify that clang-format does not crash on the given input. void verifyNoCrash(llvm::StringRef Code, const FormatStyle &Style = getLLVMStyle()) { - format(Code, Style); + format(Code, Style, IFA_DoNotCheck); } int ReplacementCount; @@ -2324,7 +2343,7 @@ "};\n"); // Incomplete try-catch blocks. - verifyFormat("try {} catch ("); + verifyIncompleteFormat("try {} catch ("); } TEST_F(FormatTest, FormatSEHTryCatch) { @@ -2727,23 +2746,23 @@ } TEST_F(FormatTest, MacroDefinitionsWithIncompleteCode) { - verifyFormat("#define A :"); + verifyIncompleteFormat("#define A :"); verifyFormat("#define SOMECASES \\\n" " case 1: \\\n" " case 2\n", getLLVMStyleWithColumns(20)); verifyFormat("#define A template "); - verifyFormat("#define STR(x) #x\n" - "f(STR(this_is_a_string_literal{));"); + verifyIncompleteFormat("#define STR(x) #x\n" + "f(STR(this_is_a_string_literal{));"); verifyFormat("#pragma omp threadprivate( \\\n" " y)), // expected-warning", getLLVMStyleWithColumns(28)); verifyFormat("#d, = };"); verifyFormat("#if \"a"); - verifyFormat("({\n" - "#define b }\\\n" - " a\n" - "a"); + verifyIncompleteFormat("({\n" + "#define b }\\\n" + " a\n" + "a"); verifyFormat("#define A \\\n" " { \\\n" " {\n" @@ -2751,7 +2770,6 @@ " } \\\n" " }", getLLVMStyleWithColumns(15)); - verifyNoCrash("#if a\na(\n#else\n#endif\n{a"); verifyNoCrash("a={0,1\n#if a\n#else\n;\n#endif\n}"); verifyNoCrash("#if a\na(\n#else\n#endif\n) a {a,b,c,d,f,g};"); @@ -3076,11 +3094,11 @@ "#else\n" "#endif"); - verifyFormat("void f(\n" - "#if A\n" - " );\n" - "#else\n" - "#endif"); + verifyIncompleteFormat("void f(\n" + "#if A\n" + " );\n" + "#else\n" + "#endif"); } TEST_F(FormatTest, GraciouslyHandleIncorrectPreprocessorConditions) { @@ -6010,16 +6028,16 @@ TEST_F(FormatTest, IncorrectCodeMissingParens) { verifyFormat("if {\n foo;\n foo();\n}"); verifyFormat("switch {\n foo;\n foo();\n}"); - verifyFormat("for {\n foo;\n foo();\n}"); + verifyIncompleteFormat("for {\n foo;\n foo();\n}"); verifyFormat("while {\n foo;\n foo();\n}"); verifyFormat("do {\n foo;\n foo();\n} while;"); } TEST_F(FormatTest, DoesNotTouchUnwrappedLinesWithErrors) { - verifyFormat("namespace {\n" - "class Foo { Foo (\n" - "};\n" - "} // comment"); + verifyIncompleteFormat("namespace {\n" + "class Foo { Foo (\n" + "};\n" + "} // comment"); } TEST_F(FormatTest, IncorrectCodeErrorDetection) { @@ -7302,8 +7320,8 @@ "}"); verifyFormat("@{1 > 2 ? @\"one\" : @\"two\" : 1 > 2 ? @1 : @2}"); - verifyFormat("[self setDict:@{}"); - verifyFormat("[self setDict:@{@1 : @2}"); + verifyIncompleteFormat("[self setDict:@{}"); + verifyIncompleteFormat("[self setDict:@{@1 : @2}"); verifyFormat("NSLog(@\"%@\", @{@1 : @2, @2 : @3}[@1]);"); verifyFormat( "NSDictionary *masses = @{@\"H\" : @1.0078, @\"He\" : @4.0026};"); @@ -7346,7 +7364,7 @@ } TEST_F(FormatTest, ObjCArrayLiterals) { - verifyFormat("@["); + verifyIncompleteFormat("@["); verifyFormat("@[]"); verifyFormat( "NSArray *array = @[ @\" Hey \", NSApp, [NSNumber numberWithInt:42] ];");