1*0a6a1f1dSLionel Sambuc //===--- FixItRewriter.cpp - Fix-It Rewriter Diagnostic Client --*- C++ -*-===//
2*0a6a1f1dSLionel Sambuc //
3*0a6a1f1dSLionel Sambuc //                     The LLVM Compiler Infrastructure
4*0a6a1f1dSLionel Sambuc //
5*0a6a1f1dSLionel Sambuc // This file is distributed under the University of Illinois Open Source
6*0a6a1f1dSLionel Sambuc // License. See LICENSE.TXT for details.
7*0a6a1f1dSLionel Sambuc //
8*0a6a1f1dSLionel Sambuc //===----------------------------------------------------------------------===//
9*0a6a1f1dSLionel Sambuc //
10*0a6a1f1dSLionel Sambuc // This is a diagnostic client adaptor that performs rewrites as
11*0a6a1f1dSLionel Sambuc // suggested by code modification hints attached to diagnostics. It
12*0a6a1f1dSLionel Sambuc // then forwards any diagnostics to the adapted diagnostic client.
13*0a6a1f1dSLionel Sambuc //
14*0a6a1f1dSLionel Sambuc //===----------------------------------------------------------------------===//
15*0a6a1f1dSLionel Sambuc 
16*0a6a1f1dSLionel Sambuc #include "clang/Rewrite/Frontend/FixItRewriter.h"
17*0a6a1f1dSLionel Sambuc #include "clang/Basic/FileManager.h"
18*0a6a1f1dSLionel Sambuc #include "clang/Basic/SourceLocation.h"
19*0a6a1f1dSLionel Sambuc #include "clang/Basic/SourceManager.h"
20*0a6a1f1dSLionel Sambuc #include "clang/Edit/Commit.h"
21*0a6a1f1dSLionel Sambuc #include "clang/Edit/EditsReceiver.h"
22*0a6a1f1dSLionel Sambuc #include "clang/Frontend/FrontendDiagnostic.h"
23*0a6a1f1dSLionel Sambuc #include "llvm/Support/Path.h"
24*0a6a1f1dSLionel Sambuc #include "llvm/Support/raw_ostream.h"
25*0a6a1f1dSLionel Sambuc #include <cstdio>
26*0a6a1f1dSLionel Sambuc #include <memory>
27*0a6a1f1dSLionel Sambuc 
28*0a6a1f1dSLionel Sambuc using namespace clang;
29*0a6a1f1dSLionel Sambuc 
FixItRewriter(DiagnosticsEngine & Diags,SourceManager & SourceMgr,const LangOptions & LangOpts,FixItOptions * FixItOpts)30*0a6a1f1dSLionel Sambuc FixItRewriter::FixItRewriter(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
31*0a6a1f1dSLionel Sambuc                              const LangOptions &LangOpts,
32*0a6a1f1dSLionel Sambuc                              FixItOptions *FixItOpts)
33*0a6a1f1dSLionel Sambuc   : Diags(Diags),
34*0a6a1f1dSLionel Sambuc     Editor(SourceMgr, LangOpts),
35*0a6a1f1dSLionel Sambuc     Rewrite(SourceMgr, LangOpts),
36*0a6a1f1dSLionel Sambuc     FixItOpts(FixItOpts),
37*0a6a1f1dSLionel Sambuc     NumFailures(0),
38*0a6a1f1dSLionel Sambuc     PrevDiagSilenced(false) {
39*0a6a1f1dSLionel Sambuc   Owner = Diags.takeClient();
40*0a6a1f1dSLionel Sambuc   Client = Diags.getClient();
41*0a6a1f1dSLionel Sambuc   Diags.setClient(this, false);
42*0a6a1f1dSLionel Sambuc }
43*0a6a1f1dSLionel Sambuc 
~FixItRewriter()44*0a6a1f1dSLionel Sambuc FixItRewriter::~FixItRewriter() {
45*0a6a1f1dSLionel Sambuc   Diags.setClient(Client, Owner.release() != nullptr);
46*0a6a1f1dSLionel Sambuc }
47*0a6a1f1dSLionel Sambuc 
WriteFixedFile(FileID ID,raw_ostream & OS)48*0a6a1f1dSLionel Sambuc bool FixItRewriter::WriteFixedFile(FileID ID, raw_ostream &OS) {
49*0a6a1f1dSLionel Sambuc   const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(ID);
50*0a6a1f1dSLionel Sambuc   if (!RewriteBuf) return true;
51*0a6a1f1dSLionel Sambuc   RewriteBuf->write(OS);
52*0a6a1f1dSLionel Sambuc   OS.flush();
53*0a6a1f1dSLionel Sambuc   return false;
54*0a6a1f1dSLionel Sambuc }
55*0a6a1f1dSLionel Sambuc 
56*0a6a1f1dSLionel Sambuc namespace {
57*0a6a1f1dSLionel Sambuc 
58*0a6a1f1dSLionel Sambuc class RewritesReceiver : public edit::EditsReceiver {
59*0a6a1f1dSLionel Sambuc   Rewriter &Rewrite;
60*0a6a1f1dSLionel Sambuc 
61*0a6a1f1dSLionel Sambuc public:
RewritesReceiver(Rewriter & Rewrite)62*0a6a1f1dSLionel Sambuc   RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { }
63*0a6a1f1dSLionel Sambuc 
insert(SourceLocation loc,StringRef text)64*0a6a1f1dSLionel Sambuc   void insert(SourceLocation loc, StringRef text) override {
65*0a6a1f1dSLionel Sambuc     Rewrite.InsertText(loc, text);
66*0a6a1f1dSLionel Sambuc   }
replace(CharSourceRange range,StringRef text)67*0a6a1f1dSLionel Sambuc   void replace(CharSourceRange range, StringRef text) override {
68*0a6a1f1dSLionel Sambuc     Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
69*0a6a1f1dSLionel Sambuc   }
70*0a6a1f1dSLionel Sambuc };
71*0a6a1f1dSLionel Sambuc 
72*0a6a1f1dSLionel Sambuc }
73*0a6a1f1dSLionel Sambuc 
WriteFixedFiles(std::vector<std::pair<std::string,std::string>> * RewrittenFiles)74*0a6a1f1dSLionel Sambuc bool FixItRewriter::WriteFixedFiles(
75*0a6a1f1dSLionel Sambuc             std::vector<std::pair<std::string, std::string> > *RewrittenFiles) {
76*0a6a1f1dSLionel Sambuc   if (NumFailures > 0 && !FixItOpts->FixWhatYouCan) {
77*0a6a1f1dSLionel Sambuc     Diag(FullSourceLoc(), diag::warn_fixit_no_changes);
78*0a6a1f1dSLionel Sambuc     return true;
79*0a6a1f1dSLionel Sambuc   }
80*0a6a1f1dSLionel Sambuc 
81*0a6a1f1dSLionel Sambuc   RewritesReceiver Rec(Rewrite);
82*0a6a1f1dSLionel Sambuc   Editor.applyRewrites(Rec);
83*0a6a1f1dSLionel Sambuc 
84*0a6a1f1dSLionel Sambuc   for (iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) {
85*0a6a1f1dSLionel Sambuc     const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first);
86*0a6a1f1dSLionel Sambuc     int fd;
87*0a6a1f1dSLionel Sambuc     std::string Filename = FixItOpts->RewriteFilename(Entry->getName(), fd);
88*0a6a1f1dSLionel Sambuc     std::error_code EC;
89*0a6a1f1dSLionel Sambuc     std::unique_ptr<llvm::raw_fd_ostream> OS;
90*0a6a1f1dSLionel Sambuc     if (fd != -1) {
91*0a6a1f1dSLionel Sambuc       OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true));
92*0a6a1f1dSLionel Sambuc     } else {
93*0a6a1f1dSLionel Sambuc       OS.reset(new llvm::raw_fd_ostream(Filename, EC, llvm::sys::fs::F_None));
94*0a6a1f1dSLionel Sambuc     }
95*0a6a1f1dSLionel Sambuc     if (EC) {
96*0a6a1f1dSLionel Sambuc       Diags.Report(clang::diag::err_fe_unable_to_open_output) << Filename
97*0a6a1f1dSLionel Sambuc                                                               << EC.message();
98*0a6a1f1dSLionel Sambuc       continue;
99*0a6a1f1dSLionel Sambuc     }
100*0a6a1f1dSLionel Sambuc     RewriteBuffer &RewriteBuf = I->second;
101*0a6a1f1dSLionel Sambuc     RewriteBuf.write(*OS);
102*0a6a1f1dSLionel Sambuc     OS->flush();
103*0a6a1f1dSLionel Sambuc 
104*0a6a1f1dSLionel Sambuc     if (RewrittenFiles)
105*0a6a1f1dSLionel Sambuc       RewrittenFiles->push_back(std::make_pair(Entry->getName(), Filename));
106*0a6a1f1dSLionel Sambuc   }
107*0a6a1f1dSLionel Sambuc 
108*0a6a1f1dSLionel Sambuc   return false;
109*0a6a1f1dSLionel Sambuc }
110*0a6a1f1dSLionel Sambuc 
IncludeInDiagnosticCounts() const111*0a6a1f1dSLionel Sambuc bool FixItRewriter::IncludeInDiagnosticCounts() const {
112*0a6a1f1dSLionel Sambuc   return Client ? Client->IncludeInDiagnosticCounts() : true;
113*0a6a1f1dSLionel Sambuc }
114*0a6a1f1dSLionel Sambuc 
HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,const Diagnostic & Info)115*0a6a1f1dSLionel Sambuc void FixItRewriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
116*0a6a1f1dSLionel Sambuc                                      const Diagnostic &Info) {
117*0a6a1f1dSLionel Sambuc   // Default implementation (Warnings/errors count).
118*0a6a1f1dSLionel Sambuc   DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
119*0a6a1f1dSLionel Sambuc 
120*0a6a1f1dSLionel Sambuc   if (!FixItOpts->Silent ||
121*0a6a1f1dSLionel Sambuc       DiagLevel >= DiagnosticsEngine::Error ||
122*0a6a1f1dSLionel Sambuc       (DiagLevel == DiagnosticsEngine::Note && !PrevDiagSilenced) ||
123*0a6a1f1dSLionel Sambuc       (DiagLevel > DiagnosticsEngine::Note && Info.getNumFixItHints())) {
124*0a6a1f1dSLionel Sambuc     Client->HandleDiagnostic(DiagLevel, Info);
125*0a6a1f1dSLionel Sambuc     PrevDiagSilenced = false;
126*0a6a1f1dSLionel Sambuc   } else {
127*0a6a1f1dSLionel Sambuc     PrevDiagSilenced = true;
128*0a6a1f1dSLionel Sambuc   }
129*0a6a1f1dSLionel Sambuc 
130*0a6a1f1dSLionel Sambuc   // Skip over any diagnostics that are ignored or notes.
131*0a6a1f1dSLionel Sambuc   if (DiagLevel <= DiagnosticsEngine::Note)
132*0a6a1f1dSLionel Sambuc     return;
133*0a6a1f1dSLionel Sambuc   // Skip over errors if we are only fixing warnings.
134*0a6a1f1dSLionel Sambuc   if (DiagLevel >= DiagnosticsEngine::Error && FixItOpts->FixOnlyWarnings) {
135*0a6a1f1dSLionel Sambuc     ++NumFailures;
136*0a6a1f1dSLionel Sambuc     return;
137*0a6a1f1dSLionel Sambuc   }
138*0a6a1f1dSLionel Sambuc 
139*0a6a1f1dSLionel Sambuc   // Make sure that we can perform all of the modifications we
140*0a6a1f1dSLionel Sambuc   // in this diagnostic.
141*0a6a1f1dSLionel Sambuc   edit::Commit commit(Editor);
142*0a6a1f1dSLionel Sambuc   for (unsigned Idx = 0, Last = Info.getNumFixItHints();
143*0a6a1f1dSLionel Sambuc        Idx < Last; ++Idx) {
144*0a6a1f1dSLionel Sambuc     const FixItHint &Hint = Info.getFixItHint(Idx);
145*0a6a1f1dSLionel Sambuc 
146*0a6a1f1dSLionel Sambuc     if (Hint.CodeToInsert.empty()) {
147*0a6a1f1dSLionel Sambuc       if (Hint.InsertFromRange.isValid())
148*0a6a1f1dSLionel Sambuc         commit.insertFromRange(Hint.RemoveRange.getBegin(),
149*0a6a1f1dSLionel Sambuc                            Hint.InsertFromRange, /*afterToken=*/false,
150*0a6a1f1dSLionel Sambuc                            Hint.BeforePreviousInsertions);
151*0a6a1f1dSLionel Sambuc       else
152*0a6a1f1dSLionel Sambuc         commit.remove(Hint.RemoveRange);
153*0a6a1f1dSLionel Sambuc     } else {
154*0a6a1f1dSLionel Sambuc       if (Hint.RemoveRange.isTokenRange() ||
155*0a6a1f1dSLionel Sambuc           Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd())
156*0a6a1f1dSLionel Sambuc         commit.replace(Hint.RemoveRange, Hint.CodeToInsert);
157*0a6a1f1dSLionel Sambuc       else
158*0a6a1f1dSLionel Sambuc         commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert,
159*0a6a1f1dSLionel Sambuc                     /*afterToken=*/false, Hint.BeforePreviousInsertions);
160*0a6a1f1dSLionel Sambuc     }
161*0a6a1f1dSLionel Sambuc   }
162*0a6a1f1dSLionel Sambuc   bool CanRewrite = Info.getNumFixItHints() > 0 && commit.isCommitable();
163*0a6a1f1dSLionel Sambuc 
164*0a6a1f1dSLionel Sambuc   if (!CanRewrite) {
165*0a6a1f1dSLionel Sambuc     if (Info.getNumFixItHints() > 0)
166*0a6a1f1dSLionel Sambuc       Diag(Info.getLocation(), diag::note_fixit_in_macro);
167*0a6a1f1dSLionel Sambuc 
168*0a6a1f1dSLionel Sambuc     // If this was an error, refuse to perform any rewriting.
169*0a6a1f1dSLionel Sambuc     if (DiagLevel >= DiagnosticsEngine::Error) {
170*0a6a1f1dSLionel Sambuc       if (++NumFailures == 1)
171*0a6a1f1dSLionel Sambuc         Diag(Info.getLocation(), diag::note_fixit_unfixed_error);
172*0a6a1f1dSLionel Sambuc     }
173*0a6a1f1dSLionel Sambuc     return;
174*0a6a1f1dSLionel Sambuc   }
175*0a6a1f1dSLionel Sambuc 
176*0a6a1f1dSLionel Sambuc   if (!Editor.commit(commit)) {
177*0a6a1f1dSLionel Sambuc     ++NumFailures;
178*0a6a1f1dSLionel Sambuc     Diag(Info.getLocation(), diag::note_fixit_failed);
179*0a6a1f1dSLionel Sambuc     return;
180*0a6a1f1dSLionel Sambuc   }
181*0a6a1f1dSLionel Sambuc 
182*0a6a1f1dSLionel Sambuc   Diag(Info.getLocation(), diag::note_fixit_applied);
183*0a6a1f1dSLionel Sambuc }
184*0a6a1f1dSLionel Sambuc 
185*0a6a1f1dSLionel Sambuc /// \brief Emit a diagnostic via the adapted diagnostic client.
Diag(SourceLocation Loc,unsigned DiagID)186*0a6a1f1dSLionel Sambuc void FixItRewriter::Diag(SourceLocation Loc, unsigned DiagID) {
187*0a6a1f1dSLionel Sambuc   // When producing this diagnostic, we temporarily bypass ourselves,
188*0a6a1f1dSLionel Sambuc   // clear out any current diagnostic, and let the downstream client
189*0a6a1f1dSLionel Sambuc   // format the diagnostic.
190*0a6a1f1dSLionel Sambuc   Diags.setClient(Client, false);
191*0a6a1f1dSLionel Sambuc   Diags.Clear();
192*0a6a1f1dSLionel Sambuc   Diags.Report(Loc, DiagID);
193*0a6a1f1dSLionel Sambuc   Diags.setClient(this, false);
194*0a6a1f1dSLionel Sambuc }
195*0a6a1f1dSLionel Sambuc 
~FixItOptions()196*0a6a1f1dSLionel Sambuc FixItOptions::~FixItOptions() {}
197