diff options
Diffstat (limited to 'lib/Frontend/Rewrite/FrontendActions.cpp')
| -rw-r--r-- | lib/Frontend/Rewrite/FrontendActions.cpp | 121 | 
1 files changed, 105 insertions, 16 deletions
diff --git a/lib/Frontend/Rewrite/FrontendActions.cpp b/lib/Frontend/Rewrite/FrontendActions.cpp index b2dfd29411302..45feffbcb5b54 100644 --- a/lib/Frontend/Rewrite/FrontendActions.cpp +++ b/lib/Frontend/Rewrite/FrontendActions.cpp @@ -18,6 +18,11 @@  #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" @@ -86,8 +91,7 @@ public:  };  } // end anonymous namespace -bool FixItAction::BeginSourceFileAction(CompilerInstance &CI, -                                        StringRef Filename) { +bool FixItAction::BeginSourceFileAction(CompilerInstance &CI) {    const FrontendOptions &FEOpts = getCompilerInstance().getFrontendOpts();    if (!FEOpts.FixItSuffix.empty()) {      FixItOpts.reset(new FixItActionSuffixInserter(FEOpts.FixItSuffix, @@ -190,27 +194,112 @@ void RewriteTestAction::ExecuteAction() {    DoRewriteTest(CI.getPreprocessor(), OS.get());  } -void RewriteIncludesAction::ExecuteAction() { -  CompilerInstance &CI = getCompilerInstance(); -  std::unique_ptr<raw_ostream> OS = -      CI.createDefaultOutputFile(true, getCurrentFile()); -  if (!OS) return; +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 " << MF->ModuleName << "\n"; + +    // Rewrite the contents of the module in a separate compiler instance. +    CompilerInstance Instance(CI.getPCHContainerOperations(), +                              &CI.getPreprocessor().getPCMCache()); +    Instance.setInvocation( +        std::make_shared<CompilerInvocation>(CI.getInvocation())); +    Instance.createDiagnostics( +        new ForwardingDiagnosticConsumer(CI.getDiagnosticClient()), +        /*ShouldOwnClient=*/true); +    Instance.getFrontendOpts().Inputs.clear(); +    Instance.getFrontendOpts().Inputs.emplace_back( +        Filename, InputKind(InputKind::Unknown, InputKind::Precompiled)); +    // 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, getCurrentFile()); +    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"; +      OS << "# 1 \""; +      OS.write_escaped(Input.getFile()); +      OS << "\"\n";      } -    // FIXME: Include additional information here so that we don't need the -    // original source files to exist on disk. -    getCurrentModule()->print(*OS); -    (*OS) << "#pragma clang module contents\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());    } -  RewriteIncludesInInput(CI.getPreprocessor(), OS.get(), -                         CI.getPreprocessorOutputOpts()); +  OutputStream.reset();  }  | 
