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