1243791Sdim//===--- FixItRewriter.cpp - Fix-It Rewriter Diagnostic Client --*- C++ -*-===//
2243791Sdim//
3243791Sdim//                     The LLVM Compiler Infrastructure
4243791Sdim//
5243791Sdim// This file is distributed under the University of Illinois Open Source
6243791Sdim// License. See LICENSE.TXT for details.
7243791Sdim//
8243791Sdim//===----------------------------------------------------------------------===//
9243791Sdim//
10243791Sdim// This is a diagnostic client adaptor that performs rewrites as
11243791Sdim// suggested by code modification hints attached to diagnostics. It
12243791Sdim// then forwards any diagnostics to the adapted diagnostic client.
13243791Sdim//
14243791Sdim//===----------------------------------------------------------------------===//
15243791Sdim
16243791Sdim#include "clang/Rewrite/Frontend/FixItRewriter.h"
17243791Sdim#include "clang/Basic/FileManager.h"
18243791Sdim#include "clang/Basic/SourceLocation.h"
19243791Sdim#include "clang/Basic/SourceManager.h"
20249423Sdim#include "clang/Edit/Commit.h"
21249423Sdim#include "clang/Edit/EditsReceiver.h"
22243791Sdim#include "clang/Frontend/FrontendDiagnostic.h"
23249423Sdim#include "llvm/ADT/OwningPtr.h"
24249423Sdim#include "llvm/Support/Path.h"
25243791Sdim#include "llvm/Support/raw_ostream.h"
26243791Sdim#include <cstdio>
27243791Sdim
28243791Sdimusing namespace clang;
29243791Sdim
30243791SdimFixItRewriter::FixItRewriter(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
31243791Sdim                             const LangOptions &LangOpts,
32243791Sdim                             FixItOptions *FixItOpts)
33243791Sdim  : Diags(Diags),
34243791Sdim    Editor(SourceMgr, LangOpts),
35243791Sdim    Rewrite(SourceMgr, LangOpts),
36243791Sdim    FixItOpts(FixItOpts),
37243791Sdim    NumFailures(0),
38243791Sdim    PrevDiagSilenced(false) {
39243791Sdim  OwnsClient = Diags.ownsClient();
40243791Sdim  Client = Diags.takeClient();
41243791Sdim  Diags.setClient(this);
42243791Sdim}
43243791Sdim
44243791SdimFixItRewriter::~FixItRewriter() {
45243791Sdim  Diags.takeClient();
46243791Sdim  Diags.setClient(Client, OwnsClient);
47243791Sdim}
48243791Sdim
49243791Sdimbool FixItRewriter::WriteFixedFile(FileID ID, raw_ostream &OS) {
50243791Sdim  const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(ID);
51243791Sdim  if (!RewriteBuf) return true;
52243791Sdim  RewriteBuf->write(OS);
53243791Sdim  OS.flush();
54243791Sdim  return false;
55243791Sdim}
56243791Sdim
57243791Sdimnamespace {
58243791Sdim
59243791Sdimclass RewritesReceiver : public edit::EditsReceiver {
60243791Sdim  Rewriter &Rewrite;
61243791Sdim
62243791Sdimpublic:
63243791Sdim  RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
64243791Sdim
65243791Sdim  virtual void insert(SourceLocation loc, StringRef text) {
66243791Sdim    Rewrite.InsertText(loc, text);
67243791Sdim  }
68243791Sdim  virtual void replace(CharSourceRange range, StringRef text) {
69243791Sdim    Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
70243791Sdim  }
71243791Sdim};
72243791Sdim
73243791Sdim}
74243791Sdim
75243791Sdimbool FixItRewriter::WriteFixedFiles(
76243791Sdim            std::vector<std::pair<std::string, std::string> > *RewrittenFiles) {
77243791Sdim  if (NumFailures > 0 && !FixItOpts->FixWhatYouCan) {
78243791Sdim    Diag(FullSourceLoc(), diag::warn_fixit_no_changes);
79243791Sdim    return true;
80243791Sdim  }
81243791Sdim
82243791Sdim  RewritesReceiver Rec(Rewrite);
83243791Sdim  Editor.applyRewrites(Rec);
84243791Sdim
85243791Sdim  for (iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) {
86243791Sdim    const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first);
87243791Sdim    int fd;
88243791Sdim    std::string Filename = FixItOpts->RewriteFilename(Entry->getName(), fd);
89243791Sdim    std::string Err;
90243791Sdim    OwningPtr<llvm::raw_fd_ostream> OS;
91243791Sdim    if (fd != -1) {
92243791Sdim      OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true));
93243791Sdim    } else {
94243791Sdim      OS.reset(new llvm::raw_fd_ostream(Filename.c_str(), Err,
95243791Sdim                                        llvm::raw_fd_ostream::F_Binary));
96243791Sdim    }
97243791Sdim    if (!Err.empty()) {
98243791Sdim      Diags.Report(clang::diag::err_fe_unable_to_open_output)
99243791Sdim          << Filename << Err;
100243791Sdim      continue;
101243791Sdim    }
102243791Sdim    RewriteBuffer &RewriteBuf = I->second;
103243791Sdim    RewriteBuf.write(*OS);
104243791Sdim    OS->flush();
105243791Sdim
106243791Sdim    if (RewrittenFiles)
107243791Sdim      RewrittenFiles->push_back(std::make_pair(Entry->getName(), Filename));
108243791Sdim  }
109243791Sdim
110243791Sdim  return false;
111243791Sdim}
112243791Sdim
113243791Sdimbool FixItRewriter::IncludeInDiagnosticCounts() const {
114243791Sdim  return Client ? Client->IncludeInDiagnosticCounts() : true;
115243791Sdim}
116243791Sdim
117243791Sdimvoid FixItRewriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
118243791Sdim                                     const Diagnostic &Info) {
119243791Sdim  // Default implementation (Warnings/errors count).
120243791Sdim  DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
121243791Sdim
122243791Sdim  if (!FixItOpts->Silent ||
123243791Sdim      DiagLevel >= DiagnosticsEngine::Error ||
124243791Sdim      (DiagLevel == DiagnosticsEngine::Note && !PrevDiagSilenced) ||
125243791Sdim      (DiagLevel > DiagnosticsEngine::Note && Info.getNumFixItHints())) {
126243791Sdim    Client->HandleDiagnostic(DiagLevel, Info);
127243791Sdim    PrevDiagSilenced = false;
128243791Sdim  } else {
129243791Sdim    PrevDiagSilenced = true;
130243791Sdim  }
131243791Sdim
132243791Sdim  // Skip over any diagnostics that are ignored or notes.
133243791Sdim  if (DiagLevel <= DiagnosticsEngine::Note)
134243791Sdim    return;
135243791Sdim  // Skip over errors if we are only fixing warnings.
136243791Sdim  if (DiagLevel >= DiagnosticsEngine::Error && FixItOpts->FixOnlyWarnings) {
137243791Sdim    ++NumFailures;
138243791Sdim    return;
139243791Sdim  }
140243791Sdim
141243791Sdim  // Make sure that we can perform all of the modifications we
142243791Sdim  // in this diagnostic.
143243791Sdim  edit::Commit commit(Editor);
144243791Sdim  for (unsigned Idx = 0, Last = Info.getNumFixItHints();
145243791Sdim       Idx < Last; ++Idx) {
146243791Sdim    const FixItHint &Hint = Info.getFixItHint(Idx);
147243791Sdim
148243791Sdim    if (Hint.CodeToInsert.empty()) {
149243791Sdim      if (Hint.InsertFromRange.isValid())
150243791Sdim        commit.insertFromRange(Hint.RemoveRange.getBegin(),
151243791Sdim                           Hint.InsertFromRange, /*afterToken=*/false,
152243791Sdim                           Hint.BeforePreviousInsertions);
153243791Sdim      else
154243791Sdim        commit.remove(Hint.RemoveRange);
155243791Sdim    } else {
156243791Sdim      if (Hint.RemoveRange.isTokenRange() ||
157243791Sdim          Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd())
158243791Sdim        commit.replace(Hint.RemoveRange, Hint.CodeToInsert);
159243791Sdim      else
160243791Sdim        commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert,
161243791Sdim                    /*afterToken=*/false, Hint.BeforePreviousInsertions);
162243791Sdim    }
163243791Sdim  }
164243791Sdim  bool CanRewrite = Info.getNumFixItHints() > 0 && commit.isCommitable();
165243791Sdim
166243791Sdim  if (!CanRewrite) {
167243791Sdim    if (Info.getNumFixItHints() > 0)
168243791Sdim      Diag(Info.getLocation(), diag::note_fixit_in_macro);
169243791Sdim
170243791Sdim    // If this was an error, refuse to perform any rewriting.
171243791Sdim    if (DiagLevel >= DiagnosticsEngine::Error) {
172243791Sdim      if (++NumFailures == 1)
173243791Sdim        Diag(Info.getLocation(), diag::note_fixit_unfixed_error);
174243791Sdim    }
175243791Sdim    return;
176243791Sdim  }
177243791Sdim
178243791Sdim  if (!Editor.commit(commit)) {
179243791Sdim    ++NumFailures;
180243791Sdim    Diag(Info.getLocation(), diag::note_fixit_failed);
181243791Sdim    return;
182243791Sdim  }
183243791Sdim
184243791Sdim  Diag(Info.getLocation(), diag::note_fixit_applied);
185243791Sdim}
186243791Sdim
187243791Sdim/// \brief Emit a diagnostic via the adapted diagnostic client.
188243791Sdimvoid FixItRewriter::Diag(SourceLocation Loc, unsigned DiagID) {
189243791Sdim  // When producing this diagnostic, we temporarily bypass ourselves,
190243791Sdim  // clear out any current diagnostic, and let the downstream client
191243791Sdim  // format the diagnostic.
192243791Sdim  Diags.takeClient();
193243791Sdim  Diags.setClient(Client);
194243791Sdim  Diags.Clear();
195243791Sdim  Diags.Report(Loc, DiagID);
196243791Sdim  Diags.takeClient();
197243791Sdim  Diags.setClient(this);
198243791Sdim}
199243791Sdim
200243791SdimFixItOptions::~FixItOptions() {}
201