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> 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 48e5dd7070Spatrick FixItAction::FixItAction() {} 49e5dd7070Spatrick FixItAction::~FixItAction() {} 50e5dd7070Spatrick 51e5dd7070Spatrick std::unique_ptr<ASTConsumer> 52e5dd7070Spatrick FixItAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { 53e5dd7070Spatrick return std::make_unique<ASTConsumer>(); 54e5dd7070Spatrick } 55e5dd7070Spatrick 56e5dd7070Spatrick namespace { 57e5dd7070Spatrick class FixItRewriteInPlace : public FixItOptions { 58e5dd7070Spatrick public: 59e5dd7070Spatrick FixItRewriteInPlace() { InPlace = true; } 60e5dd7070Spatrick 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: 70e5dd7070Spatrick FixItActionSuffixInserter(std::string NewSuffix, bool FixWhatYouCan) 71e5dd7070Spatrick : NewSuffix(std::move(NewSuffix)) { 72e5dd7070Spatrick this->FixWhatYouCan = FixWhatYouCan; 73e5dd7070Spatrick } 74e5dd7070Spatrick 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: 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 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 110e5dd7070Spatrick void FixItAction::EndSourceFileAction() { 111e5dd7070Spatrick // Otherwise rewrite all files. 112e5dd7070Spatrick Rewriter->WriteFixedFiles(); 113e5dd7070Spatrick } 114e5dd7070Spatrick 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> 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 185e5dd7070Spatrick void RewriteMacrosAction::ExecuteAction() { 186e5dd7070Spatrick CompilerInstance &CI = getCompilerInstance(); 187e5dd7070Spatrick std::unique_ptr<raw_ostream> OS = 188*a9ac8606Spatrick CI.createDefaultOutputFile(/*Binary=*/true, getCurrentFileOrBufferName()); 189e5dd7070Spatrick if (!OS) return; 190e5dd7070Spatrick 191e5dd7070Spatrick RewriteMacrosInInput(CI.getPreprocessor(), OS.get()); 192e5dd7070Spatrick } 193e5dd7070Spatrick 194e5dd7070Spatrick void RewriteTestAction::ExecuteAction() { 195e5dd7070Spatrick CompilerInstance &CI = getCompilerInstance(); 196e5dd7070Spatrick std::unique_ptr<raw_ostream> OS = 197*a9ac8606Spatrick 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: 210e5dd7070Spatrick RewriteImportsListener(CompilerInstance &CI, std::shared_ptr<raw_ostream> Out) 211e5dd7070Spatrick : CI(CI), Out(Out) {} 212e5dd7070Spatrick 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 "; 234e5dd7070Spatrick if (isValidIdentifier(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 270e5dd7070Spatrick bool RewriteIncludesAction::BeginSourceFileAction(CompilerInstance &CI) { 271e5dd7070Spatrick if (!OutputStream) { 272e5dd7070Spatrick OutputStream = 273*a9ac8606Spatrick 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 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