From: Manuel Klimek Date: Thu, 7 May 2015 12:26:30 +0000 (+0000) Subject: Implements a way to retrieve information about whether some lines were not formatted... X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=1b514675e0626935f9bf7a999aa28d7239156e77;p=clang Implements a way to retrieve information about whether some lines were not formatted due to syntax errors. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@236722 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Format/Format.h b/include/clang/Format/Format.h index 3cb4e699c4..3fdee99f3e 100644 --- a/include/clang/Format/Format.h +++ b/include/clang/Format/Format.h @@ -546,16 +546,22 @@ std::string configurationAsText(const FormatStyle &Style); /// /// 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. /// diff --git a/lib/Format/Format.cpp b/lib/Format/Format.cpp index e7ebd68515..bb10c60b2f 100644 --- a/lib/Format/Format.cpp +++ b/lib/Format/Format.cpp @@ -1219,7 +1219,7 @@ public: << "\n"); } - tooling::Replacements format() { + tooling::Replacements format(bool *IncompleteFormat) { tooling::Replacements Result; FormatTokenLexer Tokens(SourceMgr, ID, Style, Encoding); @@ -1234,7 +1234,8 @@ public: 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 @@ public: } 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 @@ public: 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 @@ private: 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 @@ tooling::Replacements reformat(const FormatStyle &Style, StringRef Code, 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) { diff --git a/lib/Format/UnwrappedLineFormatter.cpp b/lib/Format/UnwrappedLineFormatter.cpp index fb1e8dd684..672a911d8d 100644 --- a/lib/Format/UnwrappedLineFormatter.cpp +++ b/lib/Format/UnwrappedLineFormatter.cpp @@ -403,6 +403,7 @@ UnwrappedLineFormatter::format(const SmallVectorImpl &Lines, 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 @@ UnwrappedLineFormatter::format(const SmallVectorImpl &Lines, /*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,8 @@ UnwrappedLineFormatter::format(const SmallVectorImpl &Lines, } } } + if (TheLine.Type == LT_Invalid && ShouldFormat && IncompleteFormat) + *IncompleteFormat = true; if (!DryRun) markFinalized(TheLine.First); PreviousLine = *I; diff --git a/lib/Format/UnwrappedLineFormatter.h b/lib/Format/UnwrappedLineFormatter.h index d7e1f263c5..dcc67d875f 100644 --- a/lib/Format/UnwrappedLineFormatter.h +++ b/lib/Format/UnwrappedLineFormatter.h @@ -33,9 +33,10 @@ public: 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 @@ private: // are many nested blocks. std::map *, unsigned>, unsigned> PenaltyCache; + + bool *IncompleteFormat; }; } // end namespace format } // end namespace clang diff --git a/unittests/Format/FormatTest.cpp b/unittests/Format/FormatTest.cpp index 9b55361785..4e33aa9da5 100644 --- a/unittests/Format/FormatTest.cpp +++ b/unittests/Format/FormatTest.cpp @@ -21,12 +21,25 @@ FormatStyle getGoogleStyle() { return getGoogleStyle(FormatStyle::LK_Cpp); } class FormatTest : public ::testing::Test { protected: + enum IncompleteCheck { + IC_ExpectComplete, + IC_ExpectIncomplete, + IC_DoNotCheck + }; + std::string format(llvm::StringRef Code, unsigned Offset, unsigned Length, - const FormatStyle &Style) { + const FormatStyle &Style, + IncompleteCheck CheckIncomplete = IC_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 != IC_DoNotCheck) { + bool ExpectedIncompleteFormat = CheckIncomplete == IC_ExpectIncomplete; + EXPECT_EQ(ExpectedIncompleteFormat, IncompleteFormat) << Code << "\n\n"; + } ReplacementCount = Replaces.size(); std::string Result = applyAllReplacements(Code, Replaces); EXPECT_NE("", Result); @@ -35,8 +48,9 @@ protected: } std::string format(llvm::StringRef Code, - const FormatStyle &Style = getLLVMStyle()) { - return format(Code, 0, Code.size(), Style); + const FormatStyle &Style = getLLVMStyle(), + IncompleteCheck CheckIncomplete = IC_ExpectComplete) { + return format(Code, 0, Code.size(), Style, CheckIncomplete); } FormatStyle getLLVMStyleWithColumns(unsigned ColumnLimit) { @@ -56,6 +70,12 @@ protected: 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, IC_ExpectIncomplete)); + } + void verifyGoogleFormat(llvm::StringRef Code) { verifyFormat(Code, getGoogleStyle()); } @@ -68,7 +88,7 @@ protected: /// \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, IC_DoNotCheck); } int ReplacementCount; @@ -2324,7 +2344,7 @@ TEST_F(FormatTest, FormatTryCatch) { "};\n"); // Incomplete try-catch blocks. - verifyFormat("try {} catch ("); + verifyIncompleteFormat("try {} catch ("); } TEST_F(FormatTest, FormatSEHTryCatch) { @@ -2727,23 +2747,23 @@ TEST_F(FormatTest, EmptyLinesInMacroDefinitions) { } 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 +2771,6 @@ TEST_F(FormatTest, MacroDefinitionsWithIncompleteCode) { " } \\\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 +3095,11 @@ TEST_F(FormatTest, LayoutStatementsAroundPreprocessorDirectives) { "#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 +6029,16 @@ TEST_F(FormatTest, IncorrectCodeDoNoWhile) { 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 +7321,8 @@ TEST_F(FormatTest, ObjCDictLiterals) { "}"); 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 +7365,7 @@ TEST_F(FormatTest, ObjCDictLiterals) { } TEST_F(FormatTest, ObjCArrayLiterals) { - verifyFormat("@["); + verifyIncompleteFormat("@["); verifyFormat("@[]"); verifyFormat( "NSArray *array = @[ @\" Hey \", NSApp, [NSNumber numberWithInt:42] ];");