1274958Sdim//===--- FixItRewriter.cpp - Fix-It Rewriter Diagnostic Client --*- C++ -*-===// 2274958Sdim// 3274958Sdim// The LLVM Compiler Infrastructure 4274958Sdim// 5274958Sdim// This file is distributed under the University of Illinois Open Source 6274958Sdim// License. See LICENSE.TXT for details. 7274958Sdim// 8274958Sdim//===----------------------------------------------------------------------===// 9274958Sdim// 10274958Sdim// This is a diagnostic client adaptor that performs rewrites as 11274958Sdim// suggested by code modification hints attached to diagnostics. It 12274958Sdim// then forwards any diagnostics to the adapted diagnostic client. 13274958Sdim// 14274958Sdim//===----------------------------------------------------------------------===// 15274958Sdim 16274958Sdim#include "clang/Rewrite/Frontend/FixItRewriter.h" 17274958Sdim#include "clang/Basic/FileManager.h" 18274958Sdim#include "clang/Basic/SourceLocation.h" 19274958Sdim#include "clang/Basic/SourceManager.h" 20274958Sdim#include "clang/Edit/Commit.h" 21274958Sdim#include "clang/Edit/EditsReceiver.h" 22274958Sdim#include "clang/Frontend/FrontendDiagnostic.h" 23274958Sdim#include "llvm/Support/Path.h" 24274958Sdim#include "llvm/Support/raw_ostream.h" 25274958Sdim#include <cstdio> 26274958Sdim#include <memory> 27274958Sdim 28274958Sdimusing namespace clang; 29274958Sdim 30274958SdimFixItRewriter::FixItRewriter(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 31274958Sdim const LangOptions &LangOpts, 32274958Sdim FixItOptions *FixItOpts) 33274958Sdim : Diags(Diags), 34274958Sdim Editor(SourceMgr, LangOpts), 35274958Sdim Rewrite(SourceMgr, LangOpts), 36274958Sdim FixItOpts(FixItOpts), 37274958Sdim NumFailures(0), 38274958Sdim PrevDiagSilenced(false) { 39280031Sdim Owner = Diags.takeClient(); 40280031Sdim Client = Diags.getClient(); 41280031Sdim Diags.setClient(this, false); 42274958Sdim} 43274958Sdim 44274958SdimFixItRewriter::~FixItRewriter() { 45280031Sdim Diags.setClient(Client, Owner.release() != nullptr); 46274958Sdim} 47274958Sdim 48274958Sdimbool FixItRewriter::WriteFixedFile(FileID ID, raw_ostream &OS) { 49274958Sdim const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(ID); 50274958Sdim if (!RewriteBuf) return true; 51274958Sdim RewriteBuf->write(OS); 52274958Sdim OS.flush(); 53274958Sdim return false; 54274958Sdim} 55274958Sdim 56274958Sdimnamespace { 57274958Sdim 58274958Sdimclass RewritesReceiver : public edit::EditsReceiver { 59274958Sdim Rewriter &Rewrite; 60274958Sdim 61274958Sdimpublic: 62274958Sdim RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { } 63274958Sdim 64274958Sdim void insert(SourceLocation loc, StringRef text) override { 65274958Sdim Rewrite.InsertText(loc, text); 66274958Sdim } 67274958Sdim void replace(CharSourceRange range, StringRef text) override { 68274958Sdim Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text); 69274958Sdim } 70274958Sdim}; 71274958Sdim 72274958Sdim} 73274958Sdim 74274958Sdimbool FixItRewriter::WriteFixedFiles( 75274958Sdim std::vector<std::pair<std::string, std::string> > *RewrittenFiles) { 76274958Sdim if (NumFailures > 0 && !FixItOpts->FixWhatYouCan) { 77274958Sdim Diag(FullSourceLoc(), diag::warn_fixit_no_changes); 78274958Sdim return true; 79274958Sdim } 80274958Sdim 81274958Sdim RewritesReceiver Rec(Rewrite); 82274958Sdim Editor.applyRewrites(Rec); 83274958Sdim 84288943Sdim if (FixItOpts->InPlace) { 85288943Sdim // Overwriting open files on Windows is tricky, but the rewriter can do it 86288943Sdim // for us. 87288943Sdim Rewrite.overwriteChangedFiles(); 88288943Sdim return false; 89288943Sdim } 90288943Sdim 91274958Sdim for (iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) { 92274958Sdim const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first); 93274958Sdim int fd; 94274958Sdim std::string Filename = FixItOpts->RewriteFilename(Entry->getName(), fd); 95280031Sdim std::error_code EC; 96274958Sdim std::unique_ptr<llvm::raw_fd_ostream> OS; 97274958Sdim if (fd != -1) { 98274958Sdim OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true)); 99274958Sdim } else { 100280031Sdim OS.reset(new llvm::raw_fd_ostream(Filename, EC, llvm::sys::fs::F_None)); 101274958Sdim } 102280031Sdim if (EC) { 103280031Sdim Diags.Report(clang::diag::err_fe_unable_to_open_output) << Filename 104280031Sdim << EC.message(); 105274958Sdim continue; 106274958Sdim } 107274958Sdim RewriteBuffer &RewriteBuf = I->second; 108274958Sdim RewriteBuf.write(*OS); 109274958Sdim OS->flush(); 110274958Sdim 111274958Sdim if (RewrittenFiles) 112274958Sdim RewrittenFiles->push_back(std::make_pair(Entry->getName(), Filename)); 113274958Sdim } 114274958Sdim 115274958Sdim return false; 116274958Sdim} 117274958Sdim 118274958Sdimbool FixItRewriter::IncludeInDiagnosticCounts() const { 119274958Sdim return Client ? Client->IncludeInDiagnosticCounts() : true; 120274958Sdim} 121274958Sdim 122274958Sdimvoid FixItRewriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 123274958Sdim const Diagnostic &Info) { 124274958Sdim // Default implementation (Warnings/errors count). 125274958Sdim DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); 126274958Sdim 127274958Sdim if (!FixItOpts->Silent || 128274958Sdim DiagLevel >= DiagnosticsEngine::Error || 129274958Sdim (DiagLevel == DiagnosticsEngine::Note && !PrevDiagSilenced) || 130274958Sdim (DiagLevel > DiagnosticsEngine::Note && Info.getNumFixItHints())) { 131274958Sdim Client->HandleDiagnostic(DiagLevel, Info); 132274958Sdim PrevDiagSilenced = false; 133274958Sdim } else { 134274958Sdim PrevDiagSilenced = true; 135274958Sdim } 136274958Sdim 137274958Sdim // Skip over any diagnostics that are ignored or notes. 138274958Sdim if (DiagLevel <= DiagnosticsEngine::Note) 139274958Sdim return; 140274958Sdim // Skip over errors if we are only fixing warnings. 141274958Sdim if (DiagLevel >= DiagnosticsEngine::Error && FixItOpts->FixOnlyWarnings) { 142274958Sdim ++NumFailures; 143274958Sdim return; 144274958Sdim } 145274958Sdim 146274958Sdim // Make sure that we can perform all of the modifications we 147274958Sdim // in this diagnostic. 148274958Sdim edit::Commit commit(Editor); 149274958Sdim for (unsigned Idx = 0, Last = Info.getNumFixItHints(); 150274958Sdim Idx < Last; ++Idx) { 151274958Sdim const FixItHint &Hint = Info.getFixItHint(Idx); 152274958Sdim 153274958Sdim if (Hint.CodeToInsert.empty()) { 154274958Sdim if (Hint.InsertFromRange.isValid()) 155274958Sdim commit.insertFromRange(Hint.RemoveRange.getBegin(), 156274958Sdim Hint.InsertFromRange, /*afterToken=*/false, 157274958Sdim Hint.BeforePreviousInsertions); 158274958Sdim else 159274958Sdim commit.remove(Hint.RemoveRange); 160274958Sdim } else { 161274958Sdim if (Hint.RemoveRange.isTokenRange() || 162274958Sdim Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd()) 163274958Sdim commit.replace(Hint.RemoveRange, Hint.CodeToInsert); 164274958Sdim else 165274958Sdim commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert, 166274958Sdim /*afterToken=*/false, Hint.BeforePreviousInsertions); 167274958Sdim } 168274958Sdim } 169274958Sdim bool CanRewrite = Info.getNumFixItHints() > 0 && commit.isCommitable(); 170274958Sdim 171274958Sdim if (!CanRewrite) { 172274958Sdim if (Info.getNumFixItHints() > 0) 173274958Sdim Diag(Info.getLocation(), diag::note_fixit_in_macro); 174274958Sdim 175274958Sdim // If this was an error, refuse to perform any rewriting. 176274958Sdim if (DiagLevel >= DiagnosticsEngine::Error) { 177274958Sdim if (++NumFailures == 1) 178274958Sdim Diag(Info.getLocation(), diag::note_fixit_unfixed_error); 179274958Sdim } 180274958Sdim return; 181274958Sdim } 182274958Sdim 183274958Sdim if (!Editor.commit(commit)) { 184274958Sdim ++NumFailures; 185274958Sdim Diag(Info.getLocation(), diag::note_fixit_failed); 186274958Sdim return; 187274958Sdim } 188274958Sdim 189274958Sdim Diag(Info.getLocation(), diag::note_fixit_applied); 190274958Sdim} 191274958Sdim 192274958Sdim/// \brief Emit a diagnostic via the adapted diagnostic client. 193274958Sdimvoid FixItRewriter::Diag(SourceLocation Loc, unsigned DiagID) { 194274958Sdim // When producing this diagnostic, we temporarily bypass ourselves, 195274958Sdim // clear out any current diagnostic, and let the downstream client 196274958Sdim // format the diagnostic. 197280031Sdim Diags.setClient(Client, false); 198274958Sdim Diags.Clear(); 199274958Sdim Diags.Report(Loc, DiagID); 200280031Sdim Diags.setClient(this, false); 201274958Sdim} 202274958Sdim 203274958SdimFixItOptions::~FixItOptions() {} 204