clang 22.0.0git
FixItRewriter.cpp
Go to the documentation of this file.
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
17#include "clang/Basic/LLVM.h"
20#include "clang/Edit/Commit.h"
24#include "llvm/ADT/RewriteBuffer.h"
25#include "llvm/ADT/StringRef.h"
26#include "llvm/Support/FileSystem.h"
27#include "llvm/Support/raw_ostream.h"
28#include <cstdio>
29#include <memory>
30#include <string>
31#include <system_error>
32#include <utility>
33
34using namespace clang;
35using llvm::RewriteBuffer;
36
38 const LangOptions &LangOpts,
39 FixItOptions *FixItOpts)
40 : Diags(Diags), Editor(SourceMgr, LangOpts), Rewrite(SourceMgr, LangOpts),
41 FixItOpts(FixItOpts) {
42 Owner = Diags.takeClient();
43 Client = Diags.getClient();
44 Diags.setClient(this, false);
45}
46
48 Diags.setClient(Client, Owner.release() != nullptr);
49}
50
51bool FixItRewriter::WriteFixedFile(FileID ID, raw_ostream &OS) {
52 const RewriteBuffer *RewriteBuf = Rewrite.getRewriteBufferFor(ID);
53 if (!RewriteBuf) return true;
54 RewriteBuf->write(OS);
55 OS.flush();
56 return false;
57}
58
59namespace {
60
61class RewritesReceiver : public edit::EditsReceiver {
63
64public:
65 RewritesReceiver(Rewriter &Rewrite) : Rewrite(Rewrite) {}
66
67 void insert(SourceLocation loc, StringRef text) override {
68 Rewrite.InsertText(loc, text);
69 }
70
71 void replace(CharSourceRange range, StringRef text) override {
72 Rewrite.ReplaceText(range.getBegin(), Rewrite.getRangeSize(range), text);
73 }
74};
75
76} // namespace
77
79 std::vector<std::pair<std::string, std::string>> *RewrittenFiles) {
80 if (NumFailures > 0 && !FixItOpts->FixWhatYouCan) {
81 Diag(FullSourceLoc(), diag::warn_fixit_no_changes);
82 return true;
83 }
84
85 RewritesReceiver Rec(Rewrite);
86 Editor.applyRewrites(Rec);
87
88 if (FixItOpts->InPlace) {
89 // Overwriting open files on Windows is tricky, but the rewriter can do it
90 // for us.
91 Rewrite.overwriteChangedFiles();
92 return false;
93 }
94
95 for (iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) {
97 Rewrite.getSourceMgr().getFileEntryRefForID(I->first);
98 int fd;
99 std::string Filename =
100 FixItOpts->RewriteFilename(std::string(Entry->getName()), fd);
101 std::error_code EC;
102 std::unique_ptr<llvm::raw_fd_ostream> OS;
103 if (fd != -1) {
104 OS.reset(new llvm::raw_fd_ostream(fd, /*shouldClose=*/true));
105 } else {
106 OS.reset(new llvm::raw_fd_ostream(Filename, EC, llvm::sys::fs::OF_None));
107 }
108 if (EC) {
109 Diags.Report(clang::diag::err_fe_unable_to_open_output) << Filename
110 << EC.message();
111 continue;
112 }
113 RewriteBuffer &RewriteBuf = I->second;
114 RewriteBuf.write(*OS);
115 OS->flush();
116
117 if (RewrittenFiles)
118 RewrittenFiles->push_back(
119 std::make_pair(std::string(Entry->getName()), Filename));
120 }
121
122 return false;
123}
124
126 return Client ? Client->IncludeInDiagnosticCounts() : true;
127}
128
130 const Diagnostic &Info) {
131 // Default implementation (Warnings/errors count).
133
134 if (!FixItOpts->Silent ||
135 DiagLevel >= DiagnosticsEngine::Error ||
136 (DiagLevel == DiagnosticsEngine::Note && !PrevDiagSilenced) ||
137 (DiagLevel > DiagnosticsEngine::Note && Info.getNumFixItHints())) {
138 Client->HandleDiagnostic(DiagLevel, Info);
139 PrevDiagSilenced = false;
140 } else {
141 PrevDiagSilenced = true;
142 }
143
144 // Skip over any diagnostics that are ignored or notes.
145 if (DiagLevel <= DiagnosticsEngine::Note)
146 return;
147 // Skip over errors if we are only fixing warnings.
148 if (DiagLevel >= DiagnosticsEngine::Error && FixItOpts->FixOnlyWarnings) {
149 ++NumFailures;
150 return;
151 }
152
153 // Make sure that we can perform all of the modifications we
154 // in this diagnostic.
155 edit::Commit commit(Editor);
156 for (unsigned Idx = 0, Last = Info.getNumFixItHints();
157 Idx < Last; ++Idx) {
158 const FixItHint &Hint = Info.getFixItHint(Idx);
159
160 if (Hint.CodeToInsert.empty()) {
161 if (Hint.InsertFromRange.isValid())
163 Hint.InsertFromRange, /*afterToken=*/false,
165 else
166 commit.remove(Hint.RemoveRange);
167 } else {
168 if (Hint.RemoveRange.isTokenRange() ||
169 Hint.RemoveRange.getBegin() != Hint.RemoveRange.getEnd())
170 commit.replace(Hint.RemoveRange, Hint.CodeToInsert);
171 else
172 commit.insert(Hint.RemoveRange.getBegin(), Hint.CodeToInsert,
173 /*afterToken=*/false, Hint.BeforePreviousInsertions);
174 }
175 }
176 bool CanRewrite = Info.getNumFixItHints() > 0 && commit.isCommitable();
177
178 if (!CanRewrite) {
179 if (Info.getNumFixItHints() > 0)
180 Diag(Info.getLocation(), diag::note_fixit_in_macro);
181
182 // If this was an error, refuse to perform any rewriting.
183 if (DiagLevel >= DiagnosticsEngine::Error) {
184 if (++NumFailures == 1)
185 Diag(Info.getLocation(), diag::note_fixit_unfixed_error);
186 }
187 return;
188 }
189
190 if (!Editor.commit(commit)) {
191 ++NumFailures;
192 Diag(Info.getLocation(), diag::note_fixit_failed);
193 return;
194 }
195
196 Diag(Info.getLocation(), diag::note_fixit_applied);
197}
198
199/// Emit a diagnostic via the adapted diagnostic client.
200void FixItRewriter::Diag(SourceLocation Loc, unsigned DiagID) {
201 // When producing this diagnostic, we temporarily bypass ourselves,
202 // and let the downstream client format the diagnostic.
203 Diags.setClient(Client, false);
204 Diags.Report(Loc, DiagID);
205 Diags.setClient(this, false);
206}
207
Defines the Diagnostic-related interfaces.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
bool isTokenRange() const
Return true if the end of this range specifies the start of the last token.
SourceLocation getEnd() const
SourceLocation getBegin() const
virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info)
Handle this diagnostic, reporting it to the user or capturing it to a log as needed.
A little helper class (which is basically a smart pointer that forwards info from DiagnosticsEngine a...
const SourceLocation & getLocation() const
unsigned getNumFixItHints() const
const FixItHint & getFixItHint(unsigned Idx) const
Concrete class used by the front-end to report problems and issues.
Definition Diagnostic.h:231
Level
The level of the diagnostic, after it has been through mapping.
Definition Diagnostic.h:236
StringRef getName() const
The name of this FileEntry.
Definition FileEntry.h:61
An opaque identifier used by SourceManager which refers to a source file (MemoryBuffer) along with it...
Annotates a diagnostic with some code that should be inserted, removed, or replaced to fix the proble...
Definition Diagnostic.h:78
bool BeforePreviousInsertions
Definition Diagnostic.h:92
CharSourceRange RemoveRange
Code that should be replaced to correct the error.
Definition Diagnostic.h:82
CharSourceRange InsertFromRange
Code in the specific range that should be inserted in the insertion location.
Definition Diagnostic.h:86
std::string CodeToInsert
The actual code to insert at the insertion location, as a string.
Definition Diagnostic.h:90
virtual ~FixItOptions()
Rewriter::buffer_iterator iterator
void Diag(SourceLocation Loc, unsigned DiagID)
Emit a diagnostic via the adapted diagnostic client.
bool WriteFixedFiles(std::vector< std::pair< std::string, std::string > > *RewrittenFiles=nullptr)
Write the modified source files.
bool WriteFixedFile(FileID ID, raw_ostream &OS)
Write a single modified source file.
void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const Diagnostic &Info) override
HandleDiagnostic - Handle this diagnostic, reporting it to the user or capturing it to a log as neede...
FixItRewriter(DiagnosticsEngine &Diags, SourceManager &SourceMgr, const LangOptions &LangOpts, FixItOptions *FixItOpts)
Initialize a new fix-it rewriter.
~FixItRewriter() override
Destroy the fix-it rewriter.
bool IncludeInDiagnosticCounts() const override
IncludeInDiagnosticCounts - This method (whose default implementation returns true) indicates whether...
A SourceLocation and its associated SourceManager.
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Rewriter - This is the main interface to the rewrite buffers.
Definition Rewriter.h:32
Encodes a location in the source.
This class handles loading and caching of source files into memory.
bool insertFromRange(SourceLocation loc, CharSourceRange range, bool afterToken=false, bool beforePreviousInsertions=false)
Definition Commit.cpp:63
bool insert(SourceLocation loc, StringRef text, bool afterToken=false, bool beforePreviousInsertions=false)
Definition Commit.cpp:47
bool isCommitable() const
Definition Commit.h:68
bool remove(CharSourceRange range)
Definition Commit.cpp:90
bool replace(CharSourceRange range, StringRef text)
Definition Commit.cpp:115
RangeSelector range(RangeSelector Begin, RangeSelector End)
DEPRECATED. Use enclose.
The JSON file list parser is used to communicate input to InstallAPI.
CustomizableOptional< FileEntryRef > OptionalFileEntryRef
Definition FileEntry.h:208
@ Rewrite
We are substituting template parameters for (typically) other template parameters in order to rewrite...
Definition Template.h:54