diff options
Diffstat (limited to 'clang/lib/Frontend/Rewrite/FrontendActions.cpp')
| -rw-r--r-- | clang/lib/Frontend/Rewrite/FrontendActions.cpp | 323 | 
1 files changed, 323 insertions, 0 deletions
diff --git a/clang/lib/Frontend/Rewrite/FrontendActions.cpp b/clang/lib/Frontend/Rewrite/FrontendActions.cpp new file mode 100644 index 000000000000..549b86edebcd --- /dev/null +++ b/clang/lib/Frontend/Rewrite/FrontendActions.cpp @@ -0,0 +1,323 @@ +//===--- 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/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 std::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()); +      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<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(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 (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(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(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( +        std::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(); +}  | 
