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/Rewrite/FixItRewriter.h" 00017 #include "clang/Edit/Commit.h" 00018 #include "clang/Edit/EditsReceiver.h" 00019 #include "clang/Basic/FileManager.h" 00020 #include "clang/Basic/SourceLocation.h" 00021 #include "clang/Basic/SourceManager.h" 00022 #include "clang/Frontend/FrontendDiagnostic.h" 00023 #include "llvm/Support/raw_ostream.h" 00024 #include "llvm/Support/Path.h" 00025 #include "llvm/ADT/OwningPtr.h" 00026 #include <cstdio> 00027 00028 using namespace clang; 00029 00030 FixItRewriter::FixItRewriter(DiagnosticsEngine &Diags, SourceManager &SourceMgr, 00031 const LangOptions &LangOpts, 00032 FixItOptions *FixItOpts) 00033 : Diags(Diags), 00034 Editor(SourceMgr, LangOpts), 00035 Rewrite(SourceMgr, LangOpts), 00036 FixItOpts(FixItOpts), 00037 NumFailures(0), 00038 PrevDiagSilenced(false) { 00039 OwnsClient = Diags.ownsClient(); 00040 Client = Diags.takeClient(); 00041 Diags.setClient(this); 00042 } 00043 00044 FixItRewriter::~FixItRewriter() { 00045 Diags.takeClient(); 00046 Diags.setClient(Client, OwnsClient); 00047 } 00048 00049 bool FixItRewriter::WriteFixedFile(FileID ID, raw_ostream &OS) { 00050 const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(ID); 00051 if (!RewriteBuf) return true; 00052 RewriteBuf->write(OS); 00053 OS.flush(); 00054 return false; 00055 } 00056 00057 namespace { 00058 00059 class RewritesReceiver : public edit::EditsReceiver { 00060 Rewriter &Rewrite; 00061 00062 public: 00063 RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { } 00064 00065 virtual void insert(SourceLocation loc, StringRef text) { 00066 Rewrite.InsertText(loc, text); 00067 } 00068 virtual void replace(CharSourceRange range, StringRef text) { 00069 Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text); 00070 } 00071 }; 00072 00073 } 00074 00075 bool FixItRewriter::WriteFixedFiles( 00076 std::vector<std::pair<std::string, std::string> > *RewrittenFiles) { 00077 if (NumFailures > 0 && !FixItOpts->FixWhatYouCan) { 00078 Diag(FullSourceLoc(), diag::warn_fixit_no_changes); 00079 return true; 00080 } 00081 00082 RewritesReceiver Rec(Rewrite); 00083 Editor.applyRewrites(Rec); 00084 00085 for (iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) { 00086 const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first); 00087 int fd; 00088 std::string Filename = FixItOpts->RewriteFilename(Entry->getName(), fd); 00089 std::string Err; 00090 OwningPtr<llvm::raw_fd_ostream> OS; 00091 if (fd != -1) { 00092 OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true)); 00093 } else { 00094 OS.reset(new llvm::raw_fd_ostream(Filename.c_str(), Err, 00095 llvm::raw_fd_ostream::F_Binary)); 00096 } 00097 if (!Err.empty()) { 00098 Diags.Report(clang::diag::err_fe_unable_to_open_output) 00099 << Filename << Err; 00100 continue; 00101 } 00102 RewriteBuffer &RewriteBuf = I->second; 00103 RewriteBuf.write(*OS); 00104 OS->flush(); 00105 00106 if (RewrittenFiles) 00107 RewrittenFiles->push_back(std::make_pair(Entry->getName(), Filename)); 00108 } 00109 00110 return false; 00111 } 00112 00113 bool FixItRewriter::IncludeInDiagnosticCounts() const { 00114 return Client ? Client->IncludeInDiagnosticCounts() : true; 00115 } 00116 00117 void FixItRewriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, 00118 const Diagnostic &Info) { 00119 // Default implementation (Warnings/errors count). 00120 DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); 00121 00122 if (!FixItOpts->Silent || 00123 DiagLevel >= DiagnosticsEngine::Error || 00124 (DiagLevel == DiagnosticsEngine::Note && !PrevDiagSilenced) || 00125 (DiagLevel > DiagnosticsEngine::Note && Info.getNumFixItHints())) { 00126 Client->HandleDiagnostic(DiagLevel, Info); 00127 PrevDiagSilenced = false; 00128 } else { 00129 PrevDiagSilenced = true; 00130 } 00131 00132 // Skip over any diagnostics that are ignored or notes. 00133 if (DiagLevel <= DiagnosticsEngine::Note) 00134 return; 00135 // Skip over errors if we are only fixing warnings. 00136 if (DiagLevel >= DiagnosticsEngine::Error && FixItOpts->FixOnlyWarnings) { 00137 ++NumFailures; 00138 return; 00139 } 00140 00141 // Make sure that we can perform all of the modifications we 00142 // in this diagnostic. 00143 edit::Commit commit(Editor); 00144 for (unsigned Idx = 0, Last = Info.getNumFixItHints(); 00145 Idx < Last; ++Idx) { 00146 const FixItHint &Hint = Info.getFixItHint(Idx); 00147 00148 if (Hint.CodeToInsert.empty()) { 00149 if (Hint.InsertFromRange.isValid()) 00150 commit.insertFromRange(Hint.RemoveRange.getBegin(), 00151 Hint.InsertFromRange, /*afterToken=*/false, 00152 Hint.BeforePreviousInsertions); 00153 else 00154 commit.remove(Hint.RemoveRange); 00155 } else { 00156 if (Hint.RemoveRange.isTokenRange() || 00157 Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd()) 00158 commit.replace(Hint.RemoveRange, Hint.CodeToInsert); 00159 else 00160 commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert, 00161 /*afterToken=*/false, Hint.BeforePreviousInsertions); 00162 } 00163 } 00164 bool CanRewrite = Info.getNumFixItHints() > 0 && commit.isCommitable(); 00165 00166 if (!CanRewrite) { 00167 if (Info.getNumFixItHints() > 0) 00168 Diag(Info.getLocation(), diag::note_fixit_in_macro); 00169 00170 // If this was an error, refuse to perform any rewriting. 00171 if (DiagLevel >= DiagnosticsEngine::Error) { 00172 if (++NumFailures == 1) 00173 Diag(Info.getLocation(), diag::note_fixit_unfixed_error); 00174 } 00175 return; 00176 } 00177 00178 if (!Editor.commit(commit)) { 00179 ++NumFailures; 00180 Diag(Info.getLocation(), diag::note_fixit_failed); 00181 return; 00182 } 00183 00184 Diag(Info.getLocation(), diag::note_fixit_applied); 00185 } 00186 00187 /// \brief Emit a diagnostic via the adapted diagnostic client. 00188 void FixItRewriter::Diag(SourceLocation Loc, unsigned DiagID) { 00189 // When producing this diagnostic, we temporarily bypass ourselves, 00190 // clear out any current diagnostic, and let the downstream client 00191 // format the diagnostic. 00192 Diags.takeClient(); 00193 Diags.setClient(Client); 00194 Diags.Clear(); 00195 Diags.Report(Loc, DiagID); 00196 Diags.takeClient(); 00197 Diags.setClient(this); 00198 } 00199 00200 DiagnosticConsumer *FixItRewriter::clone(DiagnosticsEngine &Diags) const { 00201 return new FixItRewriter(Diags, Diags.getSourceManager(), 00202 Rewrite.getLangOpts(), FixItOpts); 00203 } 00204 00205 FixItOptions::~FixItOptions() {}