1243791Sdim//===--- FixItRewriter.cpp - Fix-It Rewriter Diagnostic Client --*- C++ -*-===// 2243791Sdim// 3243791Sdim// The LLVM Compiler Infrastructure 4243791Sdim// 5243791Sdim// This file is distributed under the University of Illinois Open Source 6243791Sdim// License. See LICENSE.TXT for details. 7243791Sdim// 8243791Sdim//===----------------------------------------------------------------------===// 9243791Sdim// 10243791Sdim// This is a diagnostic client adaptor that performs rewrites as 11243791Sdim// suggested by code modification hints attached to diagnostics. It 12243791Sdim// then forwards any diagnostics to the adapted diagnostic client. 13243791Sdim// 14243791Sdim//===----------------------------------------------------------------------===// 15243791Sdim 16243791Sdim#include "clang/Rewrite/Frontend/FixItRewriter.h" 17243791Sdim#include "clang/Basic/FileManager.h" 18243791Sdim#include "clang/Basic/SourceLocation.h" 19243791Sdim#include "clang/Basic/SourceManager.h" 20249423Sdim#include "clang/Edit/Commit.h" 21249423Sdim#include "clang/Edit/EditsReceiver.h" 22243791Sdim#include "clang/Frontend/FrontendDiagnostic.h" 23249423Sdim#include "llvm/ADT/OwningPtr.h" 24249423Sdim#include "llvm/Support/Path.h" 25243791Sdim#include "llvm/Support/raw_ostream.h" 26243791Sdim#include <cstdio> 27243791Sdim 28243791Sdimusing namespace clang; 29243791Sdim 30243791SdimFixItRewriter::FixItRewriter(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 31243791Sdim const LangOptions &LangOpts, 32243791Sdim FixItOptions *FixItOpts) 33243791Sdim : Diags(Diags), 34243791Sdim Editor(SourceMgr, LangOpts), 35243791Sdim Rewrite(SourceMgr, LangOpts), 36243791Sdim FixItOpts(FixItOpts), 37243791Sdim NumFailures(0), 38243791Sdim PrevDiagSilenced(false) { 39243791Sdim OwnsClient = Diags.ownsClient(); 40243791Sdim Client = Diags.takeClient(); 41243791Sdim Diags.setClient(this); 42243791Sdim} 43243791Sdim 44243791SdimFixItRewriter::~FixItRewriter() { 45243791Sdim Diags.takeClient(); 46243791Sdim Diags.setClient(Client, OwnsClient); 47243791Sdim} 48243791Sdim 49243791Sdimbool FixItRewriter::WriteFixedFile(FileID ID, raw_ostream &OS) { 50243791Sdim const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(ID); 51243791Sdim if (!RewriteBuf) return true; 52243791Sdim RewriteBuf->write(OS); 53243791Sdim OS.flush(); 54243791Sdim return false; 55243791Sdim} 56243791Sdim 57243791Sdimnamespace { 58243791Sdim 59243791Sdimclass RewritesReceiver : public edit::EditsReceiver { 60243791Sdim Rewriter &Rewrite; 61243791Sdim 62243791Sdimpublic: 63243791Sdim RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { } 64243791Sdim 65243791Sdim virtual void insert(SourceLocation loc, StringRef text) { 66243791Sdim Rewrite.InsertText(loc, text); 67243791Sdim } 68243791Sdim virtual void replace(CharSourceRange range, StringRef text) { 69243791Sdim Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text); 70243791Sdim } 71243791Sdim}; 72243791Sdim 73243791Sdim} 74243791Sdim 75243791Sdimbool FixItRewriter::WriteFixedFiles( 76243791Sdim std::vector<std::pair<std::string, std::string> > *RewrittenFiles) { 77243791Sdim if (NumFailures > 0 && !FixItOpts->FixWhatYouCan) { 78243791Sdim Diag(FullSourceLoc(), diag::warn_fixit_no_changes); 79243791Sdim return true; 80243791Sdim } 81243791Sdim 82243791Sdim RewritesReceiver Rec(Rewrite); 83243791Sdim Editor.applyRewrites(Rec); 84243791Sdim 85243791Sdim for (iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) { 86243791Sdim const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first); 87243791Sdim int fd; 88243791Sdim std::string Filename = FixItOpts->RewriteFilename(Entry->getName(), fd); 89243791Sdim std::string Err; 90243791Sdim OwningPtr<llvm::raw_fd_ostream> OS; 91243791Sdim if (fd != -1) { 92243791Sdim OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true)); 93243791Sdim } else { 94243791Sdim OS.reset(new llvm::raw_fd_ostream(Filename.c_str(), Err, 95243791Sdim llvm::raw_fd_ostream::F_Binary)); 96243791Sdim } 97243791Sdim if (!Err.empty()) { 98243791Sdim Diags.Report(clang::diag::err_fe_unable_to_open_output) 99243791Sdim << Filename << Err; 100243791Sdim continue; 101243791Sdim } 102243791Sdim RewriteBuffer &RewriteBuf = I->second; 103243791Sdim RewriteBuf.write(*OS); 104243791Sdim OS->flush(); 105243791Sdim 106243791Sdim if (RewrittenFiles) 107243791Sdim RewrittenFiles->push_back(std::make_pair(Entry->getName(), Filename)); 108243791Sdim } 109243791Sdim 110243791Sdim return false; 111243791Sdim} 112243791Sdim 113243791Sdimbool FixItRewriter::IncludeInDiagnosticCounts() const { 114243791Sdim return Client ? Client->IncludeInDiagnosticCounts() : true; 115243791Sdim} 116243791Sdim 117243791Sdimvoid FixItRewriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 118243791Sdim const Diagnostic &Info) { 119243791Sdim // Default implementation (Warnings/errors count). 120243791Sdim DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); 121243791Sdim 122243791Sdim if (!FixItOpts->Silent || 123243791Sdim DiagLevel >= DiagnosticsEngine::Error || 124243791Sdim (DiagLevel == DiagnosticsEngine::Note && !PrevDiagSilenced) || 125243791Sdim (DiagLevel > DiagnosticsEngine::Note && Info.getNumFixItHints())) { 126243791Sdim Client->HandleDiagnostic(DiagLevel, Info); 127243791Sdim PrevDiagSilenced = false; 128243791Sdim } else { 129243791Sdim PrevDiagSilenced = true; 130243791Sdim } 131243791Sdim 132243791Sdim // Skip over any diagnostics that are ignored or notes. 133243791Sdim if (DiagLevel <= DiagnosticsEngine::Note) 134243791Sdim return; 135243791Sdim // Skip over errors if we are only fixing warnings. 136243791Sdim if (DiagLevel >= DiagnosticsEngine::Error && FixItOpts->FixOnlyWarnings) { 137243791Sdim ++NumFailures; 138243791Sdim return; 139243791Sdim } 140243791Sdim 141243791Sdim // Make sure that we can perform all of the modifications we 142243791Sdim // in this diagnostic. 143243791Sdim edit::Commit commit(Editor); 144243791Sdim for (unsigned Idx = 0, Last = Info.getNumFixItHints(); 145243791Sdim Idx < Last; ++Idx) { 146243791Sdim const FixItHint &Hint = Info.getFixItHint(Idx); 147243791Sdim 148243791Sdim if (Hint.CodeToInsert.empty()) { 149243791Sdim if (Hint.InsertFromRange.isValid()) 150243791Sdim commit.insertFromRange(Hint.RemoveRange.getBegin(), 151243791Sdim Hint.InsertFromRange, /*afterToken=*/false, 152243791Sdim Hint.BeforePreviousInsertions); 153243791Sdim else 154243791Sdim commit.remove(Hint.RemoveRange); 155243791Sdim } else { 156243791Sdim if (Hint.RemoveRange.isTokenRange() || 157243791Sdim Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd()) 158243791Sdim commit.replace(Hint.RemoveRange, Hint.CodeToInsert); 159243791Sdim else 160243791Sdim commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert, 161243791Sdim /*afterToken=*/false, Hint.BeforePreviousInsertions); 162243791Sdim } 163243791Sdim } 164243791Sdim bool CanRewrite = Info.getNumFixItHints() > 0 && commit.isCommitable(); 165243791Sdim 166243791Sdim if (!CanRewrite) { 167243791Sdim if (Info.getNumFixItHints() > 0) 168243791Sdim Diag(Info.getLocation(), diag::note_fixit_in_macro); 169243791Sdim 170243791Sdim // If this was an error, refuse to perform any rewriting. 171243791Sdim if (DiagLevel >= DiagnosticsEngine::Error) { 172243791Sdim if (++NumFailures == 1) 173243791Sdim Diag(Info.getLocation(), diag::note_fixit_unfixed_error); 174243791Sdim } 175243791Sdim return; 176243791Sdim } 177243791Sdim 178243791Sdim if (!Editor.commit(commit)) { 179243791Sdim ++NumFailures; 180243791Sdim Diag(Info.getLocation(), diag::note_fixit_failed); 181243791Sdim return; 182243791Sdim } 183243791Sdim 184243791Sdim Diag(Info.getLocation(), diag::note_fixit_applied); 185243791Sdim} 186243791Sdim 187243791Sdim/// \brief Emit a diagnostic via the adapted diagnostic client. 188243791Sdimvoid FixItRewriter::Diag(SourceLocation Loc, unsigned DiagID) { 189243791Sdim // When producing this diagnostic, we temporarily bypass ourselves, 190243791Sdim // clear out any current diagnostic, and let the downstream client 191243791Sdim // format the diagnostic. 192243791Sdim Diags.takeClient(); 193243791Sdim Diags.setClient(Client); 194243791Sdim Diags.Clear(); 195243791Sdim Diags.Report(Loc, DiagID); 196243791Sdim Diags.takeClient(); 197243791Sdim Diags.setClient(this); 198243791Sdim} 199243791Sdim 200243791SdimFixItOptions::~FixItOptions() {} 201