1274958Sdim//===--- FixItRewriter.cpp - Fix-It Rewriter Diagnostic Client --*- C++ -*-===//
2274958Sdim//
3274958Sdim//                     The LLVM Compiler Infrastructure
4274958Sdim//
5274958Sdim// This file is distributed under the University of Illinois Open Source
6274958Sdim// License. See LICENSE.TXT for details.
7274958Sdim//
8274958Sdim//===----------------------------------------------------------------------===//
9274958Sdim//
10274958Sdim// This is a diagnostic client adaptor that performs rewrites as
11274958Sdim// suggested by code modification hints attached to diagnostics. It
12274958Sdim// then forwards any diagnostics to the adapted diagnostic client.
13274958Sdim//
14274958Sdim//===----------------------------------------------------------------------===//
15274958Sdim
16274958Sdim#include "clang/Rewrite/Frontend/FixItRewriter.h"
17274958Sdim#include "clang/Basic/FileManager.h"
18274958Sdim#include "clang/Basic/SourceLocation.h"
19274958Sdim#include "clang/Basic/SourceManager.h"
20274958Sdim#include "clang/Edit/Commit.h"
21274958Sdim#include "clang/Edit/EditsReceiver.h"
22274958Sdim#include "clang/Frontend/FrontendDiagnostic.h"
23274958Sdim#include "llvm/Support/Path.h"
24274958Sdim#include "llvm/Support/raw_ostream.h"
25274958Sdim#include <cstdio>
26274958Sdim#include <memory>
27274958Sdim
28274958Sdimusing namespace clang;
29274958Sdim
30274958SdimFixItRewriter::FixItRewriter(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
31274958Sdim                             const LangOptions &LangOpts,
32274958Sdim                             FixItOptions *FixItOpts)
33274958Sdim  : Diags(Diags),
34274958Sdim    Editor(SourceMgr, LangOpts),
35274958Sdim    Rewrite(SourceMgr, LangOpts),
36274958Sdim    FixItOpts(FixItOpts),
37274958Sdim    NumFailures(0),
38274958Sdim    PrevDiagSilenced(false) {
39280031Sdim  Owner = Diags.takeClient();
40280031Sdim  Client = Diags.getClient();
41280031Sdim  Diags.setClient(this, false);
42274958Sdim}
43274958Sdim
44274958SdimFixItRewriter::~FixItRewriter() {
45280031Sdim  Diags.setClient(Client, Owner.release() != nullptr);
46274958Sdim}
47274958Sdim
48274958Sdimbool FixItRewriter::WriteFixedFile(FileID ID, raw_ostream &OS) {
49274958Sdim  const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(ID);
50274958Sdim  if (!RewriteBuf) return true;
51274958Sdim  RewriteBuf->write(OS);
52274958Sdim  OS.flush();
53274958Sdim  return false;
54274958Sdim}
55274958Sdim
56274958Sdimnamespace {
57274958Sdim
58274958Sdimclass RewritesReceiver : public edit::EditsReceiver {
59274958Sdim  Rewriter &Rewrite;
60274958Sdim
61274958Sdimpublic:
62274958Sdim  RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
63274958Sdim
64274958Sdim  void insert(SourceLocation loc, StringRef text) override {
65274958Sdim    Rewrite.InsertText(loc, text);
66274958Sdim  }
67274958Sdim  void replace(CharSourceRange range, StringRef text) override {
68274958Sdim    Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
69274958Sdim  }
70274958Sdim};
71274958Sdim
72274958Sdim}
73274958Sdim
74274958Sdimbool FixItRewriter::WriteFixedFiles(
75274958Sdim            std::vector<std::pair<std::string, std::string> > *RewrittenFiles) {
76274958Sdim  if (NumFailures > 0 && !FixItOpts->FixWhatYouCan) {
77274958Sdim    Diag(FullSourceLoc(), diag::warn_fixit_no_changes);
78274958Sdim    return true;
79274958Sdim  }
80274958Sdim
81274958Sdim  RewritesReceiver Rec(Rewrite);
82274958Sdim  Editor.applyRewrites(Rec);
83274958Sdim
84288943Sdim  if (FixItOpts->InPlace) {
85288943Sdim    // Overwriting open files on Windows is tricky, but the rewriter can do it
86288943Sdim    // for us.
87288943Sdim    Rewrite.overwriteChangedFiles();
88288943Sdim    return false;
89288943Sdim  }
90288943Sdim
91274958Sdim  for (iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) {
92274958Sdim    const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first);
93274958Sdim    int fd;
94274958Sdim    std::string Filename = FixItOpts->RewriteFilename(Entry->getName(), fd);
95280031Sdim    std::error_code EC;
96274958Sdim    std::unique_ptr<llvm::raw_fd_ostream> OS;
97274958Sdim    if (fd != -1) {
98274958Sdim      OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true));
99274958Sdim    } else {
100280031Sdim      OS.reset(new llvm::raw_fd_ostream(Filename, EC, llvm::sys::fs::F_None));
101274958Sdim    }
102280031Sdim    if (EC) {
103280031Sdim      Diags.Report(clang::diag::err_fe_unable_to_open_output) << Filename
104280031Sdim                                                              << EC.message();
105274958Sdim      continue;
106274958Sdim    }
107274958Sdim    RewriteBuffer &RewriteBuf = I->second;
108274958Sdim    RewriteBuf.write(*OS);
109274958Sdim    OS->flush();
110274958Sdim
111274958Sdim    if (RewrittenFiles)
112274958Sdim      RewrittenFiles->push_back(std::make_pair(Entry->getName(), Filename));
113274958Sdim  }
114274958Sdim
115274958Sdim  return false;
116274958Sdim}
117274958Sdim
118274958Sdimbool FixItRewriter::IncludeInDiagnosticCounts() const {
119274958Sdim  return Client ? Client->IncludeInDiagnosticCounts() : true;
120274958Sdim}
121274958Sdim
122274958Sdimvoid FixItRewriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
123274958Sdim                                     const Diagnostic &Info) {
124274958Sdim  // Default implementation (Warnings/errors count).
125274958Sdim  DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
126274958Sdim
127274958Sdim  if (!FixItOpts->Silent ||
128274958Sdim      DiagLevel >= DiagnosticsEngine::Error ||
129274958Sdim      (DiagLevel == DiagnosticsEngine::Note && !PrevDiagSilenced) ||
130274958Sdim      (DiagLevel > DiagnosticsEngine::Note && Info.getNumFixItHints())) {
131274958Sdim    Client->HandleDiagnostic(DiagLevel, Info);
132274958Sdim    PrevDiagSilenced = false;
133274958Sdim  } else {
134274958Sdim    PrevDiagSilenced = true;
135274958Sdim  }
136274958Sdim
137274958Sdim  // Skip over any diagnostics that are ignored or notes.
138274958Sdim  if (DiagLevel <= DiagnosticsEngine::Note)
139274958Sdim    return;
140274958Sdim  // Skip over errors if we are only fixing warnings.
141274958Sdim  if (DiagLevel >= DiagnosticsEngine::Error && FixItOpts->FixOnlyWarnings) {
142274958Sdim    ++NumFailures;
143274958Sdim    return;
144274958Sdim  }
145274958Sdim
146274958Sdim  // Make sure that we can perform all of the modifications we
147274958Sdim  // in this diagnostic.
148274958Sdim  edit::Commit commit(Editor);
149274958Sdim  for (unsigned Idx = 0, Last = Info.getNumFixItHints();
150274958Sdim       Idx < Last; ++Idx) {
151274958Sdim    const FixItHint &Hint = Info.getFixItHint(Idx);
152274958Sdim
153274958Sdim    if (Hint.CodeToInsert.empty()) {
154274958Sdim      if (Hint.InsertFromRange.isValid())
155274958Sdim        commit.insertFromRange(Hint.RemoveRange.getBegin(),
156274958Sdim                           Hint.InsertFromRange, /*afterToken=*/false,
157274958Sdim                           Hint.BeforePreviousInsertions);
158274958Sdim      else
159274958Sdim        commit.remove(Hint.RemoveRange);
160274958Sdim    } else {
161274958Sdim      if (Hint.RemoveRange.isTokenRange() ||
162274958Sdim          Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd())
163274958Sdim        commit.replace(Hint.RemoveRange, Hint.CodeToInsert);
164274958Sdim      else
165274958Sdim        commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert,
166274958Sdim                    /*afterToken=*/false, Hint.BeforePreviousInsertions);
167274958Sdim    }
168274958Sdim  }
169274958Sdim  bool CanRewrite = Info.getNumFixItHints() > 0 && commit.isCommitable();
170274958Sdim
171274958Sdim  if (!CanRewrite) {
172274958Sdim    if (Info.getNumFixItHints() > 0)
173274958Sdim      Diag(Info.getLocation(), diag::note_fixit_in_macro);
174274958Sdim
175274958Sdim    // If this was an error, refuse to perform any rewriting.
176274958Sdim    if (DiagLevel >= DiagnosticsEngine::Error) {
177274958Sdim      if (++NumFailures == 1)
178274958Sdim        Diag(Info.getLocation(), diag::note_fixit_unfixed_error);
179274958Sdim    }
180274958Sdim    return;
181274958Sdim  }
182274958Sdim
183274958Sdim  if (!Editor.commit(commit)) {
184274958Sdim    ++NumFailures;
185274958Sdim    Diag(Info.getLocation(), diag::note_fixit_failed);
186274958Sdim    return;
187274958Sdim  }
188274958Sdim
189274958Sdim  Diag(Info.getLocation(), diag::note_fixit_applied);
190274958Sdim}
191274958Sdim
192274958Sdim/// \brief Emit a diagnostic via the adapted diagnostic client.
193274958Sdimvoid FixItRewriter::Diag(SourceLocation Loc, unsigned DiagID) {
194274958Sdim  // When producing this diagnostic, we temporarily bypass ourselves,
195274958Sdim  // clear out any current diagnostic, and let the downstream client
196274958Sdim  // format the diagnostic.
197280031Sdim  Diags.setClient(Client, false);
198274958Sdim  Diags.Clear();
199274958Sdim  Diags.Report(Loc, DiagID);
200280031Sdim  Diags.setClient(this, false);
201274958Sdim}
202274958Sdim
203274958SdimFixItOptions::~FixItOptions() {}
204