xref: /freebsd/contrib/llvm-project/clang/lib/Frontend/Rewrite/FrontendActions.cpp (revision 8c22b9f3ba586e008e8e55a6215a1d46eb6830b9)
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(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(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(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