]> CyberLeo.Net >> Repos - FreeBSD/releng/9.0.git/blob - contrib/llvm/tools/clang/lib/Rewrite/FixItRewriter.cpp
Copy stable/9 to releng/9.0 as part of the FreeBSD 9.0-RELEASE release
[FreeBSD/releng/9.0.git] / contrib / llvm / tools / clang / lib / Rewrite / FixItRewriter.cpp
1 //===--- FixItRewriter.cpp - Fix-It Rewriter Diagnostic Client --*- C++ -*-===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This is a diagnostic client adaptor that performs rewrites as
11 // suggested by code modification hints attached to diagnostics. It
12 // then forwards any diagnostics to the adapted diagnostic client.
13 //
14 //===----------------------------------------------------------------------===//
15
16 #include "clang/Rewrite/FixItRewriter.h"
17 #include "clang/Basic/FileManager.h"
18 #include "clang/Basic/SourceLocation.h"
19 #include "clang/Basic/SourceManager.h"
20 #include "clang/Frontend/FrontendDiagnostic.h"
21 #include "llvm/Support/raw_ostream.h"
22 #include "llvm/Support/Path.h"
23 #include "llvm/ADT/OwningPtr.h"
24 #include <cstdio>
25
26 using namespace clang;
27
28 FixItRewriter::FixItRewriter(DiagnosticsEngine &Diags, SourceManager &SourceMgr,
29                              const LangOptions &LangOpts,
30                              FixItOptions *FixItOpts)
31   : Diags(Diags),
32     Rewrite(SourceMgr, LangOpts),
33     FixItOpts(FixItOpts),
34     NumFailures(0) {
35   Client = Diags.takeClient();
36   Diags.setClient(this);
37 }
38
39 FixItRewriter::~FixItRewriter() {
40   Diags.takeClient();
41   Diags.setClient(Client);
42 }
43
44 bool FixItRewriter::WriteFixedFile(FileID ID, raw_ostream &OS) {
45   const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(ID);
46   if (!RewriteBuf) return true;
47   RewriteBuf->write(OS);
48   OS.flush();
49   return false;
50 }
51
52 bool FixItRewriter::WriteFixedFiles() {
53   if (NumFailures > 0 && !FixItOpts->FixWhatYouCan) {
54     Diag(FullSourceLoc(), diag::warn_fixit_no_changes);
55     return true;
56   }
57
58   for (iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) {
59     const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first);
60     std::string Filename = FixItOpts->RewriteFilename(Entry->getName());
61     std::string Err;
62     llvm::raw_fd_ostream OS(Filename.c_str(), Err,
63                             llvm::raw_fd_ostream::F_Binary);
64     if (!Err.empty()) {
65       Diags.Report(clang::diag::err_fe_unable_to_open_output)
66           << Filename << Err;
67       continue;
68     }
69     RewriteBuffer &RewriteBuf = I->second;
70     RewriteBuf.write(OS);
71     OS.flush();
72   }
73
74   return false;
75 }
76
77 bool FixItRewriter::IncludeInDiagnosticCounts() const {
78   return Client ? Client->IncludeInDiagnosticCounts() : true;
79 }
80
81 void FixItRewriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
82                                      const Diagnostic &Info) {
83   // Default implementation (Warnings/errors count).
84   DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info);
85
86   Client->HandleDiagnostic(DiagLevel, Info);
87
88   // Skip over any diagnostics that are ignored or notes.
89   if (DiagLevel <= DiagnosticsEngine::Note)
90     return;
91
92   // Make sure that we can perform all of the modifications we
93   // in this diagnostic.
94   bool CanRewrite = Info.getNumFixItHints() > 0;
95   for (unsigned Idx = 0, Last = Info.getNumFixItHints();
96        Idx < Last; ++Idx) {
97     const FixItHint &Hint = Info.getFixItHint(Idx);
98     if (Hint.RemoveRange.isValid() &&
99         Rewrite.getRangeSize(Hint.RemoveRange) == -1) {
100       CanRewrite = false;
101       break;
102     }
103   }
104
105   if (!CanRewrite) {
106     if (Info.getNumFixItHints() > 0)
107       Diag(Info.getLocation(), diag::note_fixit_in_macro);
108
109     // If this was an error, refuse to perform any rewriting.
110     if (DiagLevel == DiagnosticsEngine::Error ||
111           DiagLevel == DiagnosticsEngine::Fatal) {
112       if (++NumFailures == 1)
113         Diag(Info.getLocation(), diag::note_fixit_unfixed_error);
114     }
115     return;
116   }
117
118   bool Failed = false;
119   for (unsigned Idx = 0, Last = Info.getNumFixItHints();
120        Idx < Last; ++Idx) {
121     const FixItHint &Hint = Info.getFixItHint(Idx);
122
123     if (Hint.CodeToInsert.empty()) {
124       // We're removing code.
125       if (Rewrite.RemoveText(Hint.RemoveRange))
126         Failed = true;
127       continue;
128     }
129
130     // We're replacing code.
131     if (Rewrite.ReplaceText(Hint.RemoveRange.getBegin(),
132                             Rewrite.getRangeSize(Hint.RemoveRange),
133                             Hint.CodeToInsert))
134       Failed = true;
135   }
136
137   if (Failed) {
138     ++NumFailures;
139     Diag(Info.getLocation(), diag::note_fixit_failed);
140     return;
141   }
142
143   Diag(Info.getLocation(), diag::note_fixit_applied);
144 }
145
146 /// \brief Emit a diagnostic via the adapted diagnostic client.
147 void FixItRewriter::Diag(SourceLocation Loc, unsigned DiagID) {
148   // When producing this diagnostic, we temporarily bypass ourselves,
149   // clear out any current diagnostic, and let the downstream client
150   // format the diagnostic.
151   Diags.takeClient();
152   Diags.setClient(Client);
153   Diags.Clear();
154   Diags.Report(Loc, DiagID);
155   Diags.takeClient();
156   Diags.setClient(this);
157 }
158
159 DiagnosticConsumer *FixItRewriter::clone(DiagnosticsEngine &Diags) const {
160   return new FixItRewriter(Diags, Diags.getSourceManager(), 
161                            Rewrite.getLangOpts(), FixItOpts);
162 }
163
164 FixItOptions::~FixItOptions() {}