mirror of
https://github.com/llvm/llvm-project.git
synced 2025-05-08 05:26:07 +00:00

Change MemoryBufferCache to InMemoryModuleCache, moving it from Basic to Serialization. Another patch will start using it to manage module build more explicitly, but this is split out because it's mostly mechanical. Because of the move to Serialization we can no longer abuse the Preprocessor to forward it to the ASTReader. Besides the rename and file move, that means Preprocessor::Preprocessor has one fewer parameter and ASTReader::ASTReader has one more. llvm-svn: 355777
319 lines
11 KiB
C++
319 lines
11 KiB
C++
//===--- FrontendActions.cpp ----------------------------------------------===//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/Rewrite/Frontend/FrontendActions.h"
|
|
#include "clang/AST/ASTConsumer.h"
|
|
#include "clang/Basic/CharInfo.h"
|
|
#include "clang/Config/config.h"
|
|
#include "clang/Frontend/CompilerInstance.h"
|
|
#include "clang/Frontend/FrontendActions.h"
|
|
#include "clang/Frontend/FrontendDiagnostic.h"
|
|
#include "clang/Frontend/Utils.h"
|
|
#include "clang/Lex/Preprocessor.h"
|
|
#include "clang/Lex/PreprocessorOptions.h"
|
|
#include "clang/Rewrite/Frontend/ASTConsumers.h"
|
|
#include "clang/Rewrite/Frontend/FixItRewriter.h"
|
|
#include "clang/Rewrite/Frontend/Rewriters.h"
|
|
#include "clang/Serialization/ASTReader.h"
|
|
#include "clang/Serialization/Module.h"
|
|
#include "clang/Serialization/ModuleManager.h"
|
|
#include "llvm/ADT/DenseSet.h"
|
|
#include "llvm/Support/CrashRecoveryContext.h"
|
|
#include "llvm/Support/FileSystem.h"
|
|
#include "llvm/Support/Path.h"
|
|
#include "llvm/Support/raw_ostream.h"
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
using namespace clang;
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// AST Consumer Actions
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
std::unique_ptr<ASTConsumer>
|
|
HTMLPrintAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
|
|
if (std::unique_ptr<raw_ostream> OS =
|
|
CI.createDefaultOutputFile(false, InFile))
|
|
return CreateHTMLPrinter(std::move(OS), CI.getPreprocessor());
|
|
return nullptr;
|
|
}
|
|
|
|
FixItAction::FixItAction() {}
|
|
FixItAction::~FixItAction() {}
|
|
|
|
std::unique_ptr<ASTConsumer>
|
|
FixItAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
|
|
return llvm::make_unique<ASTConsumer>();
|
|
}
|
|
|
|
namespace {
|
|
class FixItRewriteInPlace : public FixItOptions {
|
|
public:
|
|
FixItRewriteInPlace() { InPlace = true; }
|
|
|
|
std::string RewriteFilename(const std::string &Filename, int &fd) override {
|
|
llvm_unreachable("don't call RewriteFilename for inplace rewrites");
|
|
}
|
|
};
|
|
|
|
class FixItActionSuffixInserter : public FixItOptions {
|
|
std::string NewSuffix;
|
|
|
|
public:
|
|
FixItActionSuffixInserter(std::string NewSuffix, bool FixWhatYouCan)
|
|
: NewSuffix(std::move(NewSuffix)) {
|
|
this->FixWhatYouCan = FixWhatYouCan;
|
|
}
|
|
|
|
std::string RewriteFilename(const std::string &Filename, int &fd) override {
|
|
fd = -1;
|
|
SmallString<128> Path(Filename);
|
|
llvm::sys::path::replace_extension(Path,
|
|
NewSuffix + llvm::sys::path::extension(Path));
|
|
return Path.str();
|
|
}
|
|
};
|
|
|
|
class FixItRewriteToTemp : public FixItOptions {
|
|
public:
|
|
std::string RewriteFilename(const std::string &Filename, int &fd) override {
|
|
SmallString<128> Path;
|
|
llvm::sys::fs::createTemporaryFile(llvm::sys::path::filename(Filename),
|
|
llvm::sys::path::extension(Filename).drop_front(), fd,
|
|
Path);
|
|
return Path.str();
|
|
}
|
|
};
|
|
} // end anonymous namespace
|
|
|
|
bool FixItAction::BeginSourceFileAction(CompilerInstance &CI) {
|
|
const FrontendOptions &FEOpts = getCompilerInstance().getFrontendOpts();
|
|
if (!FEOpts.FixItSuffix.empty()) {
|
|
FixItOpts.reset(new FixItActionSuffixInserter(FEOpts.FixItSuffix,
|
|
FEOpts.FixWhatYouCan));
|
|
} else {
|
|
FixItOpts.reset(new FixItRewriteInPlace);
|
|
FixItOpts->FixWhatYouCan = FEOpts.FixWhatYouCan;
|
|
}
|
|
Rewriter.reset(new FixItRewriter(CI.getDiagnostics(), CI.getSourceManager(),
|
|
CI.getLangOpts(), FixItOpts.get()));
|
|
return true;
|
|
}
|
|
|
|
void FixItAction::EndSourceFileAction() {
|
|
// Otherwise rewrite all files.
|
|
Rewriter->WriteFixedFiles();
|
|
}
|
|
|
|
bool FixItRecompile::BeginInvocation(CompilerInstance &CI) {
|
|
|
|
std::vector<std::pair<std::string, std::string> > RewrittenFiles;
|
|
bool err = false;
|
|
{
|
|
const FrontendOptions &FEOpts = CI.getFrontendOpts();
|
|
std::unique_ptr<FrontendAction> FixAction(new SyntaxOnlyAction());
|
|
if (FixAction->BeginSourceFile(CI, FEOpts.Inputs[0])) {
|
|
std::unique_ptr<FixItOptions> FixItOpts;
|
|
if (FEOpts.FixToTemporaries)
|
|
FixItOpts.reset(new FixItRewriteToTemp());
|
|
else
|
|
FixItOpts.reset(new FixItRewriteInPlace());
|
|
FixItOpts->Silent = true;
|
|
FixItOpts->FixWhatYouCan = FEOpts.FixWhatYouCan;
|
|
FixItOpts->FixOnlyWarnings = FEOpts.FixOnlyWarnings;
|
|
FixItRewriter Rewriter(CI.getDiagnostics(), CI.getSourceManager(),
|
|
CI.getLangOpts(), FixItOpts.get());
|
|
FixAction->Execute();
|
|
|
|
err = Rewriter.WriteFixedFiles(&RewrittenFiles);
|
|
|
|
FixAction->EndSourceFile();
|
|
CI.setSourceManager(nullptr);
|
|
CI.setFileManager(nullptr);
|
|
} else {
|
|
err = true;
|
|
}
|
|
}
|
|
if (err)
|
|
return false;
|
|
CI.getDiagnosticClient().clear();
|
|
CI.getDiagnostics().Reset();
|
|
|
|
PreprocessorOptions &PPOpts = CI.getPreprocessorOpts();
|
|
PPOpts.RemappedFiles.insert(PPOpts.RemappedFiles.end(),
|
|
RewrittenFiles.begin(), RewrittenFiles.end());
|
|
PPOpts.RemappedFilesKeepOriginalName = false;
|
|
|
|
return true;
|
|
}
|
|
|
|
#if CLANG_ENABLE_OBJC_REWRITER
|
|
|
|
std::unique_ptr<ASTConsumer>
|
|
RewriteObjCAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
|
|
if (std::unique_ptr<raw_ostream> OS =
|
|
CI.createDefaultOutputFile(false, InFile, "cpp")) {
|
|
if (CI.getLangOpts().ObjCRuntime.isNonFragile())
|
|
return CreateModernObjCRewriter(
|
|
InFile, std::move(OS), CI.getDiagnostics(), CI.getLangOpts(),
|
|
CI.getDiagnosticOpts().NoRewriteMacros,
|
|
(CI.getCodeGenOpts().getDebugInfo() != codegenoptions::NoDebugInfo));
|
|
return CreateObjCRewriter(InFile, std::move(OS), CI.getDiagnostics(),
|
|
CI.getLangOpts(),
|
|
CI.getDiagnosticOpts().NoRewriteMacros);
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
#endif
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
// Preprocessor Actions
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
void RewriteMacrosAction::ExecuteAction() {
|
|
CompilerInstance &CI = getCompilerInstance();
|
|
std::unique_ptr<raw_ostream> OS =
|
|
CI.createDefaultOutputFile(true, getCurrentFileOrBufferName());
|
|
if (!OS) return;
|
|
|
|
RewriteMacrosInInput(CI.getPreprocessor(), OS.get());
|
|
}
|
|
|
|
void RewriteTestAction::ExecuteAction() {
|
|
CompilerInstance &CI = getCompilerInstance();
|
|
std::unique_ptr<raw_ostream> OS =
|
|
CI.createDefaultOutputFile(false, getCurrentFileOrBufferName());
|
|
if (!OS) return;
|
|
|
|
DoRewriteTest(CI.getPreprocessor(), OS.get());
|
|
}
|
|
|
|
class RewriteIncludesAction::RewriteImportsListener : public ASTReaderListener {
|
|
CompilerInstance &CI;
|
|
std::weak_ptr<raw_ostream> Out;
|
|
|
|
llvm::DenseSet<const FileEntry*> Rewritten;
|
|
|
|
public:
|
|
RewriteImportsListener(CompilerInstance &CI, std::shared_ptr<raw_ostream> Out)
|
|
: CI(CI), Out(Out) {}
|
|
|
|
void visitModuleFile(StringRef Filename,
|
|
serialization::ModuleKind Kind) override {
|
|
auto *File = CI.getFileManager().getFile(Filename);
|
|
assert(File && "missing file for loaded module?");
|
|
|
|
// Only rewrite each module file once.
|
|
if (!Rewritten.insert(File).second)
|
|
return;
|
|
|
|
serialization::ModuleFile *MF =
|
|
CI.getModuleManager()->getModuleManager().lookup(File);
|
|
assert(File && "missing module file for loaded module?");
|
|
|
|
// Not interested in PCH / preambles.
|
|
if (!MF->isModule())
|
|
return;
|
|
|
|
auto OS = Out.lock();
|
|
assert(OS && "loaded module file after finishing rewrite action?");
|
|
|
|
(*OS) << "#pragma clang module build ";
|
|
if (isValidIdentifier(MF->ModuleName))
|
|
(*OS) << MF->ModuleName;
|
|
else {
|
|
(*OS) << '"';
|
|
OS->write_escaped(MF->ModuleName);
|
|
(*OS) << '"';
|
|
}
|
|
(*OS) << '\n';
|
|
|
|
// Rewrite the contents of the module in a separate compiler instance.
|
|
CompilerInstance Instance(CI.getPCHContainerOperations(),
|
|
&CI.getModuleCache());
|
|
Instance.setInvocation(
|
|
std::make_shared<CompilerInvocation>(CI.getInvocation()));
|
|
Instance.createDiagnostics(
|
|
new ForwardingDiagnosticConsumer(CI.getDiagnosticClient()),
|
|
/*ShouldOwnClient=*/true);
|
|
Instance.getFrontendOpts().DisableFree = false;
|
|
Instance.getFrontendOpts().Inputs.clear();
|
|
Instance.getFrontendOpts().Inputs.emplace_back(
|
|
Filename, InputKind(InputKind::Unknown, InputKind::Precompiled));
|
|
Instance.getFrontendOpts().ModuleFiles.clear();
|
|
Instance.getFrontendOpts().ModuleMapFiles.clear();
|
|
// Don't recursively rewrite imports. We handle them all at the top level.
|
|
Instance.getPreprocessorOutputOpts().RewriteImports = false;
|
|
|
|
llvm::CrashRecoveryContext().RunSafelyOnThread([&]() {
|
|
RewriteIncludesAction Action;
|
|
Action.OutputStream = OS;
|
|
Instance.ExecuteAction(Action);
|
|
});
|
|
|
|
(*OS) << "#pragma clang module endbuild /*" << MF->ModuleName << "*/\n";
|
|
}
|
|
};
|
|
|
|
bool RewriteIncludesAction::BeginSourceFileAction(CompilerInstance &CI) {
|
|
if (!OutputStream) {
|
|
OutputStream =
|
|
CI.createDefaultOutputFile(true, getCurrentFileOrBufferName());
|
|
if (!OutputStream)
|
|
return false;
|
|
}
|
|
|
|
auto &OS = *OutputStream;
|
|
|
|
// If we're preprocessing a module map, start by dumping the contents of the
|
|
// module itself before switching to the input buffer.
|
|
auto &Input = getCurrentInput();
|
|
if (Input.getKind().getFormat() == InputKind::ModuleMap) {
|
|
if (Input.isFile()) {
|
|
OS << "# 1 \"";
|
|
OS.write_escaped(Input.getFile());
|
|
OS << "\"\n";
|
|
}
|
|
getCurrentModule()->print(OS);
|
|
OS << "#pragma clang module contents\n";
|
|
}
|
|
|
|
// If we're rewriting imports, set up a listener to track when we import
|
|
// module files.
|
|
if (CI.getPreprocessorOutputOpts().RewriteImports) {
|
|
CI.createModuleManager();
|
|
CI.getModuleManager()->addListener(
|
|
llvm::make_unique<RewriteImportsListener>(CI, OutputStream));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void RewriteIncludesAction::ExecuteAction() {
|
|
CompilerInstance &CI = getCompilerInstance();
|
|
|
|
// If we're rewriting imports, emit the module build output first rather
|
|
// than switching back and forth (potentially in the middle of a line).
|
|
if (CI.getPreprocessorOutputOpts().RewriteImports) {
|
|
std::string Buffer;
|
|
llvm::raw_string_ostream OS(Buffer);
|
|
|
|
RewriteIncludesInInput(CI.getPreprocessor(), &OS,
|
|
CI.getPreprocessorOutputOpts());
|
|
|
|
(*OutputStream) << OS.str();
|
|
} else {
|
|
RewriteIncludesInInput(CI.getPreprocessor(), OutputStream.get(),
|
|
CI.getPreprocessorOutputOpts());
|
|
}
|
|
|
|
OutputStream.reset();
|
|
}
|