clang API Documentation
00001 //===--- FixItRewriter.cpp - Fix-It Rewriter Diagnostic Client --*- C++ -*-===// 00002 // 00003 // The LLVM Compiler Infrastructure 00004 // 00005 // This file is distributed under the University of Illinois Open Source 00006 // License. See LICENSE.TXT for details. 00007 // 00008 //===----------------------------------------------------------------------===// 00009 // 00010 // This is a diagnostic client adaptor that performs rewrites as 00011 // suggested by code modification hints attached to diagnostics. It 00012 // then forwards any diagnostics to the adapted diagnostic client. 00013 // 00014 //===----------------------------------------------------------------------===// 00015 00016 #include "clang/Frontend/FixItRewriter.h" 00017 #include "clang/Basic/FileManager.h" 00018 #include "clang/Basic/SourceLocation.h" 00019 #include "clang/Basic/SourceManager.h" 00020 #include "clang/Frontend/FrontendDiagnostic.h" 00021 #include "llvm/Support/raw_ostream.h" 00022 #include "llvm/System/Path.h" 00023 #include "llvm/ADT/OwningPtr.h" 00024 #include <cstdio> 00025 00026 using namespace clang; 00027 00028 FixItRewriter::FixItRewriter(Diagnostic &Diags, SourceManager &SourceMgr, 00029 const LangOptions &LangOpts, 00030 FixItPathRewriter *PathRewriter) 00031 : Diags(Diags), 00032 Rewrite(SourceMgr, LangOpts), 00033 PathRewriter(PathRewriter), 00034 NumFailures(0) { 00035 Client = Diags.getClient(); 00036 Diags.setClient(this); 00037 } 00038 00039 FixItRewriter::~FixItRewriter() { 00040 Diags.setClient(Client); 00041 } 00042 00043 bool FixItRewriter::WriteFixedFile(FileID ID, llvm::raw_ostream &OS) { 00044 const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(ID); 00045 if (!RewriteBuf) return true; 00046 RewriteBuf->write(OS); 00047 OS.flush(); 00048 return false; 00049 } 00050 00051 bool FixItRewriter::WriteFixedFiles() { 00052 if (NumFailures > 0) { 00053 Diag(FullSourceLoc(), diag::warn_fixit_no_changes); 00054 return true; 00055 } 00056 00057 for (iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) { 00058 const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first); 00059 std::string Filename = Entry->getName(); 00060 if (PathRewriter) 00061 Filename = PathRewriter->RewriteFilename(Filename); 00062 std::string Err; 00063 llvm::raw_fd_ostream OS(Filename.c_str(), Err, 00064 llvm::raw_fd_ostream::F_Binary); 00065 if (!Err.empty()) { 00066 Diags.Report(clang::diag::err_fe_unable_to_open_output) 00067 << Filename << Err; 00068 continue; 00069 } 00070 RewriteBuffer &RewriteBuf = I->second; 00071 RewriteBuf.write(OS); 00072 OS.flush(); 00073 } 00074 00075 return false; 00076 } 00077 00078 bool FixItRewriter::IncludeInDiagnosticCounts() const { 00079 return Client ? Client->IncludeInDiagnosticCounts() : true; 00080 } 00081 00082 void FixItRewriter::HandleDiagnostic(Diagnostic::Level DiagLevel, 00083 const DiagnosticInfo &Info) { 00084 Client->HandleDiagnostic(DiagLevel, Info); 00085 00086 // Skip over any diagnostics that are ignored or notes. 00087 if (DiagLevel <= Diagnostic::Note) 00088 return; 00089 00090 // Make sure that we can perform all of the modifications we 00091 // in this diagnostic. 00092 bool CanRewrite = Info.getNumFixItHints() > 0; 00093 for (unsigned Idx = 0, Last = Info.getNumFixItHints(); 00094 Idx < Last; ++Idx) { 00095 const FixItHint &Hint = Info.getFixItHint(Idx); 00096 if (Hint.RemoveRange.isValid() && 00097 Rewrite.getRangeSize(Hint.RemoveRange) == -1) { 00098 CanRewrite = false; 00099 break; 00100 } 00101 00102 if (Hint.InsertionLoc.isValid() && 00103 !Rewrite.isRewritable(Hint.InsertionLoc)) { 00104 CanRewrite = false; 00105 break; 00106 } 00107 } 00108 00109 if (!CanRewrite) { 00110 if (Info.getNumFixItHints() > 0) 00111 Diag(Info.getLocation(), diag::note_fixit_in_macro); 00112 00113 // If this was an error, refuse to perform any rewriting. 00114 if (DiagLevel == Diagnostic::Error || DiagLevel == Diagnostic::Fatal) { 00115 if (++NumFailures == 1) 00116 Diag(Info.getLocation(), diag::note_fixit_unfixed_error); 00117 } 00118 return; 00119 } 00120 00121 bool Failed = false; 00122 for (unsigned Idx = 0, Last = Info.getNumFixItHints(); 00123 Idx < Last; ++Idx) { 00124 const FixItHint &Hint = Info.getFixItHint(Idx); 00125 if (!Hint.RemoveRange.isValid()) { 00126 // We're adding code. 00127 if (Rewrite.InsertTextBefore(Hint.InsertionLoc, Hint.CodeToInsert)) 00128 Failed = true; 00129 continue; 00130 } 00131 00132 if (Hint.CodeToInsert.empty()) { 00133 // We're removing code. 00134 if (Rewrite.RemoveText(Hint.RemoveRange.getBegin(), 00135 Rewrite.getRangeSize(Hint.RemoveRange))) 00136 Failed = true; 00137 continue; 00138 } 00139 00140 // We're replacing code. 00141 if (Rewrite.ReplaceText(Hint.RemoveRange.getBegin(), 00142 Rewrite.getRangeSize(Hint.RemoveRange), 00143 Hint.CodeToInsert)) 00144 Failed = true; 00145 } 00146 00147 if (Failed) { 00148 ++NumFailures; 00149 Diag(Info.getLocation(), diag::note_fixit_failed); 00150 return; 00151 } 00152 00153 Diag(Info.getLocation(), diag::note_fixit_applied); 00154 } 00155 00156 /// \brief Emit a diagnostic via the adapted diagnostic client. 00157 void FixItRewriter::Diag(FullSourceLoc Loc, unsigned DiagID) { 00158 // When producing this diagnostic, we temporarily bypass ourselves, 00159 // clear out any current diagnostic, and let the downstream client 00160 // format the diagnostic. 00161 Diags.setClient(Client); 00162 Diags.Clear(); 00163 Diags.Report(Loc, DiagID); 00164 Diags.setClient(this); 00165 } 00166 00167 FixItPathRewriter::~FixItPathRewriter() {}