10b57cec5SDimitry Andric //===- FixItRewriter.cpp - Fix-It Rewriter Diagnostic Client --------------===// 20b57cec5SDimitry Andric // 30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 60b57cec5SDimitry Andric // 70b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 80b57cec5SDimitry Andric // 90b57cec5SDimitry Andric // This is a diagnostic client adaptor that performs rewrites as 100b57cec5SDimitry Andric // suggested by code modification hints attached to diagnostics. It 110b57cec5SDimitry Andric // then forwards any diagnostics to the adapted diagnostic client. 120b57cec5SDimitry Andric // 130b57cec5SDimitry Andric //===----------------------------------------------------------------------===// 140b57cec5SDimitry Andric 150b57cec5SDimitry Andric #include "clang/Rewrite/Frontend/FixItRewriter.h" 160b57cec5SDimitry Andric #include "clang/Basic/Diagnostic.h" 170b57cec5SDimitry Andric #include "clang/Basic/FileManager.h" 180b57cec5SDimitry Andric #include "clang/Basic/LLVM.h" 190b57cec5SDimitry Andric #include "clang/Basic/SourceLocation.h" 200b57cec5SDimitry Andric #include "clang/Basic/SourceManager.h" 210b57cec5SDimitry Andric #include "clang/Edit/Commit.h" 220b57cec5SDimitry Andric #include "clang/Edit/EditsReceiver.h" 230b57cec5SDimitry Andric #include "clang/Frontend/FrontendDiagnostic.h" 240b57cec5SDimitry Andric #include "clang/Rewrite/Core/RewriteBuffer.h" 250b57cec5SDimitry Andric #include "clang/Rewrite/Core/Rewriter.h" 260b57cec5SDimitry Andric #include "llvm/ADT/StringRef.h" 270b57cec5SDimitry Andric #include "llvm/Support/FileSystem.h" 280b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h" 290b57cec5SDimitry Andric #include <cstdio> 300b57cec5SDimitry Andric #include <memory> 310b57cec5SDimitry Andric #include <string> 320b57cec5SDimitry Andric #include <system_error> 330b57cec5SDimitry Andric #include <utility> 340b57cec5SDimitry Andric 350b57cec5SDimitry Andric using namespace clang; 360b57cec5SDimitry Andric 370b57cec5SDimitry Andric FixItRewriter::FixItRewriter(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 380b57cec5SDimitry Andric const LangOptions &LangOpts, 390b57cec5SDimitry Andric FixItOptions *FixItOpts) 400b57cec5SDimitry Andric : Diags(Diags), Editor(SourceMgr, LangOpts), Rewrite(SourceMgr, LangOpts), 410b57cec5SDimitry Andric FixItOpts(FixItOpts) { 420b57cec5SDimitry Andric Owner = Diags.takeClient(); 430b57cec5SDimitry Andric Client = Diags.getClient(); 440b57cec5SDimitry Andric Diags.setClient(this, false); 450b57cec5SDimitry Andric } 460b57cec5SDimitry Andric 470b57cec5SDimitry Andric FixItRewriter::~FixItRewriter() { 480b57cec5SDimitry Andric Diags.setClient(Client, Owner.release() != nullptr); 490b57cec5SDimitry Andric } 500b57cec5SDimitry Andric 510b57cec5SDimitry Andric bool FixItRewriter::WriteFixedFile(FileID ID, raw_ostream &OS) { 520b57cec5SDimitry Andric const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(ID); 530b57cec5SDimitry Andric if (!RewriteBuf) return true; 540b57cec5SDimitry Andric RewriteBuf->write(OS); 550b57cec5SDimitry Andric OS.flush(); 560b57cec5SDimitry Andric return false; 570b57cec5SDimitry Andric } 580b57cec5SDimitry Andric 590b57cec5SDimitry Andric namespace { 600b57cec5SDimitry Andric 610b57cec5SDimitry Andric class RewritesReceiver : public edit::EditsReceiver { 620b57cec5SDimitry Andric Rewriter &Rewrite; 630b57cec5SDimitry Andric 640b57cec5SDimitry Andric public: 650b57cec5SDimitry Andric RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) {} 660b57cec5SDimitry Andric 670b57cec5SDimitry Andric void insert(SourceLocation loc, StringRef text) override { 680b57cec5SDimitry Andric Rewrite.InsertText(loc, text); 690b57cec5SDimitry Andric } 700b57cec5SDimitry Andric 710b57cec5SDimitry Andric void replace(CharSourceRange range, StringRef text) override { 720b57cec5SDimitry Andric Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text); 730b57cec5SDimitry Andric } 740b57cec5SDimitry Andric }; 750b57cec5SDimitry Andric 760b57cec5SDimitry Andric } // namespace 770b57cec5SDimitry Andric 780b57cec5SDimitry Andric bool FixItRewriter::WriteFixedFiles( 790b57cec5SDimitry Andric std::vector<std::pair<std::string, std::string>> *RewrittenFiles) { 800b57cec5SDimitry Andric if (NumFailures > 0 && !FixItOpts->FixWhatYouCan) { 810b57cec5SDimitry Andric Diag(FullSourceLoc(), diag::warn_fixit_no_changes); 820b57cec5SDimitry Andric return true; 830b57cec5SDimitry Andric } 840b57cec5SDimitry Andric 850b57cec5SDimitry Andric RewritesReceiver Rec(Rewrite); 860b57cec5SDimitry Andric Editor.applyRewrites(Rec); 870b57cec5SDimitry Andric 880b57cec5SDimitry Andric if (FixItOpts->InPlace) { 890b57cec5SDimitry Andric // Overwriting open files on Windows is tricky, but the rewriter can do it 900b57cec5SDimitry Andric // for us. 910b57cec5SDimitry Andric Rewrite.overwriteChangedFiles(); 920b57cec5SDimitry Andric return false; 930b57cec5SDimitry Andric } 940b57cec5SDimitry Andric 950b57cec5SDimitry Andric for (iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) { 96*5f757f3fSDimitry Andric OptionalFileEntryRef Entry = 97*5f757f3fSDimitry Andric Rewrite.getSourceMgr().getFileEntryRefForID(I->first); 980b57cec5SDimitry Andric int fd; 995ffd83dbSDimitry Andric std::string Filename = 1005ffd83dbSDimitry Andric FixItOpts->RewriteFilename(std::string(Entry->getName()), fd); 1010b57cec5SDimitry Andric std::error_code EC; 1020b57cec5SDimitry Andric std::unique_ptr<llvm::raw_fd_ostream> OS; 1030b57cec5SDimitry Andric if (fd != -1) { 1040b57cec5SDimitry Andric OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true)); 1050b57cec5SDimitry Andric } else { 106a7dea167SDimitry Andric OS.reset(new llvm::raw_fd_ostream(Filename, EC, llvm::sys::fs::OF_None)); 1070b57cec5SDimitry Andric } 1080b57cec5SDimitry Andric if (EC) { 1090b57cec5SDimitry Andric Diags.Report(clang::diag::err_fe_unable_to_open_output) << Filename 1100b57cec5SDimitry Andric << EC.message(); 1110b57cec5SDimitry Andric continue; 1120b57cec5SDimitry Andric } 1130b57cec5SDimitry Andric RewriteBuffer &RewriteBuf = I->second; 1140b57cec5SDimitry Andric RewriteBuf.write(*OS); 1150b57cec5SDimitry Andric OS->flush(); 1160b57cec5SDimitry Andric 1170b57cec5SDimitry Andric if (RewrittenFiles) 1185ffd83dbSDimitry Andric RewrittenFiles->push_back( 1195ffd83dbSDimitry Andric std::make_pair(std::string(Entry->getName()), Filename)); 1200b57cec5SDimitry Andric } 1210b57cec5SDimitry Andric 1220b57cec5SDimitry Andric return false; 1230b57cec5SDimitry Andric } 1240b57cec5SDimitry Andric 1250b57cec5SDimitry Andric bool FixItRewriter::IncludeInDiagnosticCounts() const { 1260b57cec5SDimitry Andric return Client ? Client->IncludeInDiagnosticCounts() : true; 1270b57cec5SDimitry Andric } 1280b57cec5SDimitry Andric 1290b57cec5SDimitry Andric void FixItRewriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 1300b57cec5SDimitry Andric const Diagnostic &Info) { 1310b57cec5SDimitry Andric // Default implementation (Warnings/errors count). 1320b57cec5SDimitry Andric DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); 1330b57cec5SDimitry Andric 1340b57cec5SDimitry Andric if (!FixItOpts->Silent || 1350b57cec5SDimitry Andric DiagLevel >= DiagnosticsEngine::Error || 1360b57cec5SDimitry Andric (DiagLevel == DiagnosticsEngine::Note && !PrevDiagSilenced) || 1370b57cec5SDimitry Andric (DiagLevel > DiagnosticsEngine::Note && Info.getNumFixItHints())) { 1380b57cec5SDimitry Andric Client->HandleDiagnostic(DiagLevel, Info); 1390b57cec5SDimitry Andric PrevDiagSilenced = false; 1400b57cec5SDimitry Andric } else { 1410b57cec5SDimitry Andric PrevDiagSilenced = true; 1420b57cec5SDimitry Andric } 1430b57cec5SDimitry Andric 1440b57cec5SDimitry Andric // Skip over any diagnostics that are ignored or notes. 1450b57cec5SDimitry Andric if (DiagLevel <= DiagnosticsEngine::Note) 1460b57cec5SDimitry Andric return; 1470b57cec5SDimitry Andric // Skip over errors if we are only fixing warnings. 1480b57cec5SDimitry Andric if (DiagLevel >= DiagnosticsEngine::Error && FixItOpts->FixOnlyWarnings) { 1490b57cec5SDimitry Andric ++NumFailures; 1500b57cec5SDimitry Andric return; 1510b57cec5SDimitry Andric } 1520b57cec5SDimitry Andric 1530b57cec5SDimitry Andric // Make sure that we can perform all of the modifications we 1540b57cec5SDimitry Andric // in this diagnostic. 1550b57cec5SDimitry Andric edit::Commit commit(Editor); 1560b57cec5SDimitry Andric for (unsigned Idx = 0, Last = Info.getNumFixItHints(); 1570b57cec5SDimitry Andric Idx < Last; ++Idx) { 1580b57cec5SDimitry Andric const FixItHint &Hint = Info.getFixItHint(Idx); 1590b57cec5SDimitry Andric 1600b57cec5SDimitry Andric if (Hint.CodeToInsert.empty()) { 1610b57cec5SDimitry Andric if (Hint.InsertFromRange.isValid()) 1620b57cec5SDimitry Andric commit.insertFromRange(Hint.RemoveRange.getBegin(), 1630b57cec5SDimitry Andric Hint.InsertFromRange, /*afterToken=*/false, 1640b57cec5SDimitry Andric Hint.BeforePreviousInsertions); 1650b57cec5SDimitry Andric else 1660b57cec5SDimitry Andric commit.remove(Hint.RemoveRange); 1670b57cec5SDimitry Andric } else { 1680b57cec5SDimitry Andric if (Hint.RemoveRange.isTokenRange() || 1690b57cec5SDimitry Andric Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd()) 1700b57cec5SDimitry Andric commit.replace(Hint.RemoveRange, Hint.CodeToInsert); 1710b57cec5SDimitry Andric else 1720b57cec5SDimitry Andric commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert, 1730b57cec5SDimitry Andric /*afterToken=*/false, Hint.BeforePreviousInsertions); 1740b57cec5SDimitry Andric } 1750b57cec5SDimitry Andric } 1760b57cec5SDimitry Andric bool CanRewrite = Info.getNumFixItHints() > 0 && commit.isCommitable(); 1770b57cec5SDimitry Andric 1780b57cec5SDimitry Andric if (!CanRewrite) { 1790b57cec5SDimitry Andric if (Info.getNumFixItHints() > 0) 1800b57cec5SDimitry Andric Diag(Info.getLocation(), diag::note_fixit_in_macro); 1810b57cec5SDimitry Andric 1820b57cec5SDimitry Andric // If this was an error, refuse to perform any rewriting. 1830b57cec5SDimitry Andric if (DiagLevel >= DiagnosticsEngine::Error) { 1840b57cec5SDimitry Andric if (++NumFailures == 1) 1850b57cec5SDimitry Andric Diag(Info.getLocation(), diag::note_fixit_unfixed_error); 1860b57cec5SDimitry Andric } 1870b57cec5SDimitry Andric return; 1880b57cec5SDimitry Andric } 1890b57cec5SDimitry Andric 1900b57cec5SDimitry Andric if (!Editor.commit(commit)) { 1910b57cec5SDimitry Andric ++NumFailures; 1920b57cec5SDimitry Andric Diag(Info.getLocation(), diag::note_fixit_failed); 1930b57cec5SDimitry Andric return; 1940b57cec5SDimitry Andric } 1950b57cec5SDimitry Andric 1960b57cec5SDimitry Andric Diag(Info.getLocation(), diag::note_fixit_applied); 1970b57cec5SDimitry Andric } 1980b57cec5SDimitry Andric 1990b57cec5SDimitry Andric /// Emit a diagnostic via the adapted diagnostic client. 2000b57cec5SDimitry Andric void FixItRewriter::Diag(SourceLocation Loc, unsigned DiagID) { 2010b57cec5SDimitry Andric // When producing this diagnostic, we temporarily bypass ourselves, 2020b57cec5SDimitry Andric // clear out any current diagnostic, and let the downstream client 2030b57cec5SDimitry Andric // format the diagnostic. 2040b57cec5SDimitry Andric Diags.setClient(Client, false); 2050b57cec5SDimitry Andric Diags.Clear(); 2060b57cec5SDimitry Andric Diags.Report(Loc, DiagID); 2070b57cec5SDimitry Andric Diags.setClient(this, false); 2080b57cec5SDimitry Andric } 2090b57cec5SDimitry Andric 2100b57cec5SDimitry Andric FixItOptions::~FixItOptions() = default; 211