Index: include/clang/Format/Format.h =================================================================== --- include/clang/Format/Format.h +++ 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: lib/Format/Format.cpp =================================================================== --- lib/Format/Format.cpp +++ 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: lib/Format/UnwrappedLineFormatter.h =================================================================== --- lib/Format/UnwrappedLineFormatter.h +++ 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: lib/Format/UnwrappedLineFormatter.cpp =================================================================== --- lib/Format/UnwrappedLineFormatter.cpp +++ 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: test/Format/incomplete.cpp =================================================================== --- test/Format/incomplete.cpp +++ 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: tools/clang-format/ClangFormat.cpp =================================================================== --- tools/clang-format/ClangFormat.cpp +++ 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: unittests/Format/FormatTest.cpp =================================================================== --- unittests/Format/FormatTest.cpp +++ unittests/Format/FormatTest.cpp @@ -42,12 +42,13 @@ 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); + reformat(Style, Code, Ranges, "", &Status); if (CheckIncomplete != IC_DoNotCheck) { bool ExpectedIncompleteFormat = CheckIncomplete == IC_ExpectIncomplete; - EXPECT_EQ(ExpectedIncompleteFormat, IncompleteFormat) << Code << "\n\n"; + EXPECT_EQ(ExpectedIncompleteFormat, !Status.FormatComplete) + << Code << "\n\n"; } ReplacementCount = Replaces.size(); auto Result = applyAllReplacements(Code, Replaces); Index: unittests/Format/FormatTestComments.cpp =================================================================== --- unittests/Format/FormatTestComments.cpp +++ unittests/Format/FormatTestComments.cpp @@ -41,12 +41,13 @@ 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); + reformat(Style, Code, Ranges, "", &Status); if (CheckIncomplete != IC_DoNotCheck) { bool ExpectedIncompleteFormat = CheckIncomplete == IC_ExpectIncomplete; - EXPECT_EQ(ExpectedIncompleteFormat, IncompleteFormat) << Code << "\n\n"; + EXPECT_EQ(ExpectedIncompleteFormat, !Status.FormatComplete) + << Code << "\n\n"; } ReplacementCount = Replaces.size(); auto Result = applyAllReplacements(Code, Replaces); Index: unittests/Format/FormatTestJS.cpp =================================================================== --- unittests/Format/FormatTestJS.cpp +++ 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: unittests/Format/FormatTestObjC.cpp =================================================================== --- unittests/Format/FormatTestObjC.cpp +++ unittests/Format/FormatTestObjC.cpp @@ -44,12 +44,13 @@ 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); + reformat(Style, Code, Ranges, "", &Status); if (CheckIncomplete != IC_DoNotCheck) { bool ExpectedIncompleteFormat = CheckIncomplete == IC_ExpectIncomplete; - EXPECT_EQ(ExpectedIncompleteFormat, IncompleteFormat) << Code << "\n\n"; + EXPECT_EQ(ExpectedIncompleteFormat, !Status.FormatComplete) + << Code << "\n\n"; } auto Result = applyAllReplacements(Code, Replaces); EXPECT_TRUE(static_cast(Result)); Index: unittests/Format/FormatTestSelective.cpp =================================================================== --- unittests/Format/FormatTestSelective.cpp +++ 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");