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