https://github.com/chomosuke updated https://github.com/llvm/llvm-project/pull/118569
>From d2257eb43bbd9ce2dfd8a13123c2048e1cf8b439 Mon Sep 17 00:00:00 2001 From: chomosuke <a13323...@gmail.com> Date: Wed, 4 Dec 2024 00:07:31 +0000 Subject: [PATCH 1/4] clangd cleanupAroundReplacements just as clang-tidy does --- clang-tools-extra/clangd/Diagnostics.cpp | 48 +++++++++++++++++++++++- 1 file changed, 46 insertions(+), 2 deletions(-) diff --git a/clang-tools-extra/clangd/Diagnostics.cpp b/clang-tools-extra/clangd/Diagnostics.cpp index a59d1e7ac84096..af8d0d534af689 100644 --- a/clang-tools-extra/clangd/Diagnostics.cpp +++ b/clang-tools-extra/clangd/Diagnostics.cpp @@ -19,6 +19,7 @@ #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TokenKinds.h" +#include "clang/Format/Format.h" #include "clang/Lex/Lexer.h" #include "clang/Lex/Token.h" #include "llvm/ADT/ArrayRef.h" @@ -741,7 +742,7 @@ void StoreDiags::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, return false; // Copy as we may modify the ranges. auto FixIts = Info.getFixItHints().vec(); - llvm::SmallVector<TextEdit, 1> Edits; + auto Replacements = std::make_optional<tooling::Replacements>(); for (auto &FixIt : FixIts) { // Allow fixits within a single macro-arg expansion to be applied. // This can be incorrect if the argument is expanded multiple times in @@ -761,7 +762,50 @@ void StoreDiags::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, return false; if (!isInsideMainFile(FixIt.RemoveRange.getBegin(), SM)) return false; - Edits.push_back(toTextEdit(FixIt, SM, *LangOpts)); + + auto R = tooling::Replacement(SM, FixIt.RemoveRange, FixIt.CodeToInsert, + *LangOpts); + auto Err = Replacements->add(R); + if (Err) { + // FIXME: Remove duplicated code as in ClangTidy.cpp + unsigned NewOffset = + Replacements->getShiftedCodePosition(R.getOffset()); + unsigned NewLength = Replacements->getShiftedCodePosition( + R.getOffset() + R.getLength()) - + NewOffset; + if (NewLength == R.getLength()) { + llvm::consumeError(std::move(Err)); + R = tooling::Replacement(R.getFilePath(), NewOffset, NewLength, + R.getReplacementText()); + Replacements = Replacements->merge(tooling::Replacements(R)); + } else { + log("Skipping formatting the replacement due to conflict: {0}", + llvm::toString(std::move(Err))); + Replacements = std::nullopt; + break; + } + } + } + + llvm::SmallVector<TextEdit, 1> Edits; + + if (Replacements) { + StringRef Code = SM.getBufferData(SM.getMainFileID()); + auto Repl = format::cleanupAroundReplacements(Code, *Replacements, + format::getNoStyle()); + if (!Repl) { + log("Skipping formatting the replacement due to conflict: {0}", + llvm::toString(std::move(Repl.takeError()))); + Replacements = std::nullopt; + } else { + auto Es = replacementsToEdits(Code, *Repl); + Edits.append(Es.begin(), Es.end()); + } + } + if (!Replacements) { + for (auto &FixIt : FixIts) { + Edits.push_back(toTextEdit(FixIt, SM, *LangOpts)); + } } llvm::SmallString<64> Message; >From cbdc1e25e3a39472c068a742b0f40e9c71221521 Mon Sep 17 00:00:00 2001 From: chomosuke <a13323...@gmail.com> Date: Wed, 4 Dec 2024 01:15:57 +0000 Subject: [PATCH 2/4] fixed tests --- .../clangd/unittests/DiagnosticsTests.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp index 7a47d6ebebf3b2..94765b48099eda 100644 --- a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp +++ b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp @@ -876,17 +876,17 @@ TEST(DiagnosticTest, ClangTidySelfContainedDiags) { clangd::Fix ExpectedCFix; ExpectedCFix.Message = "variable 'C' is not initialized"; - ExpectedCFix.Edits.push_back(TextEdit{Main.range("CFix"), " = NAN"}); ExpectedCFix.Edits.push_back( TextEdit{Main.range("MathHeader"), "#include <math.h>\n\n"}); + ExpectedCFix.Edits.push_back(TextEdit{Main.range("CFix"), " = NAN"}); // Again in clang-tidy only the include directive would be emitted for the // first warning. However we need the include attaching for both warnings. clangd::Fix ExpectedDFix; ExpectedDFix.Message = "variable 'D' is not initialized"; - ExpectedDFix.Edits.push_back(TextEdit{Main.range("DFix"), " = NAN"}); ExpectedDFix.Edits.push_back( TextEdit{Main.range("MathHeader"), "#include <math.h>\n\n"}); + ExpectedDFix.Edits.push_back(TextEdit{Main.range("DFix"), " = NAN"}); EXPECT_THAT( TU.build().getDiagnostics(), ifTidyChecks(UnorderedElementsAre( @@ -921,14 +921,14 @@ TEST(DiagnosticTest, ClangTidySelfContainedDiagsFormatting) { clangd::Fix const ExpectedFix1{ "prefer using 'override' or (rarely) 'final' " "instead of 'virtual'", - {TextEdit{Main.range("override1"), " override"}, - TextEdit{Main.range("virtual1"), ""}}, + {TextEdit{Main.range("virtual1"), ""}, + TextEdit{Main.range("override1"), " override"}}, {}}; clangd::Fix const ExpectedFix2{ "prefer using 'override' or (rarely) 'final' " "instead of 'virtual'", - {TextEdit{Main.range("override2"), " override"}, - TextEdit{Main.range("virtual2"), ""}}, + {TextEdit{Main.range("virtual2"), ""}, + TextEdit{Main.range("override2"), " override"}}, {}}; // Note that in the Fix we expect the "virtual" keyword and the following // whitespace to be deleted @@ -1930,10 +1930,10 @@ TEST(ParsedASTTest, ModuleSawDiag) { TestTU TU; auto AST = TU.build(); - #if 0 +#if 0 EXPECT_THAT(AST.getDiagnostics(), testing::Contains(Diag(Code.range(), KDiagMsg.str()))); - #endif +#endif } TEST(Preamble, EndsOnNonEmptyLine) { >From 0735f2821c36827c1b123d63d4ca9e716cc790dd Mon Sep 17 00:00:00 2001 From: chomosuke <a13323...@gmail.com> Date: Wed, 4 Dec 2024 13:16:31 +0000 Subject: [PATCH 3/4] Added test --- .../clangd/unittests/DiagnosticsTests.cpp | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp index 94765b48099eda..6d9f8d5931a0e5 100644 --- a/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp +++ b/clang-tools-extra/clangd/unittests/DiagnosticsTests.cpp @@ -2136,6 +2136,43 @@ TEST(DiagnosticsTest, UnusedInHeader) { EXPECT_THAT(TU.build().getDiagnostics(), IsEmpty()); } +TEST(DiagnosticsTest, CleanupAroundReplacements) { + Annotations Test(R"cpp( + struct PositiveValueChar { + PositiveValueChar() : $c0fix[[c0()]]$comma[[,]] $c1fix[[c1()]] {} + const char $c0[[c0]]; + wchar_t $c1[[c1]]; + }; + )cpp"); + auto TU = TestTU::withCode(Test.code()); + TU.ClangTidyProvider = addTidyChecks("modernize-use-default-member-init"); + + clangd::Fix C0Fix; + C0Fix.Message = "use default member initializer for 'c0'"; + C0Fix.Edits.push_back( + TextEdit{{Test.range("c0fix").start, Test.range("comma").end}, ""}); + C0Fix.Edits.push_back( + TextEdit{{Test.range("c0").end, Test.range("c0").end}, "{}"}); + + clangd::Fix C1Fix; + C1Fix.Message = "use default member initializer for 'c1'"; + C1Fix.Edits.push_back( + TextEdit{Test.range("comma"), ""}); + C1Fix.Edits.push_back( + TextEdit{Test.range("c1fix"), ""}); + C1Fix.Edits.push_back( + TextEdit{{Test.range("c1").end, Test.range("c1").end}, "{}"}); + + EXPECT_THAT(TU.build().getDiagnostics(), + ifTidyChecks(UnorderedElementsAre( + AllOf(Diag(Test.range("c0"), + "use default member initializer for 'c0'"), + withFix(equalToFix(C0Fix))), + AllOf(Diag(Test.range("c1"), + "use default member initializer for 'c1'"), + withFix(equalToFix(C1Fix)))))); +} + } // namespace } // namespace clangd } // namespace clang >From f0331d1bc663dfc24df7832798cb794e87e01efa Mon Sep 17 00:00:00 2001 From: chomosuke <a13323...@gmail.com> Date: Wed, 4 Dec 2024 14:24:25 +0000 Subject: [PATCH 4/4] removed duplicated code --- clang-tools-extra/clang-tidy/ClangTidy.cpp | 34 ++++++------------- clang-tools-extra/clangd/Diagnostics.cpp | 23 +++---------- .../include/clang/Tooling/Core/Replacement.h | 6 +++- clang/lib/Tooling/Core/Replacement.cpp | 33 ++++++++++++------ 4 files changed, 42 insertions(+), 54 deletions(-) diff --git a/clang-tools-extra/clang-tidy/ClangTidy.cpp b/clang-tools-extra/clang-tidy/ClangTidy.cpp index 9c8c93c5d16c72..fc9926e2105a9a 100644 --- a/clang-tools-extra/clang-tidy/ClangTidy.cpp +++ b/clang-tools-extra/clang-tidy/ClangTidy.cpp @@ -149,26 +149,12 @@ class ErrorReporter { Repl.getLength(), Repl.getReplacementText()); auto &Entry = FileReplacements[R.getFilePath()]; Replacements &Replacements = Entry.Replaces; - llvm::Error Err = Replacements.add(R); + llvm::Error Err = Replacements.addOrMerge(R); if (Err) { // FIXME: Implement better conflict handling. - llvm::errs() << "Trying to resolve conflict: " - << llvm::toString(std::move(Err)) << "\n"; - unsigned NewOffset = - Replacements.getShiftedCodePosition(R.getOffset()); - unsigned NewLength = Replacements.getShiftedCodePosition( - R.getOffset() + R.getLength()) - - NewOffset; - if (NewLength == R.getLength()) { - R = Replacement(R.getFilePath(), NewOffset, NewLength, - R.getReplacementText()); - Replacements = Replacements.merge(tooling::Replacements(R)); - CanBeApplied = true; - ++AppliedFixes; - } else { - llvm::errs() - << "Can't resolve conflict, skipping the replacement.\n"; - } + llvm::errs() + << "Can't resolve conflict, skipping the replacement: " + << llvm::toString(std::move(Err)) << '\n'; } else { CanBeApplied = true; ++AppliedFixes; @@ -424,8 +410,8 @@ ClangTidyASTConsumerFactory::createASTConsumer( std::unique_ptr<ClangTidyProfiling> Profiling; if (Context.getEnableProfiling()) { - Profiling = std::make_unique<ClangTidyProfiling>( - Context.getProfileStorageParams()); + Profiling = + std::make_unique<ClangTidyProfiling>(Context.getProfileStorageParams()); FinderOptions.CheckProfiling.emplace(Profiling->Records); } @@ -437,8 +423,8 @@ ClangTidyASTConsumerFactory::createASTConsumer( if (Context.canEnableModuleHeadersParsing() && Context.getLangOpts().Modules && OverlayFS != nullptr) { - auto ModuleExpander = std::make_unique<ExpandModularHeadersPPCallbacks>( - &Compiler, OverlayFS); + auto ModuleExpander = + std::make_unique<ExpandModularHeadersPPCallbacks>(&Compiler, OverlayFS); ModuleExpanderPP = ModuleExpander->getPreprocessor(); PP->addPPCallbacks(std::move(ModuleExpander)); } @@ -502,7 +488,7 @@ getCheckNames(const ClangTidyOptions &Options, bool AllowEnablingAnalyzerAlphaCheckers) { clang::tidy::ClangTidyContext Context( std::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(), - Options), + Options), AllowEnablingAnalyzerAlphaCheckers); ClangTidyASTConsumerFactory Factory(Context); return Factory.getCheckNames(); @@ -513,7 +499,7 @@ getCheckOptions(const ClangTidyOptions &Options, bool AllowEnablingAnalyzerAlphaCheckers) { clang::tidy::ClangTidyContext Context( std::make_unique<DefaultOptionsProvider>(ClangTidyGlobalOptions(), - Options), + Options), AllowEnablingAnalyzerAlphaCheckers); ClangTidyDiagnosticConsumer DiagConsumer(Context); DiagnosticsEngine DE(llvm::makeIntrusiveRefCnt<DiagnosticIDs>(), diff --git a/clang-tools-extra/clangd/Diagnostics.cpp b/clang-tools-extra/clangd/Diagnostics.cpp index af8d0d534af689..9f270af506fce1 100644 --- a/clang-tools-extra/clangd/Diagnostics.cpp +++ b/clang-tools-extra/clangd/Diagnostics.cpp @@ -765,25 +765,12 @@ void StoreDiags::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, auto R = tooling::Replacement(SM, FixIt.RemoveRange, FixIt.CodeToInsert, *LangOpts); - auto Err = Replacements->add(R); + auto Err = Replacements->addOrMerge(R); if (Err) { - // FIXME: Remove duplicated code as in ClangTidy.cpp - unsigned NewOffset = - Replacements->getShiftedCodePosition(R.getOffset()); - unsigned NewLength = Replacements->getShiftedCodePosition( - R.getOffset() + R.getLength()) - - NewOffset; - if (NewLength == R.getLength()) { - llvm::consumeError(std::move(Err)); - R = tooling::Replacement(R.getFilePath(), NewOffset, NewLength, - R.getReplacementText()); - Replacements = Replacements->merge(tooling::Replacements(R)); - } else { - log("Skipping formatting the replacement due to conflict: {0}", - llvm::toString(std::move(Err))); - Replacements = std::nullopt; - break; - } + log("Skipping formatting the replacement due to conflict: {0}", + llvm::toString(std::move(Err))); + Replacements = std::nullopt; + break; } } diff --git a/clang/include/clang/Tooling/Core/Replacement.h b/clang/include/clang/Tooling/Core/Replacement.h index f9452111e147f1..835a68847592ba 100644 --- a/clang/include/clang/Tooling/Core/Replacement.h +++ b/clang/include/clang/Tooling/Core/Replacement.h @@ -260,6 +260,10 @@ class Replacements { /// category of replacements. llvm::Error add(const Replacement &R); + /// Adds a replacement `R` into `Replaces` or merges it into `Replaces` by + /// applying all existing Replaces first if there is conflict. + llvm::Error addOrMerge(const Replacement &R); + /// Merges \p Replaces into the current replacements. \p Replaces /// refers to code after applying the current replacements. [[nodiscard]] Replacements merge(const Replacements &Replaces) const; @@ -282,7 +286,7 @@ class Replacements { const_iterator end() const { return Replaces.end(); } - const_reverse_iterator rbegin() const { return Replaces.rbegin(); } + const_reverse_iterator rbegin() const { return Replaces.rbegin(); } const_reverse_iterator rend() const { return Replaces.rend(); } diff --git a/clang/lib/Tooling/Core/Replacement.cpp b/clang/lib/Tooling/Core/Replacement.cpp index 92e9859ca206e5..4586fbd7a03223 100644 --- a/clang/lib/Tooling/Core/Replacement.cpp +++ b/clang/lib/Tooling/Core/Replacement.cpp @@ -40,7 +40,7 @@ using namespace clang; using namespace tooling; -static const char * const InvalidLocation = ""; +static const char *const InvalidLocation = ""; Replacement::Replacement() : FilePath(InvalidLocation) {} @@ -61,9 +61,7 @@ Replacement::Replacement(const SourceManager &Sources, setFromSourceRange(Sources, Range, ReplacementText, LangOpts); } -bool Replacement::isApplicable() const { - return FilePath != InvalidLocation; -} +bool Replacement::isApplicable() const { return FilePath != InvalidLocation; } bool Replacement::apply(Rewriter &Rewrite) const { SourceManager &SM = Rewrite.getSourceMgr(); @@ -72,9 +70,8 @@ bool Replacement::apply(Rewriter &Rewrite) const { return false; FileID ID = SM.getOrCreateFileID(*Entry, SrcMgr::C_User); - const SourceLocation Start = - SM.getLocForStartOfFile(ID). - getLocWithOffset(ReplacementRange.getOffset()); + const SourceLocation Start = SM.getLocForStartOfFile(ID).getLocWithOffset( + ReplacementRange.getOffset()); // ReplaceText returns false on success. // ReplaceText only fails if the source location is not a file location, in // which case we already returned false earlier. @@ -139,7 +136,8 @@ static int getRangeSize(const SourceManager &Sources, SourceLocation SpellingEnd = Sources.getSpellingLoc(Range.getEnd()); std::pair<FileID, unsigned> Start = Sources.getDecomposedLoc(SpellingBegin); std::pair<FileID, unsigned> End = Sources.getDecomposedLoc(SpellingEnd); - if (Start.first != End.first) return -1; + if (Start.first != End.first) + return -1; if (Range.isTokenRange()) End.second += Lexer::MeasureTokenLength(SpellingEnd, Sources, LangOpts); return End.second - Start.second; @@ -342,6 +340,20 @@ llvm::Error Replacements::add(const Replacement &R) { return llvm::Error::success(); } +llvm::Error Replacements::addOrMerge(const Replacement &R) { + auto Err = add(R); + if (Err) { + llvm::consumeError(std::move(Err)); + auto Replace = getReplacementInChangedCode(R); + if (Replace.getLength() != R.getLength()) { + return llvm::make_error<ReplacementError>( + replacement_error::overlap_conflict, R); + } + *this = merge(tooling::Replacements(Replace)); + } + return llvm::Error::success(); +} + namespace { // Represents a merged replacement, i.e. a replacement consisting of multiple @@ -578,7 +590,7 @@ bool applyAllReplacements(const Replacements &Replaces, Rewriter &Rewrite) { } llvm::Expected<std::string> applyAllReplacements(StringRef Code, - const Replacements &Replaces) { + const Replacements &Replaces) { if (Replaces.empty()) return Code.str(); @@ -593,8 +605,7 @@ llvm::Expected<std::string> applyAllReplacements(StringRef Code, InMemoryFileSystem->addFile( "<stdin>", 0, llvm::MemoryBuffer::getMemBuffer(Code, "<stdin>")); FileID ID = SourceMgr.createFileID(*Files.getOptionalFileRef("<stdin>"), - SourceLocation(), - clang::SrcMgr::C_User); + SourceLocation(), clang::SrcMgr::C_User); for (auto I = Replaces.rbegin(), E = Replaces.rend(); I != E; ++I) { Replacement Replace("<stdin>", I->getOffset(), I->getLength(), I->getReplacementText()); _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits