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