Home | History | Annotate | Line # | Download | only in Rewrite
      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-exception
      6 //
      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/FrontendDiagnostic.h"
     17 #include "clang/Frontend/Utils.h"
     18 #include "clang/Lex/Preprocessor.h"
     19 #include "clang/Lex/PreprocessorOptions.h"
     20 #include "clang/Rewrite/Frontend/ASTConsumers.h"
     21 #include "clang/Rewrite/Frontend/FixItRewriter.h"
     22 #include "clang/Rewrite/Frontend/Rewriters.h"
     23 #include "clang/Serialization/ASTReader.h"
     24 #include "clang/Serialization/ModuleFile.h"
     25 #include "clang/Serialization/ModuleManager.h"
     26 #include "llvm/ADT/DenseSet.h"
     27 #include "llvm/Support/CrashRecoveryContext.h"
     28 #include "llvm/Support/FileSystem.h"
     29 #include "llvm/Support/Path.h"
     30 #include "llvm/Support/raw_ostream.h"
     31 #include <memory>
     32 #include <utility>
     33 
     34 using namespace clang;
     35 
     36 //===----------------------------------------------------------------------===//
     37 // AST Consumer Actions
     38 //===----------------------------------------------------------------------===//
     39 
     40 std::unique_ptr<ASTConsumer>
     41 HTMLPrintAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
     42   if (std::unique_ptr<raw_ostream> OS =
     43           CI.createDefaultOutputFile(false, InFile))
     44     return CreateHTMLPrinter(std::move(OS), CI.getPreprocessor());
     45   return nullptr;
     46 }
     47 
     48 FixItAction::FixItAction() {}
     49 FixItAction::~FixItAction() {}
     50 
     51 std::unique_ptr<ASTConsumer>
     52 FixItAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
     53   return std::make_unique<ASTConsumer>();
     54 }
     55 
     56 namespace {
     57 class FixItRewriteInPlace : public FixItOptions {
     58 public:
     59   FixItRewriteInPlace() { InPlace = true; }
     60 
     61   std::string RewriteFilename(const std::string &Filename, int &fd) override {
     62     llvm_unreachable("don't call RewriteFilename for inplace rewrites");
     63   }
     64 };
     65 
     66 class FixItActionSuffixInserter : public FixItOptions {
     67   std::string NewSuffix;
     68 
     69 public:
     70   FixItActionSuffixInserter(std::string NewSuffix, bool FixWhatYouCan)
     71       : NewSuffix(std::move(NewSuffix)) {
     72     this->FixWhatYouCan = FixWhatYouCan;
     73   }
     74 
     75   std::string RewriteFilename(const std::string &Filename, int &fd) override {
     76     fd = -1;
     77     SmallString<128> Path(Filename);
     78     llvm::sys::path::replace_extension(Path,
     79       NewSuffix + llvm::sys::path::extension(Path));
     80     return std::string(Path.str());
     81   }
     82 };
     83 
     84 class FixItRewriteToTemp : public FixItOptions {
     85 public:
     86   std::string RewriteFilename(const std::string &Filename, int &fd) override {
     87     SmallString<128> Path;
     88     llvm::sys::fs::createTemporaryFile(llvm::sys::path::filename(Filename),
     89                                        llvm::sys::path::extension(Filename).drop_front(), fd,
     90                                        Path);
     91     return std::string(Path.str());
     92   }
     93 };
     94 } // end anonymous namespace
     95 
     96 bool FixItAction::BeginSourceFileAction(CompilerInstance &CI) {
     97   const FrontendOptions &FEOpts = getCompilerInstance().getFrontendOpts();
     98   if (!FEOpts.FixItSuffix.empty()) {
     99     FixItOpts.reset(new FixItActionSuffixInserter(FEOpts.FixItSuffix,
    100                                                   FEOpts.FixWhatYouCan));
    101   } else {
    102     FixItOpts.reset(new FixItRewriteInPlace);
    103     FixItOpts->FixWhatYouCan = FEOpts.FixWhatYouCan;
    104   }
    105   Rewriter.reset(new FixItRewriter(CI.getDiagnostics(), CI.getSourceManager(),
    106                                    CI.getLangOpts(), FixItOpts.get()));
    107   return true;
    108 }
    109 
    110 void FixItAction::EndSourceFileAction() {
    111   // Otherwise rewrite all files.
    112   Rewriter->WriteFixedFiles();
    113 }
    114 
    115 bool 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       else
    127         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_REWRITER
    162 
    163 std::unique_ptr<ASTConsumer>
    164 RewriteObjCAction::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(
    169           std::string(InFile), std::move(OS), CI.getDiagnostics(),
    170           CI.getLangOpts(), CI.getDiagnosticOpts().NoRewriteMacros,
    171           (CI.getCodeGenOpts().getDebugInfo() != codegenoptions::NoDebugInfo));
    172     return CreateObjCRewriter(std::string(InFile), std::move(OS),
    173                               CI.getDiagnostics(), CI.getLangOpts(),
    174                               CI.getDiagnosticOpts().NoRewriteMacros);
    175   }
    176   return nullptr;
    177 }
    178 
    179 #endif
    180 
    181 //===----------------------------------------------------------------------===//
    182 // Preprocessor Actions
    183 //===----------------------------------------------------------------------===//
    184 
    185 void RewriteMacrosAction::ExecuteAction() {
    186   CompilerInstance &CI = getCompilerInstance();
    187   std::unique_ptr<raw_ostream> OS =
    188       CI.createDefaultOutputFile(/*Binary=*/true, getCurrentFileOrBufferName());
    189   if (!OS) return;
    190 
    191   RewriteMacrosInInput(CI.getPreprocessor(), OS.get());
    192 }
    193 
    194 void RewriteTestAction::ExecuteAction() {
    195   CompilerInstance &CI = getCompilerInstance();
    196   std::unique_ptr<raw_ostream> OS =
    197       CI.createDefaultOutputFile(/*Binary=*/false, getCurrentFileOrBufferName());
    198   if (!OS) return;
    199 
    200   DoRewriteTest(CI.getPreprocessor(), OS.get());
    201 }
    202 
    203 class RewriteIncludesAction::RewriteImportsListener : public ASTReaderListener {
    204   CompilerInstance &CI;
    205   std::weak_ptr<raw_ostream> Out;
    206 
    207   llvm::DenseSet<const FileEntry*> Rewritten;
    208 
    209 public:
    210   RewriteImportsListener(CompilerInstance &CI, std::shared_ptr<raw_ostream> Out)
    211       : CI(CI), Out(Out) {}
    212 
    213   void visitModuleFile(StringRef Filename,
    214                        serialization::ModuleKind Kind) override {
    215     auto File = CI.getFileManager().getFile(Filename);
    216     assert(File && "missing file for loaded module?");
    217 
    218     // Only rewrite each module file once.
    219     if (!Rewritten.insert(*File).second)
    220       return;
    221 
    222     serialization::ModuleFile *MF =
    223         CI.getASTReader()->getModuleManager().lookup(*File);
    224     assert(MF && "missing module file for loaded module?");
    225 
    226     // Not interested in PCH / preambles.
    227     if (!MF->isModule())
    228       return;
    229 
    230     auto OS = Out.lock();
    231     assert(OS && "loaded module file after finishing rewrite action?");
    232 
    233     (*OS) << "#pragma clang module build ";
    234     if (isValidIdentifier(MF->ModuleName))
    235       (*OS) << MF->ModuleName;
    236     else {
    237       (*OS) << '"';
    238       OS->write_escaped(MF->ModuleName);
    239       (*OS) << '"';
    240     }
    241     (*OS) << '\n';
    242 
    243     // Rewrite the contents of the module in a separate compiler instance.
    244     CompilerInstance Instance(CI.getPCHContainerOperations(),
    245                               &CI.getModuleCache());
    246     Instance.setInvocation(
    247         std::make_shared<CompilerInvocation>(CI.getInvocation()));
    248     Instance.createDiagnostics(
    249         new ForwardingDiagnosticConsumer(CI.getDiagnosticClient()),
    250         /*ShouldOwnClient=*/true);
    251     Instance.getFrontendOpts().DisableFree = false;
    252     Instance.getFrontendOpts().Inputs.clear();
    253     Instance.getFrontendOpts().Inputs.emplace_back(
    254         Filename, InputKind(Language::Unknown, InputKind::Precompiled));
    255     Instance.getFrontendOpts().ModuleFiles.clear();
    256     Instance.getFrontendOpts().ModuleMapFiles.clear();
    257     // Don't recursively rewrite imports. We handle them all at the top level.
    258     Instance.getPreprocessorOutputOpts().RewriteImports = false;
    259 
    260     llvm::CrashRecoveryContext().RunSafelyOnThread([&]() {
    261       RewriteIncludesAction Action;
    262       Action.OutputStream = OS;
    263       Instance.ExecuteAction(Action);
    264     });
    265 
    266     (*OS) << "#pragma clang module endbuild /*" << MF->ModuleName << "*/\n";
    267   }
    268 };
    269 
    270 bool RewriteIncludesAction::BeginSourceFileAction(CompilerInstance &CI) {
    271   if (!OutputStream) {
    272     OutputStream =
    273         CI.createDefaultOutputFile(/*Binary=*/true, getCurrentFileOrBufferName());
    274     if (!OutputStream)
    275       return false;
    276   }
    277 
    278   auto &OS = *OutputStream;
    279 
    280   // If we're preprocessing a module map, start by dumping the contents of the
    281   // module itself before switching to the input buffer.
    282   auto &Input = getCurrentInput();
    283   if (Input.getKind().getFormat() == InputKind::ModuleMap) {
    284     if (Input.isFile()) {
    285       OS << "# 1 \"";
    286       OS.write_escaped(Input.getFile());
    287       OS << "\"\n";
    288     }
    289     getCurrentModule()->print(OS);
    290     OS << "#pragma clang module contents\n";
    291   }
    292 
    293   // If we're rewriting imports, set up a listener to track when we import
    294   // module files.
    295   if (CI.getPreprocessorOutputOpts().RewriteImports) {
    296     CI.createASTReader();
    297     CI.getASTReader()->addListener(
    298         std::make_unique<RewriteImportsListener>(CI, OutputStream));
    299   }
    300 
    301   return true;
    302 }
    303 
    304 void RewriteIncludesAction::ExecuteAction() {
    305   CompilerInstance &CI = getCompilerInstance();
    306 
    307   // If we're rewriting imports, emit the module build output first rather
    308   // than switching back and forth (potentially in the middle of a line).
    309   if (CI.getPreprocessorOutputOpts().RewriteImports) {
    310     std::string Buffer;
    311     llvm::raw_string_ostream OS(Buffer);
    312 
    313     RewriteIncludesInInput(CI.getPreprocessor(), &OS,
    314                            CI.getPreprocessorOutputOpts());
    315 
    316     (*OutputStream) << OS.str();
    317   } else {
    318     RewriteIncludesInInput(CI.getPreprocessor(), OutputStream.get(),
    319                            CI.getPreprocessorOutputOpts());
    320   }
    321 
    322   OutputStream.reset();
    323 }
    324