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