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