1558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor//===--- FixItRewriter.cpp - Fix-It Rewriter Diagnostic Client --*- C++ -*-===//
2558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor//
3558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor//                     The LLVM Compiler Infrastructure
4558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor//
5558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor// This file is distributed under the University of Illinois Open Source
6558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor// License. See LICENSE.TXT for details.
7558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor//
8558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor//===----------------------------------------------------------------------===//
9558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor//
10558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor// This is a diagnostic client adaptor that performs rewrites as
11558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor// suggested by code modification hints attached to diagnostics. It
12558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor// then forwards any diagnostics to the adapted diagnostic client.
13558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor//
14558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor//===----------------------------------------------------------------------===//
15837a406c6666ebe4dc7b75f7848a7c0cc523e307Douglas Gregor
16305c613af6cfc40e519c75d9d2c84c6fa9a841c0Ted Kremenek#include "clang/Rewrite/Frontend/FixItRewriter.h"
17d4a97a18ea3cda3ba095e7c0c6708e7a39cf31dbNick Lewycky#include "clang/Basic/FileManager.h"
18d4a97a18ea3cda3ba095e7c0c6708e7a39cf31dbNick Lewycky#include "clang/Basic/SourceLocation.h"
19837a406c6666ebe4dc7b75f7848a7c0cc523e307Douglas Gregor#include "clang/Basic/SourceManager.h"
2055fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruth#include "clang/Edit/Commit.h"
2155fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruth#include "clang/Edit/EditsReceiver.h"
22de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor#include "clang/Frontend/FrontendDiagnostic.h"
236cb7c1a43b0c8f739d1f54b7fdae5ede86033496Benjamin Kramer#include "llvm/ADT/OwningPtr.h"
2455fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruth#include "llvm/Support/Path.h"
2555fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruth#include "llvm/Support/raw_ostream.h"
26f42e4a6e089e8413247400fe58ad299193371f9cTorok Edwin#include <cstdio>
27f42e4a6e089e8413247400fe58ad299193371f9cTorok Edwin
28558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregorusing namespace clang;
29558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor
30d6471f7c1921c7802804ce3ff6fe9768310f72b9David BlaikieFixItRewriter::FixItRewriter(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
31ba5f6eced29937e4e4851a2c0980744768413d66Nick Lewycky                             const LangOptions &LangOpts,
321450f265fcc84a7ca64dd9f3b8d4492c5bd55e23Nick Lewycky                             FixItOptions *FixItOpts)
33ba5f6eced29937e4e4851a2c0980744768413d66Nick Lewycky  : Diags(Diags),
3430660a898545416f0fea2d717f16f75640001e38Ted Kremenek    Editor(SourceMgr, LangOpts),
35ba5f6eced29937e4e4851a2c0980744768413d66Nick Lewycky    Rewrite(SourceMgr, LangOpts),
361450f265fcc84a7ca64dd9f3b8d4492c5bd55e23Nick Lewycky    FixItOpts(FixItOpts),
3761d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis    NumFailures(0),
3861d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis    PrevDiagSilenced(false) {
3961d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis  OwnsClient = Diags.ownsClient();
40bdbb004f38978da0c4a75af3294d1c7b5ff84af1Douglas Gregor  Client = Diags.takeClient();
41de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor  Diags.setClient(this);
42558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor}
43558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor
44558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas GregorFixItRewriter::~FixItRewriter() {
45bdbb004f38978da0c4a75af3294d1c7b5ff84af1Douglas Gregor  Diags.takeClient();
4661d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis  Diags.setClient(Client, OwnsClient);
47558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor}
48558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor
495f9e272e632e951b1efe824cd16acb4d96077930Chris Lattnerbool FixItRewriter::WriteFixedFile(FileID ID, raw_ostream &OS) {
50d4a97a18ea3cda3ba095e7c0c6708e7a39cf31dbNick Lewycky  const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(ID);
51d4a97a18ea3cda3ba095e7c0c6708e7a39cf31dbNick Lewycky  if (!RewriteBuf) return true;
520ade808e0ac411baa2dbc1f76ad352b9b6d6d3f8Nick Lewycky  RewriteBuf->write(OS);
53d4a97a18ea3cda3ba095e7c0c6708e7a39cf31dbNick Lewycky  OS.flush();
54d4a97a18ea3cda3ba095e7c0c6708e7a39cf31dbNick Lewycky  return false;
55d4a97a18ea3cda3ba095e7c0c6708e7a39cf31dbNick Lewycky}
56d4a97a18ea3cda3ba095e7c0c6708e7a39cf31dbNick Lewycky
5730660a898545416f0fea2d717f16f75640001e38Ted Kremeneknamespace {
5830660a898545416f0fea2d717f16f75640001e38Ted Kremenek
5930660a898545416f0fea2d717f16f75640001e38Ted Kremenekclass RewritesReceiver : public edit::EditsReceiver {
6030660a898545416f0fea2d717f16f75640001e38Ted Kremenek  Rewriter &Rewrite;
6130660a898545416f0fea2d717f16f75640001e38Ted Kremenek
6230660a898545416f0fea2d717f16f75640001e38Ted Kremenekpublic:
6330660a898545416f0fea2d717f16f75640001e38Ted Kremenek  RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
6430660a898545416f0fea2d717f16f75640001e38Ted Kremenek
6530660a898545416f0fea2d717f16f75640001e38Ted Kremenek  virtual void insert(SourceLocation loc, StringRef text) {
6630660a898545416f0fea2d717f16f75640001e38Ted Kremenek    Rewrite.InsertText(loc, text);
6730660a898545416f0fea2d717f16f75640001e38Ted Kremenek  }
6830660a898545416f0fea2d717f16f75640001e38Ted Kremenek  virtual void replace(CharSourceRange range, StringRef text) {
6930660a898545416f0fea2d717f16f75640001e38Ted Kremenek    Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
7030660a898545416f0fea2d717f16f75640001e38Ted Kremenek  }
7130660a898545416f0fea2d717f16f75640001e38Ted Kremenek};
7230660a898545416f0fea2d717f16f75640001e38Ted Kremenek
7330660a898545416f0fea2d717f16f75640001e38Ted Kremenek}
7430660a898545416f0fea2d717f16f75640001e38Ted Kremenek
7561d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidisbool FixItRewriter::WriteFixedFiles(
7661d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis            std::vector<std::pair<std::string, std::string> > *RewrittenFiles) {
7796872c44700bcfc184bf99a6755898e7930f65edNick Lewycky  if (NumFailures > 0 && !FixItOpts->FixWhatYouCan) {
78de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor    Diag(FullSourceLoc(), diag::warn_fixit_no_changes);
79558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor    return true;
80558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor  }
81558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor
8230660a898545416f0fea2d717f16f75640001e38Ted Kremenek  RewritesReceiver Rec(Rewrite);
8330660a898545416f0fea2d717f16f75640001e38Ted Kremenek  Editor.applyRewrites(Rec);
8430660a898545416f0fea2d717f16f75640001e38Ted Kremenek
85d4a97a18ea3cda3ba095e7c0c6708e7a39cf31dbNick Lewycky  for (iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) {
86d4a97a18ea3cda3ba095e7c0c6708e7a39cf31dbNick Lewycky    const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first);
87c8af9107fd1eb014d9124b753c38c4d06fa219f4Argyrios Kyrtzidis    int fd;
88c8af9107fd1eb014d9124b753c38c4d06fa219f4Argyrios Kyrtzidis    std::string Filename = FixItOpts->RewriteFilename(Entry->getName(), fd);
89558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor    std::string Err;
906f42b62b6194f53bcbc349f5d17388e1936535d7Dylan Noblesmith    OwningPtr<llvm::raw_fd_ostream> OS;
91c8af9107fd1eb014d9124b753c38c4d06fa219f4Argyrios Kyrtzidis    if (fd != -1) {
92c8af9107fd1eb014d9124b753c38c4d06fa219f4Argyrios Kyrtzidis      OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true));
93c8af9107fd1eb014d9124b753c38c4d06fa219f4Argyrios Kyrtzidis    } else {
94c8af9107fd1eb014d9124b753c38c4d06fa219f4Argyrios Kyrtzidis      OS.reset(new llvm::raw_fd_ostream(Filename.c_str(), Err,
95d965f95daa97097c8ddc5e1165ceae585a888719Rafael Espindola                                        llvm::sys::fs::F_Binary));
96c8af9107fd1eb014d9124b753c38c4d06fa219f4Argyrios Kyrtzidis    }
97d4a97a18ea3cda3ba095e7c0c6708e7a39cf31dbNick Lewycky    if (!Err.empty()) {
98d4a97a18ea3cda3ba095e7c0c6708e7a39cf31dbNick Lewycky      Diags.Report(clang::diag::err_fe_unable_to_open_output)
99ba5f6eced29937e4e4851a2c0980744768413d66Nick Lewycky          << Filename << Err;
100d4a97a18ea3cda3ba095e7c0c6708e7a39cf31dbNick Lewycky      continue;
101d4a97a18ea3cda3ba095e7c0c6708e7a39cf31dbNick Lewycky    }
102d4a97a18ea3cda3ba095e7c0c6708e7a39cf31dbNick Lewycky    RewriteBuffer &RewriteBuf = I->second;
103c8af9107fd1eb014d9124b753c38c4d06fa219f4Argyrios Kyrtzidis    RewriteBuf.write(*OS);
104c8af9107fd1eb014d9124b753c38c4d06fa219f4Argyrios Kyrtzidis    OS->flush();
10561d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis
10661d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis    if (RewrittenFiles)
10761d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis      RewrittenFiles->push_back(std::make_pair(Entry->getName(), Filename));
108558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor  }
109558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor
110558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor  return false;
111558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor}
112558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor
113558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregorbool FixItRewriter::IncludeInDiagnosticCounts() const {
114d4a97a18ea3cda3ba095e7c0c6708e7a39cf31dbNick Lewycky  return Client ? Client->IncludeInDiagnosticCounts() : true;
115558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor}
116558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor
117d6471f7c1921c7802804ce3ff6fe9768310f72b9David Blaikievoid FixItRewriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
11840847cfb58acc3cac7d68727df9455ac45f2e118David Blaikie                                     const Diagnostic &Info) {
119dea22a399d312444b2a6bb26b4a1be7c4ded1858Argyrios Kyrtzidis  // Default implementation (Warnings/errors count).
12078ad0b98848c17a0a11847fa1d456e2dfec8aa2fDavid Blaikie  DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
121dea22a399d312444b2a6bb26b4a1be7c4ded1858Argyrios Kyrtzidis
12261d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis  if (!FixItOpts->Silent ||
12361d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis      DiagLevel >= DiagnosticsEngine::Error ||
12461d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis      (DiagLevel == DiagnosticsEngine::Note && !PrevDiagSilenced) ||
12561d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis      (DiagLevel > DiagnosticsEngine::Note && Info.getNumFixItHints())) {
12661d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis    Client->HandleDiagnostic(DiagLevel, Info);
12761d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis    PrevDiagSilenced = false;
12861d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis  } else {
12961d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis    PrevDiagSilenced = true;
13061d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis  }
13126df2f09587ad6978ac8e357ca46b2731d591cc4Douglas Gregor
132ba5f6eced29937e4e4851a2c0980744768413d66Nick Lewycky  // Skip over any diagnostics that are ignored or notes.
133d6471f7c1921c7802804ce3ff6fe9768310f72b9David Blaikie  if (DiagLevel <= DiagnosticsEngine::Note)
13426df2f09587ad6978ac8e357ca46b2731d591cc4Douglas Gregor    return;
13561d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis  // Skip over errors if we are only fixing warnings.
13661d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis  if (DiagLevel >= DiagnosticsEngine::Error && FixItOpts->FixOnlyWarnings) {
13761d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis    ++NumFailures;
13861d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis    return;
13961d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis  }
14026df2f09587ad6978ac8e357ca46b2731d591cc4Douglas Gregor
141558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor  // Make sure that we can perform all of the modifications we
142558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor  // in this diagnostic.
14330660a898545416f0fea2d717f16f75640001e38Ted Kremenek  edit::Commit commit(Editor);
144849b243d4065f56742a4677d6dc8277609a151f8Douglas Gregor  for (unsigned Idx = 0, Last = Info.getNumFixItHints();
145837a406c6666ebe4dc7b75f7848a7c0cc523e307Douglas Gregor       Idx < Last; ++Idx) {
146849b243d4065f56742a4677d6dc8277609a151f8Douglas Gregor    const FixItHint &Hint = Info.getFixItHint(Idx);
14730660a898545416f0fea2d717f16f75640001e38Ted Kremenek
14830660a898545416f0fea2d717f16f75640001e38Ted Kremenek    if (Hint.CodeToInsert.empty()) {
14930660a898545416f0fea2d717f16f75640001e38Ted Kremenek      if (Hint.InsertFromRange.isValid())
15030660a898545416f0fea2d717f16f75640001e38Ted Kremenek        commit.insertFromRange(Hint.RemoveRange.getBegin(),
15130660a898545416f0fea2d717f16f75640001e38Ted Kremenek                           Hint.InsertFromRange, /*afterToken=*/false,
15230660a898545416f0fea2d717f16f75640001e38Ted Kremenek                           Hint.BeforePreviousInsertions);
15330660a898545416f0fea2d717f16f75640001e38Ted Kremenek      else
15430660a898545416f0fea2d717f16f75640001e38Ted Kremenek        commit.remove(Hint.RemoveRange);
15530660a898545416f0fea2d717f16f75640001e38Ted Kremenek    } else {
15630660a898545416f0fea2d717f16f75640001e38Ted Kremenek      if (Hint.RemoveRange.isTokenRange() ||
15730660a898545416f0fea2d717f16f75640001e38Ted Kremenek          Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd())
15830660a898545416f0fea2d717f16f75640001e38Ted Kremenek        commit.replace(Hint.RemoveRange, Hint.CodeToInsert);
15930660a898545416f0fea2d717f16f75640001e38Ted Kremenek      else
16030660a898545416f0fea2d717f16f75640001e38Ted Kremenek        commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert,
16130660a898545416f0fea2d717f16f75640001e38Ted Kremenek                    /*afterToken=*/false, Hint.BeforePreviousInsertions);
162558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor    }
163558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor  }
16430660a898545416f0fea2d717f16f75640001e38Ted Kremenek  bool CanRewrite = Info.getNumFixItHints() > 0 && commit.isCommitable();
165558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor
1661eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  if (!CanRewrite) {
167849b243d4065f56742a4677d6dc8277609a151f8Douglas Gregor    if (Info.getNumFixItHints() > 0)
168de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor      Diag(Info.getLocation(), diag::note_fixit_in_macro);
169837a406c6666ebe4dc7b75f7848a7c0cc523e307Douglas Gregor
170837a406c6666ebe4dc7b75f7848a7c0cc523e307Douglas Gregor    // If this was an error, refuse to perform any rewriting.
17161d679ab2831b161c857cf3f974312fbd4ef1efdArgyrios Kyrtzidis    if (DiagLevel >= DiagnosticsEngine::Error) {
172de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor      if (++NumFailures == 1)
173de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor        Diag(Info.getLocation(), diag::note_fixit_unfixed_error);
174837a406c6666ebe4dc7b75f7848a7c0cc523e307Douglas Gregor    }
175558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor    return;
176837a406c6666ebe4dc7b75f7848a7c0cc523e307Douglas Gregor  }
17730660a898545416f0fea2d717f16f75640001e38Ted Kremenek
17830660a898545416f0fea2d717f16f75640001e38Ted Kremenek  if (!Editor.commit(commit)) {
179558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor    ++NumFailures;
180de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor    Diag(Info.getLocation(), diag::note_fixit_failed);
181de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor    return;
182de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor  }
183de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor
184de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor  Diag(Info.getLocation(), diag::note_fixit_applied);
185de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor}
186de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor
187de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor/// \brief Emit a diagnostic via the adapted diagnostic client.
18833e4e70c8c0a17e0ccb7465d96556b077a68ecb1Argyrios Kyrtzidisvoid FixItRewriter::Diag(SourceLocation Loc, unsigned DiagID) {
189de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor  // When producing this diagnostic, we temporarily bypass ourselves,
190de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor  // clear out any current diagnostic, and let the downstream client
191de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor  // format the diagnostic.
192bdbb004f38978da0c4a75af3294d1c7b5ff84af1Douglas Gregor  Diags.takeClient();
193de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor  Diags.setClient(Client);
194de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor  Diags.Clear();
195de4bf6a63219c5b9d3bce1fed3dfe075568098a0Douglas Gregor  Diags.Report(Loc, DiagID);
196bdbb004f38978da0c4a75af3294d1c7b5ff84af1Douglas Gregor  Diags.takeClient();
1971eb4433ac451dc16f4133a88af2d002ac26c58efMike Stump  Diags.setClient(this);
198558cb56caf8906e0adbe643e3febbef0b7af1b9fDouglas Gregor}
199ba5f6eced29937e4e4851a2c0980744768413d66Nick Lewycky
2001450f265fcc84a7ca64dd9f3b8d4492c5bd55e23Nick LewyckyFixItOptions::~FixItOptions() {}
201