clang API Documentation

FixItRewriter.cpp

Go to the documentation of this file.
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() {}