1 //===--- FixItRewriter.cpp - Fix-It Rewriter Diagnostic Client --*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This is a diagnostic client adaptor that performs rewrites as 11 // suggested by code modification hints attached to diagnostics. It 12 // then forwards any diagnostics to the adapted diagnostic client. 13 // 14 //===----------------------------------------------------------------------===// 15 16 #include "clang/Rewrite/FixItRewriter.h" 17 #include "clang/Basic/FileManager.h" 18 #include "clang/Basic/SourceLocation.h" 19 #include "clang/Basic/SourceManager.h" 20 #include "clang/Frontend/FrontendDiagnostic.h" 21 #include "llvm/Support/raw_ostream.h" 22 #include "llvm/Support/Path.h" 23 #include "llvm/ADT/OwningPtr.h" 24 #include <cstdio> 25 26 using namespace clang; 27 28 FixItRewriter::FixItRewriter(Diagnostic &Diags, SourceManager &SourceMgr, 29 const LangOptions &LangOpts, 30 FixItOptions *FixItOpts) 31 : Diags(Diags), 32 Rewrite(SourceMgr, LangOpts), 33 FixItOpts(FixItOpts), 34 NumFailures(0) { 35 Client = Diags.takeClient(); 36 Diags.setClient(this); 37 } 38 39 FixItRewriter::~FixItRewriter() { 40 Diags.takeClient(); 41 Diags.setClient(Client); 42 } 43 44 bool FixItRewriter::WriteFixedFile(FileID ID, llvm::raw_ostream &OS) { 45 const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(ID); 46 if (!RewriteBuf) return true; 47 RewriteBuf->write(OS); 48 OS.flush(); 49 return false; 50 } 51 52 bool FixItRewriter::WriteFixedFiles() { 53 if (NumFailures > 0 && !FixItOpts->FixWhatYouCan) { 54 Diag(FullSourceLoc(), diag::warn_fixit_no_changes); 55 return true; 56 } 57 58 for (iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) { 59 const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first); 60 std::string Filename = FixItOpts->RewriteFilename(Entry->getName()); 61 std::string Err; 62 llvm::raw_fd_ostream OS(Filename.c_str(), Err, 63 llvm::raw_fd_ostream::F_Binary); 64 if (!Err.empty()) { 65 Diags.Report(clang::diag::err_fe_unable_to_open_output) 66 << Filename << Err; 67 continue; 68 } 69 RewriteBuffer &RewriteBuf = I->second; 70 RewriteBuf.write(OS); 71 OS.flush(); 72 } 73 74 return false; 75 } 76 77 bool FixItRewriter::IncludeInDiagnosticCounts() const { 78 return Client ? Client->IncludeInDiagnosticCounts() : true; 79 } 80 81 void FixItRewriter::HandleDiagnostic(Diagnostic::Level DiagLevel, 82 const DiagnosticInfo &Info) { 83 // Default implementation (Warnings/errors count). 84 DiagnosticClient::HandleDiagnostic(DiagLevel, Info); 85 86 Client->HandleDiagnostic(DiagLevel, Info); 87 88 // Skip over any diagnostics that are ignored or notes. 89 if (DiagLevel <= Diagnostic::Note) 90 return; 91 92 // Make sure that we can perform all of the modifications we 93 // in this diagnostic. 94 bool CanRewrite = Info.getNumFixItHints() > 0; 95 for (unsigned Idx = 0, Last = Info.getNumFixItHints(); 96 Idx < Last; ++Idx) { 97 const FixItHint &Hint = Info.getFixItHint(Idx); 98 if (Hint.RemoveRange.isValid() && 99 Rewrite.getRangeSize(Hint.RemoveRange) == -1) { 100 CanRewrite = false; 101 break; 102 } 103 } 104 105 if (!CanRewrite) { 106 if (Info.getNumFixItHints() > 0) 107 Diag(Info.getLocation(), diag::note_fixit_in_macro); 108 109 // If this was an error, refuse to perform any rewriting. 110 if (DiagLevel == Diagnostic::Error || DiagLevel == Diagnostic::Fatal) { 111 if (++NumFailures == 1) 112 Diag(Info.getLocation(), diag::note_fixit_unfixed_error); 113 } 114 return; 115 } 116 117 bool Failed = false; 118 for (unsigned Idx = 0, Last = Info.getNumFixItHints(); 119 Idx < Last; ++Idx) { 120 const FixItHint &Hint = Info.getFixItHint(Idx); 121 122 if (Hint.CodeToInsert.empty()) { 123 // We're removing code. 124 if (Rewrite.RemoveText(Hint.RemoveRange)) 125 Failed = true; 126 continue; 127 } 128 129 // We're replacing code. 130 if (Rewrite.ReplaceText(Hint.RemoveRange.getBegin(), 131 Rewrite.getRangeSize(Hint.RemoveRange), 132 Hint.CodeToInsert)) 133 Failed = true; 134 } 135 136 if (Failed) { 137 ++NumFailures; 138 Diag(Info.getLocation(), diag::note_fixit_failed); 139 return; 140 } 141 142 Diag(Info.getLocation(), diag::note_fixit_applied); 143 } 144 145 /// \brief Emit a diagnostic via the adapted diagnostic client. 146 void FixItRewriter::Diag(SourceLocation Loc, unsigned DiagID) { 147 // When producing this diagnostic, we temporarily bypass ourselves, 148 // clear out any current diagnostic, and let the downstream client 149 // format the diagnostic. 150 Diags.takeClient(); 151 Diags.setClient(Client); 152 Diags.Clear(); 153 Diags.Report(Loc, DiagID); 154 Diags.takeClient(); 155 Diags.setClient(this); 156 } 157 158 FixItOptions::~FixItOptions() {} 159