1 | //===--- FrontendActions.cpp ----------------------------------------------===// |
---|---|
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 | #include "clang/Rewrite/Frontend/FrontendActions.h" |
10 | #include "clang/AST/ASTConsumer.h" |
11 | #include "clang/Basic/CharInfo.h" |
12 | #include "clang/Basic/LangStandard.h" |
13 | #include "clang/Config/config.h" |
14 | #include "clang/Frontend/CompilerInstance.h" |
15 | #include "clang/Frontend/FrontendActions.h" |
16 | #include "clang/Frontend/Utils.h" |
17 | #include "clang/Lex/Preprocessor.h" |
18 | #include "clang/Lex/PreprocessorOptions.h" |
19 | #include "clang/Rewrite/Frontend/ASTConsumers.h" |
20 | #include "clang/Rewrite/Frontend/FixItRewriter.h" |
21 | #include "clang/Rewrite/Frontend/Rewriters.h" |
22 | #include "clang/Serialization/ASTReader.h" |
23 | #include "clang/Serialization/ModuleFile.h" |
24 | #include "clang/Serialization/ModuleManager.h" |
25 | #include "llvm/ADT/DenseSet.h" |
26 | #include "llvm/Support/CrashRecoveryContext.h" |
27 | #include "llvm/Support/FileSystem.h" |
28 | #include "llvm/Support/Path.h" |
29 | #include "llvm/Support/raw_ostream.h" |
30 | #include <memory> |
31 | #include <utility> |
32 | |
33 | using namespace clang; |
34 | |
35 | //===----------------------------------------------------------------------===// |
36 | // AST Consumer Actions |
37 | //===----------------------------------------------------------------------===// |
38 | |
39 | std::unique_ptr<ASTConsumer> |
40 | HTMLPrintAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { |
41 | if (std::unique_ptr<raw_ostream> OS = |
42 | CI.createDefaultOutputFile(Binary: false, BaseInput: InFile)) |
43 | return CreateHTMLPrinter(OS: std::move(OS), PP&: CI.getPreprocessor()); |
44 | return nullptr; |
45 | } |
46 | |
47 | FixItAction::FixItAction() {} |
48 | FixItAction::~FixItAction() {} |
49 | |
50 | std::unique_ptr<ASTConsumer> |
51 | FixItAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { |
52 | return std::make_unique<ASTConsumer>(); |
53 | } |
54 | |
55 | namespace { |
56 | class FixItRewriteInPlace : public FixItOptions { |
57 | public: |
58 | FixItRewriteInPlace() { InPlace = true; } |
59 | |
60 | std::string RewriteFilename(const std::string &Filename, int &fd) override { |
61 | llvm_unreachable("don't call RewriteFilename for inplace rewrites"); |
62 | } |
63 | }; |
64 | |
65 | class FixItActionSuffixInserter : public FixItOptions { |
66 | std::string NewSuffix; |
67 | |
68 | public: |
69 | FixItActionSuffixInserter(std::string NewSuffix, bool FixWhatYouCan) |
70 | : NewSuffix(std::move(NewSuffix)) { |
71 | this->FixWhatYouCan = FixWhatYouCan; |
72 | } |
73 | |
74 | std::string RewriteFilename(const std::string &Filename, int &fd) override { |
75 | fd = -1; |
76 | SmallString<128> Path(Filename); |
77 | llvm::sys::path::replace_extension(path&: Path, |
78 | extension: NewSuffix + llvm::sys::path::extension(path: Path)); |
79 | return std::string(Path); |
80 | } |
81 | }; |
82 | |
83 | class FixItRewriteToTemp : public FixItOptions { |
84 | public: |
85 | std::string RewriteFilename(const std::string &Filename, int &fd) override { |
86 | SmallString<128> Path; |
87 | llvm::sys::fs::createTemporaryFile(Prefix: llvm::sys::path::filename(path: Filename), |
88 | Suffix: llvm::sys::path::extension(path: Filename).drop_front(), ResultFD&: fd, |
89 | ResultPath&: Path); |
90 | return std::string(Path); |
91 | } |
92 | }; |
93 | } // end anonymous namespace |
94 | |
95 | bool FixItAction::BeginSourceFileAction(CompilerInstance &CI) { |
96 | const FrontendOptions &FEOpts = getCompilerInstance().getFrontendOpts(); |
97 | if (!FEOpts.FixItSuffix.empty()) { |
98 | FixItOpts.reset(p: new FixItActionSuffixInserter(FEOpts.FixItSuffix, |
99 | FEOpts.FixWhatYouCan)); |
100 | } else { |
101 | FixItOpts.reset(p: new FixItRewriteInPlace); |
102 | FixItOpts->FixWhatYouCan = FEOpts.FixWhatYouCan; |
103 | } |
104 | Rewriter.reset(p: new FixItRewriter(CI.getDiagnostics(), CI.getSourceManager(), |
105 | CI.getLangOpts(), FixItOpts.get())); |
106 | return true; |
107 | } |
108 | |
109 | void FixItAction::EndSourceFileAction() { |
110 | // Otherwise rewrite all files. |
111 | Rewriter->WriteFixedFiles(); |
112 | } |
113 | |
114 | bool FixItRecompile::BeginInvocation(CompilerInstance &CI) { |
115 | |
116 | std::vector<std::pair<std::string, std::string> > RewrittenFiles; |
117 | bool err = false; |
118 | { |
119 | const FrontendOptions &FEOpts = CI.getFrontendOpts(); |
120 | std::unique_ptr<FrontendAction> FixAction(new SyntaxOnlyAction()); |
121 | if (FixAction->BeginSourceFile(CI, Input: FEOpts.Inputs[0])) { |
122 | std::unique_ptr<FixItOptions> FixItOpts; |
123 | if (FEOpts.FixToTemporaries) |
124 | FixItOpts.reset(p: new FixItRewriteToTemp()); |
125 | else |
126 | FixItOpts.reset(p: new FixItRewriteInPlace()); |
127 | FixItOpts->Silent = true; |
128 | FixItOpts->FixWhatYouCan = FEOpts.FixWhatYouCan; |
129 | FixItOpts->FixOnlyWarnings = FEOpts.FixOnlyWarnings; |
130 | FixItRewriter Rewriter(CI.getDiagnostics(), CI.getSourceManager(), |
131 | CI.getLangOpts(), FixItOpts.get()); |
132 | if (llvm::Error Err = FixAction->Execute()) { |
133 | // FIXME this drops the error on the floor. |
134 | consumeError(Err: std::move(Err)); |
135 | return false; |
136 | } |
137 | |
138 | err = Rewriter.WriteFixedFiles(RewrittenFiles: &RewrittenFiles); |
139 | |
140 | FixAction->EndSourceFile(); |
141 | CI.setSourceManager(nullptr); |
142 | CI.setFileManager(nullptr); |
143 | } else { |
144 | err = true; |
145 | } |
146 | } |
147 | if (err) |
148 | return false; |
149 | CI.getDiagnosticClient().clear(); |
150 | CI.getDiagnostics().Reset(); |
151 | |
152 | PreprocessorOptions &PPOpts = CI.getPreprocessorOpts(); |
153 | PPOpts.RemappedFiles.insert(position: PPOpts.RemappedFiles.end(), |
154 | first: RewrittenFiles.begin(), last: RewrittenFiles.end()); |
155 | PPOpts.RemappedFilesKeepOriginalName = false; |
156 | |
157 | return true; |
158 | } |
159 | |
160 | #if CLANG_ENABLE_OBJC_REWRITER |
161 | |
162 | std::unique_ptr<ASTConsumer> |
163 | RewriteObjCAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { |
164 | if (std::unique_ptr<raw_ostream> OS = |
165 | CI.createDefaultOutputFile(false, InFile, "cpp")) { |
166 | if (CI.getLangOpts().ObjCRuntime.isNonFragile()) |
167 | return CreateModernObjCRewriter(std::string(InFile), std::move(OS), |
168 | CI.getDiagnostics(), CI.getLangOpts(), |
169 | CI.getDiagnosticOpts().NoRewriteMacros, |
170 | (CI.getCodeGenOpts().getDebugInfo() != |
171 | llvm::codegenoptions::NoDebugInfo)); |
172 | return CreateObjCRewriter(std::string(InFile), std::move(OS), |
173 | CI.getDiagnostics(), CI.getLangOpts(), |
174 | CI.getDiagnosticOpts().NoRewriteMacros); |
175 | } |
176 | return nullptr; |
177 | } |
178 | |
179 | #endif |
180 | |
181 | //===----------------------------------------------------------------------===// |
182 | // Preprocessor Actions |
183 | //===----------------------------------------------------------------------===// |
184 | |
185 | void RewriteMacrosAction::ExecuteAction() { |
186 | CompilerInstance &CI = getCompilerInstance(); |
187 | std::unique_ptr<raw_ostream> OS = |
188 | CI.createDefaultOutputFile(/*Binary=*/true, BaseInput: getCurrentFileOrBufferName()); |
189 | if (!OS) return; |
190 | |
191 | RewriteMacrosInInput(PP&: CI.getPreprocessor(), OS: OS.get()); |
192 | } |
193 | |
194 | void RewriteTestAction::ExecuteAction() { |
195 | CompilerInstance &CI = getCompilerInstance(); |
196 | std::unique_ptr<raw_ostream> OS = |
197 | CI.createDefaultOutputFile(/*Binary=*/false, BaseInput: getCurrentFileOrBufferName()); |
198 | if (!OS) return; |
199 | |
200 | DoRewriteTest(PP&: CI.getPreprocessor(), OS: OS.get()); |
201 | } |
202 | |
203 | class RewriteIncludesAction::RewriteImportsListener : public ASTReaderListener { |
204 | CompilerInstance &CI; |
205 | std::weak_ptr<raw_ostream> Out; |
206 | |
207 | llvm::DenseSet<const FileEntry*> Rewritten; |
208 | |
209 | public: |
210 | RewriteImportsListener(CompilerInstance &CI, std::shared_ptr<raw_ostream> Out) |
211 | : CI(CI), Out(Out) {} |
212 | |
213 | void visitModuleFile(StringRef Filename, |
214 | serialization::ModuleKind Kind) override { |
215 | auto File = CI.getFileManager().getOptionalFileRef(Filename); |
216 | assert(File && "missing file for loaded module?"); |
217 | |
218 | // Only rewrite each module file once. |
219 | if (!Rewritten.insert(V: *File).second) |
220 | return; |
221 | |
222 | serialization::ModuleFile *MF = |
223 | CI.getASTReader()->getModuleManager().lookup(File: *File); |
224 | assert(MF && "missing module file for loaded module?"); |
225 | |
226 | // Not interested in PCH / preambles. |
227 | if (!MF->isModule()) |
228 | return; |
229 | |
230 | auto OS = Out.lock(); |
231 | assert(OS && "loaded module file after finishing rewrite action?"); |
232 | |
233 | (*OS) << "#pragma clang module build "; |
234 | if (isValidAsciiIdentifier(S: MF->ModuleName)) |
235 | (*OS) << MF->ModuleName; |
236 | else { |
237 | (*OS) << '"'; |
238 | OS->write_escaped(Str: MF->ModuleName); |
239 | (*OS) << '"'; |
240 | } |
241 | (*OS) << '\n'; |
242 | |
243 | // Rewrite the contents of the module in a separate compiler instance. |
244 | CompilerInstance Instance( |
245 | std::make_shared<CompilerInvocation>(args&: CI.getInvocation()), |
246 | CI.getPCHContainerOperations(), &CI.getModuleCache()); |
247 | Instance.createDiagnostics( |
248 | VFS&: CI.getVirtualFileSystem(), |
249 | Client: new ForwardingDiagnosticConsumer(CI.getDiagnosticClient()), |
250 | /*ShouldOwnClient=*/true); |
251 | Instance.getFrontendOpts().DisableFree = false; |
252 | Instance.getFrontendOpts().Inputs.clear(); |
253 | Instance.getFrontendOpts().Inputs.emplace_back( |
254 | Args&: Filename, Args: InputKind(Language::Unknown, InputKind::Precompiled)); |
255 | Instance.getFrontendOpts().ModuleFiles.clear(); |
256 | Instance.getFrontendOpts().ModuleMapFiles.clear(); |
257 | // Don't recursively rewrite imports. We handle them all at the top level. |
258 | Instance.getPreprocessorOutputOpts().RewriteImports = false; |
259 | |
260 | llvm::CrashRecoveryContext().RunSafelyOnThread([&]() { |
261 | RewriteIncludesAction Action; |
262 | Action.OutputStream = OS; |
263 | Instance.ExecuteAction(Act&: Action); |
264 | }); |
265 | |
266 | (*OS) << "#pragma clang module endbuild /*"<< MF->ModuleName << "*/\n"; |
267 | } |
268 | }; |
269 | |
270 | bool RewriteIncludesAction::BeginSourceFileAction(CompilerInstance &CI) { |
271 | if (!OutputStream) { |
272 | OutputStream = |
273 | CI.createDefaultOutputFile(/*Binary=*/true, BaseInput: getCurrentFileOrBufferName()); |
274 | if (!OutputStream) |
275 | return false; |
276 | } |
277 | |
278 | auto &OS = *OutputStream; |
279 | |
280 | // If we're preprocessing a module map, start by dumping the contents of the |
281 | // module itself before switching to the input buffer. |
282 | auto &Input = getCurrentInput(); |
283 | if (Input.getKind().getFormat() == InputKind::ModuleMap) { |
284 | if (Input.isFile()) { |
285 | OS << "# 1 \""; |
286 | OS.write_escaped(Str: Input.getFile()); |
287 | OS << "\"\n"; |
288 | } |
289 | getCurrentModule()->print(OS); |
290 | OS << "#pragma clang module contents\n"; |
291 | } |
292 | |
293 | // If we're rewriting imports, set up a listener to track when we import |
294 | // module files. |
295 | if (CI.getPreprocessorOutputOpts().RewriteImports) { |
296 | CI.createASTReader(); |
297 | CI.getASTReader()->addListener( |
298 | L: std::make_unique<RewriteImportsListener>(args&: CI, args&: OutputStream)); |
299 | } |
300 | |
301 | return true; |
302 | } |
303 | |
304 | void RewriteIncludesAction::ExecuteAction() { |
305 | CompilerInstance &CI = getCompilerInstance(); |
306 | |
307 | // If we're rewriting imports, emit the module build output first rather |
308 | // than switching back and forth (potentially in the middle of a line). |
309 | if (CI.getPreprocessorOutputOpts().RewriteImports) { |
310 | std::string Buffer; |
311 | llvm::raw_string_ostream OS(Buffer); |
312 | |
313 | RewriteIncludesInInput(PP&: CI.getPreprocessor(), OS: &OS, |
314 | Opts: CI.getPreprocessorOutputOpts()); |
315 | |
316 | (*OutputStream) << OS.str(); |
317 | } else { |
318 | RewriteIncludesInInput(PP&: CI.getPreprocessor(), OS: OutputStream.get(), |
319 | Opts: CI.getPreprocessorOutputOpts()); |
320 | } |
321 | |
322 | OutputStream.reset(); |
323 | } |
324 |
Definitions
- CreateASTConsumer
- FixItAction
- ~FixItAction
- CreateASTConsumer
- FixItRewriteInPlace
- FixItRewriteInPlace
- RewriteFilename
- FixItActionSuffixInserter
- FixItActionSuffixInserter
- RewriteFilename
- FixItRewriteToTemp
- RewriteFilename
- BeginSourceFileAction
- EndSourceFileAction
- BeginInvocation
- ExecuteAction
- ExecuteAction
- RewriteImportsListener
- RewriteImportsListener
- visitModuleFile
- BeginSourceFileAction
Improve your Profiling and Debugging skills
Find out more