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