Index: cfe/trunk/include/clang/Format/Format.h =================================================================== --- cfe/trunk/include/clang/Format/Format.h +++ cfe/trunk/include/clang/Format/Format.h @@ -1512,6 +1512,18 @@ cleanupAroundReplacements(StringRef Code, const tooling::Replacements &Replaces, const FormatStyle &Style); +/// \brief Represents the status of a formatting attempt. +struct FormattingAttemptStatus { + /// \brief A value of ``false`` means that any of the affected ranges were not + /// formatted due to a non-recoverable syntax error. + bool FormatComplete = true; + + /// \brief If ``FormatComplete`` is false, ``Line`` records a one-based + /// original line number at which a syntax error might have occurred. This is + /// based on a best-effort analysis and could be imprecise. + unsigned Line = 0; +}; + /// \brief Reformats the given \p Ranges in \p Code. /// /// Each range is extended on either end to its next bigger logic unit, i.e. @@ -1521,13 +1533,20 @@ /// Returns the ``Replacements`` necessary to make all \p Ranges comply with /// \p Style. /// -/// If ``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. +/// If ``Status`` is non-null, its value will be populated with the status of +/// this formatting attempt. See \c FormattingAttemptStatus. tooling::Replacements reformat(const FormatStyle &Style, StringRef Code, ArrayRef Ranges, StringRef FileName = "", - bool *IncompleteFormat = nullptr); + FormattingAttemptStatus *Status = nullptr); + +/// \brief Same as above, except if ``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, StringRef Code, + ArrayRef Ranges, + StringRef FileName, + bool *IncompleteFormat); /// \brief Clean up any erroneous/redundant code in the given \p Ranges in \p /// Code. Index: cfe/trunk/lib/Format/Format.cpp =================================================================== --- cfe/trunk/lib/Format/Format.cpp +++ cfe/trunk/lib/Format/Format.cpp @@ -908,8 +908,8 @@ class Formatter : public TokenAnalyzer { public: Formatter(const Environment &Env, const FormatStyle &Style, - bool *IncompleteFormat) - : TokenAnalyzer(Env, Style), IncompleteFormat(IncompleteFormat) {} + FormattingAttemptStatus *Status) + : TokenAnalyzer(Env, Style), Status(Status) {} tooling::Replacements analyze(TokenAnnotator &Annotator, @@ -931,7 +931,7 @@ Env.getSourceManager(), Whitespaces, Encoding, BinPackInconclusiveFunctions); UnwrappedLineFormatter(&Indenter, &Whitespaces, Style, Tokens.getKeywords(), - IncompleteFormat) + Env.getSourceManager(), Status) .format(AnnotatedLines); for (const auto &R : Whitespaces.generateReplacements()) if (Result.add(R)) @@ -1013,7 +1013,7 @@ } bool BinPackInconclusiveFunctions; - bool *IncompleteFormat; + FormattingAttemptStatus *Status; }; // This class clean up the erroneous/redundant code around the given ranges in @@ -1830,7 +1830,8 @@ tooling::Replacements reformat(const FormatStyle &Style, StringRef Code, ArrayRef Ranges, - StringRef FileName, bool *IncompleteFormat) { + StringRef FileName, + FormattingAttemptStatus *Status) { FormatStyle Expanded = expandPresets(Style); if (Expanded.DisableFormat) return tooling::Replacements(); @@ -1846,11 +1847,11 @@ auto NewEnv = Environment::CreateVirtualEnvironment( *NewCode, FileName, tooling::calculateRangesAfterReplacements(Fixes, Ranges)); - Formatter Format(*NewEnv, Expanded, IncompleteFormat); + Formatter Format(*NewEnv, Expanded, Status); return Fixes.merge(Format.process()); } } - Formatter Format(*Env, Expanded, IncompleteFormat); + Formatter Format(*Env, Expanded, Status); return Format.process(); }; @@ -1866,7 +1867,7 @@ return reformatAfterApplying(Requoter); } - Formatter Format(*Env, Expanded, IncompleteFormat); + Formatter Format(*Env, Expanded, Status); return Format.process(); } @@ -1879,6 +1880,16 @@ return Clean.process(); } +tooling::Replacements reformat(const FormatStyle &Style, StringRef Code, + ArrayRef Ranges, + StringRef FileName, bool *IncompleteFormat) { + FormattingAttemptStatus Status; + auto Result = reformat(Style, Code, Ranges, FileName, &Status); + if (!Status.FormatComplete) + *IncompleteFormat = true; + return Result; +} + tooling::Replacements fixNamespaceEndComments(const FormatStyle &Style, StringRef Code, ArrayRef Ranges, Index: cfe/trunk/lib/Format/UnwrappedLineFormatter.h =================================================================== --- cfe/trunk/lib/Format/UnwrappedLineFormatter.h +++ cfe/trunk/lib/Format/UnwrappedLineFormatter.h @@ -32,9 +32,11 @@ WhitespaceManager *Whitespaces, const FormatStyle &Style, const AdditionalKeywords &Keywords, - bool *IncompleteFormat) + const SourceManager &SourceMgr, + FormattingAttemptStatus *Status) : Indenter(Indenter), Whitespaces(Whitespaces), Style(Style), - Keywords(Keywords), IncompleteFormat(IncompleteFormat) {} + Keywords(Keywords), SourceMgr(SourceMgr), + Status(Status) {} /// \brief Format the current block and return the penalty. unsigned format(const SmallVectorImpl &Lines, @@ -63,7 +65,8 @@ WhitespaceManager *Whitespaces; const FormatStyle &Style; const AdditionalKeywords &Keywords; - bool *IncompleteFormat; + const SourceManager &SourceMgr; + FormattingAttemptStatus *Status; }; } // end namespace format } // end namespace clang Index: cfe/trunk/lib/Format/UnwrappedLineFormatter.cpp =================================================================== --- cfe/trunk/lib/Format/UnwrappedLineFormatter.cpp +++ cfe/trunk/lib/Format/UnwrappedLineFormatter.cpp @@ -835,8 +835,11 @@ bool ShouldFormat = TheLine.Affected || FixIndentation; // We cannot format this line; if the reason is that the line had a // parsing error, remember that. - if (ShouldFormat && TheLine.Type == LT_Invalid && IncompleteFormat) - *IncompleteFormat = true; + if (ShouldFormat && TheLine.Type == LT_Invalid && Status) { + Status->FormatComplete = false; + Status->Line = + SourceMgr.getSpellingLineNumber(TheLine.First->Tok.getLocation()); + } if (ShouldFormat && TheLine.Type != LT_Invalid) { if (!DryRun) Index: cfe/trunk/test/Format/incomplete.cpp =================================================================== --- cfe/trunk/test/Format/incomplete.cpp +++ cfe/trunk/test/Format/incomplete.cpp @@ -1,6 +1,6 @@ // RUN: grep -Ev "// *[A-Z-]+:" %s | clang-format -style=LLVM -cursor=0 \ // RUN: | FileCheck -strict-whitespace %s -// CHECK: {{"IncompleteFormat": true}} +// CHECK: {{"IncompleteFormat": true, "Line": 2}} // CHECK: {{^int\ \i;$}} int i; // CHECK: {{^f \( g \(;$}} Index: cfe/trunk/tools/clang-format/ClangFormat.cpp =================================================================== --- cfe/trunk/tools/clang-format/ClangFormat.cpp +++ cfe/trunk/tools/clang-format/ClangFormat.cpp @@ -276,14 +276,17 @@ } // Get new affected ranges after sorting `#includes`. Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges); - bool IncompleteFormat = false; + FormattingAttemptStatus Status; Replacements FormatChanges = reformat(*FormatStyle, *ChangedCode, Ranges, - AssumedFileName, &IncompleteFormat); + AssumedFileName, &Status); Replaces = Replaces.merge(FormatChanges); if (OutputXML) { outs() << "\n\n"; + << (Status.FormatComplete ? "false" : "true") << "'"; + if (!Status.FormatComplete) + outs() << " line=" << Status.Line; + outs() << ">\n"; if (Cursor.getNumOccurrences() != 0) outs() << "" << FormatChanges.getShiftedCodePosition(CursorPosition) @@ -307,11 +310,15 @@ if (Rewrite.overwriteChangedFiles()) return true; } else { - if (Cursor.getNumOccurrences() != 0) + if (Cursor.getNumOccurrences() != 0) { outs() << "{ \"Cursor\": " << FormatChanges.getShiftedCodePosition(CursorPosition) << ", \"IncompleteFormat\": " - << (IncompleteFormat ? "true" : "false") << " }\n"; + << (Status.FormatComplete ? "false" : "true"); + if (!Status.FormatComplete) + outs() << ", \"Line\": " << Status.Line; + outs() << " }\n"; + } Rewrite.getEditBuffer(ID).write(outs()); } } Index: cfe/trunk/unittests/Format/FormatTest.cpp =================================================================== --- cfe/trunk/unittests/Format/FormatTest.cpp +++ cfe/trunk/unittests/Format/FormatTest.cpp @@ -30,24 +30,25 @@ class FormatTest : public ::testing::Test { protected: - enum IncompleteCheck { - IC_ExpectComplete, - IC_ExpectIncomplete, - IC_DoNotCheck + enum StatusCheck { + SC_ExpectComplete, + SC_ExpectIncomplete, + SC_DoNotCheck }; std::string format(llvm::StringRef Code, const FormatStyle &Style = getLLVMStyle(), - IncompleteCheck CheckIncomplete = IC_ExpectComplete) { + StatusCheck CheckComplete = SC_ExpectComplete) { DEBUG(llvm::errs() << "---\n"); DEBUG(llvm::errs() << Code << "\n\n"); std::vector Ranges(1, tooling::Range(0, Code.size())); - bool IncompleteFormat = false; + FormattingAttemptStatus Status; tooling::Replacements Replaces = - reformat(Style, Code, Ranges, "", &IncompleteFormat); - if (CheckIncomplete != IC_DoNotCheck) { - bool ExpectedIncompleteFormat = CheckIncomplete == IC_ExpectIncomplete; - EXPECT_EQ(ExpectedIncompleteFormat, IncompleteFormat) << Code << "\n\n"; + reformat(Style, Code, Ranges, "", &Status); + if (CheckComplete != SC_DoNotCheck) { + bool ExpectedCompleteFormat = CheckComplete == SC_ExpectComplete; + EXPECT_EQ(ExpectedCompleteFormat, Status.FormatComplete) + << Code << "\n\n"; } ReplacementCount = Replaces.size(); auto Result = applyAllReplacements(Code, Replaces); @@ -83,7 +84,7 @@ void verifyIncompleteFormat(llvm::StringRef Code, const FormatStyle &Style = getLLVMStyle()) { EXPECT_EQ(Code.str(), - format(test::messUp(Code), Style, IC_ExpectIncomplete)); + format(test::messUp(Code), Style, SC_ExpectIncomplete)); } void verifyGoogleFormat(llvm::StringRef Code) { @@ -98,7 +99,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, IC_DoNotCheck); + format(Code, Style, SC_DoNotCheck); } int ReplacementCount; @@ -6189,7 +6190,7 @@ // Deeply nested part is untouched, rest is formatted. EXPECT_EQ(std::string("int i;\n") + Code + "int j;\n", format(std::string("int i;\n") + Code + "int j;\n", - getLLVMStyle(), IC_ExpectIncomplete)); + getLLVMStyle(), SC_ExpectIncomplete)); } //===----------------------------------------------------------------------===// Index: cfe/trunk/unittests/Format/FormatTestComments.cpp =================================================================== --- cfe/trunk/unittests/Format/FormatTestComments.cpp +++ cfe/trunk/unittests/Format/FormatTestComments.cpp @@ -29,24 +29,25 @@ class FormatTestComments : public ::testing::Test { protected: - enum IncompleteCheck { - IC_ExpectComplete, - IC_ExpectIncomplete, - IC_DoNotCheck + enum StatusCheck { + SC_ExpectComplete, + SC_ExpectIncomplete, + SC_DoNotCheck }; std::string format(llvm::StringRef Code, const FormatStyle &Style = getLLVMStyle(), - IncompleteCheck CheckIncomplete = IC_ExpectComplete) { + StatusCheck CheckComplete = SC_ExpectComplete) { DEBUG(llvm::errs() << "---\n"); DEBUG(llvm::errs() << Code << "\n\n"); std::vector Ranges(1, tooling::Range(0, Code.size())); - bool IncompleteFormat = false; + FormattingAttemptStatus Status; tooling::Replacements Replaces = - reformat(Style, Code, Ranges, "", &IncompleteFormat); - if (CheckIncomplete != IC_DoNotCheck) { - bool ExpectedIncompleteFormat = CheckIncomplete == IC_ExpectIncomplete; - EXPECT_EQ(ExpectedIncompleteFormat, IncompleteFormat) << Code << "\n\n"; + reformat(Style, Code, Ranges, "", &Status); + if (CheckComplete != SC_DoNotCheck) { + bool ExpectedCompleteFormat = CheckComplete == SC_ExpectComplete; + EXPECT_EQ(ExpectedCompleteFormat, Status.FormatComplete) + << Code << "\n\n"; } ReplacementCount = Replaces.size(); auto Result = applyAllReplacements(Code, Replaces); @@ -73,7 +74,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, IC_DoNotCheck); + format(Code, Style, SC_DoNotCheck); } int ReplacementCount; Index: cfe/trunk/unittests/Format/FormatTestJS.cpp =================================================================== --- cfe/trunk/unittests/Format/FormatTestJS.cpp +++ cfe/trunk/unittests/Format/FormatTestJS.cpp @@ -24,10 +24,10 @@ DEBUG(llvm::errs() << "---\n"); DEBUG(llvm::errs() << Code << "\n\n"); std::vector Ranges(1, tooling::Range(Offset, Length)); - bool IncompleteFormat = false; + FormattingAttemptStatus Status; tooling::Replacements Replaces = - reformat(Style, Code, Ranges, "", &IncompleteFormat); - EXPECT_FALSE(IncompleteFormat); + reformat(Style, Code, Ranges, "", &Status); + EXPECT_TRUE(Status.FormatComplete); auto Result = applyAllReplacements(Code, Replaces); EXPECT_TRUE(static_cast(Result)); DEBUG(llvm::errs() << "\n" << *Result << "\n\n"); Index: cfe/trunk/unittests/Format/FormatTestObjC.cpp =================================================================== --- cfe/trunk/unittests/Format/FormatTestObjC.cpp +++ cfe/trunk/unittests/Format/FormatTestObjC.cpp @@ -33,23 +33,24 @@ Style.Language = FormatStyle::LK_ObjC; } - enum IncompleteCheck { - IC_ExpectComplete, - IC_ExpectIncomplete, - IC_DoNotCheck + enum StatusCheck { + SC_ExpectComplete, + SC_ExpectIncomplete, + SC_DoNotCheck }; std::string format(llvm::StringRef Code, - IncompleteCheck CheckIncomplete = IC_ExpectComplete) { + StatusCheck CheckComplete = SC_ExpectComplete) { DEBUG(llvm::errs() << "---\n"); DEBUG(llvm::errs() << Code << "\n\n"); std::vector Ranges(1, tooling::Range(0, Code.size())); - bool IncompleteFormat = false; + FormattingAttemptStatus Status; tooling::Replacements Replaces = - reformat(Style, Code, Ranges, "", &IncompleteFormat); - if (CheckIncomplete != IC_DoNotCheck) { - bool ExpectedIncompleteFormat = CheckIncomplete == IC_ExpectIncomplete; - EXPECT_EQ(ExpectedIncompleteFormat, IncompleteFormat) << Code << "\n\n"; + 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)); @@ -62,7 +63,7 @@ } void verifyIncompleteFormat(StringRef Code) { - EXPECT_EQ(Code.str(), format(test::messUp(Code), IC_ExpectIncomplete)); + EXPECT_EQ(Code.str(), format(test::messUp(Code), SC_ExpectIncomplete)); } FormatStyle Style; Index: cfe/trunk/unittests/Format/FormatTestSelective.cpp =================================================================== --- cfe/trunk/unittests/Format/FormatTestSelective.cpp +++ cfe/trunk/unittests/Format/FormatTestSelective.cpp @@ -24,10 +24,10 @@ DEBUG(llvm::errs() << "---\n"); DEBUG(llvm::errs() << Code << "\n\n"); std::vector Ranges(1, tooling::Range(Offset, Length)); - bool IncompleteFormat = false; + FormattingAttemptStatus Status; tooling::Replacements Replaces = - reformat(Style, Code, Ranges, "", &IncompleteFormat); - EXPECT_FALSE(IncompleteFormat) << Code << "\n\n"; + reformat(Style, Code, Ranges, "", &Status); + EXPECT_TRUE(Status.FormatComplete) << Code << "\n\n"; auto Result = applyAllReplacements(Code, Replaces); EXPECT_TRUE(static_cast(Result)); DEBUG(llvm::errs() << "\n" << *Result << "\n\n");