/Users/buildslave/jenkins/sharedspace/clang-stage2-coverage-R@2/llvm/tools/clang/lib/Frontend/Rewrite/FixItRewriter.cpp
Line | Count | Source (jump to first uncovered line) |
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/Frontend/FixItRewriter.h" |
17 | | #include "clang/Basic/FileManager.h" |
18 | | #include "clang/Basic/SourceLocation.h" |
19 | | #include "clang/Basic/SourceManager.h" |
20 | | #include "clang/Edit/Commit.h" |
21 | | #include "clang/Edit/EditsReceiver.h" |
22 | | #include "clang/Frontend/FrontendDiagnostic.h" |
23 | | #include "llvm/Support/Path.h" |
24 | | #include "llvm/Support/raw_ostream.h" |
25 | | #include <cstdio> |
26 | | #include <memory> |
27 | | |
28 | | using namespace clang; |
29 | | |
30 | | FixItRewriter::FixItRewriter(DiagnosticsEngine &Diags, SourceManager &SourceMgr, |
31 | | const LangOptions &LangOpts, |
32 | | FixItOptions *FixItOpts) |
33 | | : Diags(Diags), |
34 | | Editor(SourceMgr, LangOpts), |
35 | | Rewrite(SourceMgr, LangOpts), |
36 | | FixItOpts(FixItOpts), |
37 | | NumFailures(0), |
38 | 58 | PrevDiagSilenced(false) { |
39 | 58 | Owner = Diags.takeClient(); |
40 | 58 | Client = Diags.getClient(); |
41 | 58 | Diags.setClient(this, false); |
42 | 58 | } |
43 | | |
44 | 58 | FixItRewriter::~FixItRewriter() { |
45 | 58 | Diags.setClient(Client, Owner.release() != nullptr); |
46 | 58 | } |
47 | | |
48 | 0 | bool FixItRewriter::WriteFixedFile(FileID ID, raw_ostream &OS) { |
49 | 0 | const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(ID); |
50 | 0 | if (!RewriteBuf0 ) return true0 ; |
51 | 0 | RewriteBuf->write(OS); |
52 | 0 | OS.flush(); |
53 | 0 | return false; |
54 | 0 | } |
55 | | |
56 | | namespace { |
57 | | |
58 | | class RewritesReceiver : public edit::EditsReceiver { |
59 | | Rewriter &Rewrite; |
60 | | |
61 | | public: |
62 | 56 | RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) { } |
63 | | |
64 | 271 | void insert(SourceLocation loc, StringRef text) override { |
65 | 271 | Rewrite.InsertText(loc, text); |
66 | 271 | } |
67 | 493 | void replace(CharSourceRange range, StringRef text) override { |
68 | 493 | Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text); |
69 | 493 | } |
70 | | }; |
71 | | |
72 | | } |
73 | | |
74 | | bool FixItRewriter::WriteFixedFiles( |
75 | 58 | std::vector<std::pair<std::string, std::string> > *RewrittenFiles) { |
76 | 58 | if (NumFailures > 0 && 58 !FixItOpts->FixWhatYouCan2 ) { |
77 | 2 | Diag(FullSourceLoc(), diag::warn_fixit_no_changes); |
78 | 2 | return true; |
79 | 2 | } |
80 | 56 | |
81 | 56 | RewritesReceiver Rec(Rewrite); |
82 | 56 | Editor.applyRewrites(Rec); |
83 | 56 | |
84 | 56 | if (FixItOpts->InPlace56 ) { |
85 | 54 | // Overwriting open files on Windows is tricky, but the rewriter can do it |
86 | 54 | // for us. |
87 | 54 | Rewrite.overwriteChangedFiles(); |
88 | 54 | return false; |
89 | 54 | } |
90 | 2 | |
91 | 4 | for (iterator I = buffer_begin(), E = buffer_end(); 2 I != E4 ; ++I2 ) { |
92 | 2 | const FileEntry *Entry = Rewrite.getSourceMgr().getFileEntryForID(I->first); |
93 | 2 | int fd; |
94 | 2 | std::string Filename = FixItOpts->RewriteFilename(Entry->getName(), fd); |
95 | 2 | std::error_code EC; |
96 | 2 | std::unique_ptr<llvm::raw_fd_ostream> OS; |
97 | 2 | if (fd != -12 ) { |
98 | 1 | OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true)); |
99 | 2 | } else { |
100 | 1 | OS.reset(new llvm::raw_fd_ostream(Filename, EC, llvm::sys::fs::F_None)); |
101 | 1 | } |
102 | 2 | if (EC2 ) { |
103 | 0 | Diags.Report(clang::diag::err_fe_unable_to_open_output) << Filename |
104 | 0 | << EC.message(); |
105 | 0 | continue; |
106 | 0 | } |
107 | 2 | RewriteBuffer &RewriteBuf = I->second; |
108 | 2 | RewriteBuf.write(*OS); |
109 | 2 | OS->flush(); |
110 | 2 | |
111 | 2 | if (RewrittenFiles) |
112 | 1 | RewrittenFiles->push_back(std::make_pair(Entry->getName(), Filename)); |
113 | 2 | } |
114 | 58 | |
115 | 58 | return false; |
116 | 58 | } |
117 | | |
118 | 2.13k | bool FixItRewriter::IncludeInDiagnosticCounts() const { |
119 | 2.13k | return Client ? Client->IncludeInDiagnosticCounts()2.13k : true0 ; |
120 | 2.13k | } |
121 | | |
122 | | void FixItRewriter::HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, |
123 | 838 | const Diagnostic &Info) { |
124 | 838 | // Default implementation (Warnings/errors count). |
125 | 838 | DiagnosticConsumer::HandleDiagnostic(DiagLevel, Info); |
126 | 838 | |
127 | 838 | if (!FixItOpts->Silent || |
128 | 2 | DiagLevel >= DiagnosticsEngine::Error || |
129 | 0 | (DiagLevel == DiagnosticsEngine::Note && 0 !PrevDiagSilenced0 ) || |
130 | 838 | (DiagLevel > DiagnosticsEngine::Note && 0 Info.getNumFixItHints()0 )) { |
131 | 838 | Client->HandleDiagnostic(DiagLevel, Info); |
132 | 838 | PrevDiagSilenced = false; |
133 | 838 | } else { |
134 | 0 | PrevDiagSilenced = true; |
135 | 0 | } |
136 | 838 | |
137 | 838 | // Skip over any diagnostics that are ignored or notes. |
138 | 838 | if (DiagLevel <= DiagnosticsEngine::Note) |
139 | 125 | return; |
140 | 713 | // Skip over errors if we are only fixing warnings. |
141 | 713 | if (713 DiagLevel >= DiagnosticsEngine::Error && 713 FixItOpts->FixOnlyWarnings455 ) { |
142 | 1 | ++NumFailures; |
143 | 1 | return; |
144 | 1 | } |
145 | 712 | |
146 | 712 | // Make sure that we can perform all of the modifications we |
147 | 712 | // in this diagnostic. |
148 | 712 | edit::Commit commit(Editor); |
149 | 712 | for (unsigned Idx = 0, Last = Info.getNumFixItHints(); |
150 | 1.51k | Idx < Last1.51k ; ++Idx802 ) { |
151 | 802 | const FixItHint &Hint = Info.getFixItHint(Idx); |
152 | 802 | |
153 | 802 | if (Hint.CodeToInsert.empty()802 ) { |
154 | 169 | if (Hint.InsertFromRange.isValid()) |
155 | 26 | commit.insertFromRange(Hint.RemoveRange.getBegin(), |
156 | 26 | Hint.InsertFromRange, /*afterToken=*/false, |
157 | 26 | Hint.BeforePreviousInsertions); |
158 | 169 | else |
159 | 143 | commit.remove(Hint.RemoveRange); |
160 | 802 | } else { |
161 | 633 | if (Hint.RemoveRange.isTokenRange() || |
162 | 389 | Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd()) |
163 | 362 | commit.replace(Hint.RemoveRange, Hint.CodeToInsert); |
164 | 633 | else |
165 | 271 | commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert, |
166 | 271 | /*afterToken=*/false, Hint.BeforePreviousInsertions); |
167 | 633 | } |
168 | 802 | } |
169 | 670 | bool CanRewrite = Info.getNumFixItHints() > 0 && commit.isCommitable(); |
170 | 712 | |
171 | 712 | if (!CanRewrite712 ) { |
172 | 42 | if (Info.getNumFixItHints() > 0) |
173 | 0 | Diag(Info.getLocation(), diag::note_fixit_in_macro); |
174 | 42 | |
175 | 42 | // If this was an error, refuse to perform any rewriting. |
176 | 42 | if (DiagLevel >= DiagnosticsEngine::Error42 ) { |
177 | 2 | if (++NumFailures == 1) |
178 | 1 | Diag(Info.getLocation(), diag::note_fixit_unfixed_error); |
179 | 2 | } |
180 | 42 | return; |
181 | 42 | } |
182 | 670 | |
183 | 670 | if (670 !Editor.commit(commit)670 ) { |
184 | 0 | ++NumFailures; |
185 | 0 | Diag(Info.getLocation(), diag::note_fixit_failed); |
186 | 0 | return; |
187 | 0 | } |
188 | 670 | |
189 | 670 | Diag(Info.getLocation(), diag::note_fixit_applied); |
190 | 670 | } |
191 | | |
192 | | /// \brief Emit a diagnostic via the adapted diagnostic client. |
193 | 673 | void FixItRewriter::Diag(SourceLocation Loc, unsigned DiagID) { |
194 | 673 | // When producing this diagnostic, we temporarily bypass ourselves, |
195 | 673 | // clear out any current diagnostic, and let the downstream client |
196 | 673 | // format the diagnostic. |
197 | 673 | Diags.setClient(Client, false); |
198 | 673 | Diags.Clear(); |
199 | 673 | Diags.Report(Loc, DiagID); |
200 | 673 | Diags.setClient(this, false); |
201 | 673 | } |
202 | | |
203 | 58 | FixItOptions::~FixItOptions() {} |