//===--- 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/Basic/LangStandard.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/ModuleFile.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 #include using namespace clang; //===----------------------------------------------------------------------===// // AST Consumer Actions //===----------------------------------------------------------------------===// std::unique_ptr HTMLPrintAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { if (std::unique_ptr OS = CI.createDefaultOutputFile(false, InFile)) return CreateHTMLPrinter(std::move(OS), CI.getPreprocessor()); return nullptr; } FixItAction::FixItAction() {} FixItAction::~FixItAction() {} std::unique_ptr FixItAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { return std::make_unique(); } 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 std::string(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 std::string(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 > RewrittenFiles; bool err = false; { const FrontendOptions &FEOpts = CI.getFrontendOpts(); std::unique_ptr FixAction(new SyntaxOnlyAction()); if (FixAction->BeginSourceFile(CI, FEOpts.Inputs[0])) { std::unique_ptr 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()); if (llvm::Error Err = FixAction->Execute()) { // FIXME this drops the error on the floor. consumeError(std::move(Err)); return false; } 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 RewriteObjCAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { if (std::unique_ptr OS = CI.createDefaultOutputFile(false, InFile, "cpp")) { if (CI.getLangOpts().ObjCRuntime.isNonFragile()) return CreateModernObjCRewriter( std::string(InFile), std::move(OS), CI.getDiagnostics(), CI.getLangOpts(), CI.getDiagnosticOpts().NoRewriteMacros, (CI.getCodeGenOpts().getDebugInfo() != codegenoptions::NoDebugInfo)); return CreateObjCRewriter(std::string(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 OS = CI.createDefaultOutputFile(/*Binary=*/true, getCurrentFileOrBufferName()); if (!OS) return; RewriteMacrosInInput(CI.getPreprocessor(), OS.get()); } void RewriteTestAction::ExecuteAction() { CompilerInstance &CI = getCompilerInstance(); std::unique_ptr OS = CI.createDefaultOutputFile(/*Binary=*/false, getCurrentFileOrBufferName()); if (!OS) return; DoRewriteTest(CI.getPreprocessor(), OS.get()); } class RewriteIncludesAction::RewriteImportsListener : public ASTReaderListener { CompilerInstance &CI; std::weak_ptr Out; llvm::DenseSet Rewritten; public: RewriteImportsListener(CompilerInstance &CI, std::shared_ptr 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.getASTReader()->getModuleManager().lookup(*File); assert(MF && "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 (isValidAsciiIdentifier(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(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(Language::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(/*Binary=*/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.createASTReader(); CI.getASTReader()->addListener( std::make_unique(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(); }