xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/Interpreter/IncrementalParser.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
1 //===--------- IncrementalParser.cpp - Incremental Compilation  -----------===//
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 // This file implements the class which performs incremental code compilation.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #include "IncrementalParser.h"
14 
15 #include "clang/CodeGen/BackendUtil.h"
16 #include "clang/CodeGen/CodeGenAction.h"
17 #include "clang/CodeGen/ModuleBuilder.h"
18 #include "clang/Frontend/CompilerInstance.h"
19 #include "clang/Frontend/FrontendAction.h"
20 #include "clang/FrontendTool/Utils.h"
21 #include "clang/Parse/Parser.h"
22 #include "clang/Sema/Sema.h"
23 
24 #include "llvm/Option/ArgList.h"
25 #include "llvm/Support/CrashRecoveryContext.h"
26 #include "llvm/Support/Error.h"
27 #include "llvm/Support/Timer.h"
28 
29 #include <sstream>
30 
31 namespace clang {
32 
33 /// A custom action enabling the incremental processing functionality.
34 ///
35 /// The usual \p FrontendAction expects one call to ExecuteAction and once it
36 /// sees a call to \p EndSourceFile it deletes some of the important objects
37 /// such as \p Preprocessor and \p Sema assuming no further input will come.
38 ///
39 /// \p IncrementalAction ensures it keep its underlying action's objects alive
40 /// as long as the \p IncrementalParser needs them.
41 ///
42 class IncrementalAction : public WrapperFrontendAction {
43 private:
44   bool IsTerminating = false;
45 
46 public:
IncrementalAction(CompilerInstance & CI,llvm::LLVMContext & LLVMCtx,llvm::Error & Err)47   IncrementalAction(CompilerInstance &CI, llvm::LLVMContext &LLVMCtx,
48                     llvm::Error &Err)
49       : WrapperFrontendAction([&]() {
50           llvm::ErrorAsOutParameter EAO(&Err);
51           std::unique_ptr<FrontendAction> Act;
52           switch (CI.getFrontendOpts().ProgramAction) {
53           default:
54             Err = llvm::createStringError(
55                 std::errc::state_not_recoverable,
56                 "Driver initialization failed. "
57                 "Incremental mode for action %d is not supported",
58                 CI.getFrontendOpts().ProgramAction);
59             return Act;
60           case frontend::ASTDump:
61             LLVM_FALLTHROUGH;
62           case frontend::ASTPrint:
63             LLVM_FALLTHROUGH;
64           case frontend::ParseSyntaxOnly:
65             Act = CreateFrontendAction(CI);
66             break;
67           case frontend::EmitAssembly:
68             LLVM_FALLTHROUGH;
69           case frontend::EmitObj:
70             LLVM_FALLTHROUGH;
71           case frontend::EmitLLVMOnly:
72             Act.reset(new EmitLLVMOnlyAction(&LLVMCtx));
73             break;
74           }
75           return Act;
76         }()) {}
getWrapped() const77   FrontendAction *getWrapped() const { return WrappedAction.get(); }
ExecuteAction()78   void ExecuteAction() override {
79     CompilerInstance &CI = getCompilerInstance();
80     assert(CI.hasPreprocessor() && "No PP!");
81 
82     // FIXME: Move the truncation aspect of this into Sema, we delayed this till
83     // here so the source manager would be initialized.
84     if (hasCodeCompletionSupport() &&
85         !CI.getFrontendOpts().CodeCompletionAt.FileName.empty())
86       CI.createCodeCompletionConsumer();
87 
88     // Use a code completion consumer?
89     CodeCompleteConsumer *CompletionConsumer = nullptr;
90     if (CI.hasCodeCompletionConsumer())
91       CompletionConsumer = &CI.getCodeCompletionConsumer();
92 
93     Preprocessor &PP = CI.getPreprocessor();
94     PP.enableIncrementalProcessing();
95     PP.EnterMainSourceFile();
96 
97     if (!CI.hasSema())
98       CI.createSema(getTranslationUnitKind(), CompletionConsumer);
99   }
100 
101   // Do not terminate after processing the input. This allows us to keep various
102   // clang objects alive and to incrementally grow the current TU.
EndSourceFile()103   void EndSourceFile() override {
104     // The WrappedAction can be nullptr if we issued an error in the ctor.
105     if (IsTerminating && getWrapped())
106       WrapperFrontendAction::EndSourceFile();
107   }
108 
FinalizeAction()109   void FinalizeAction() {
110     assert(!IsTerminating && "Already finalized!");
111     IsTerminating = true;
112     EndSourceFile();
113   }
114 };
115 
IncrementalParser(std::unique_ptr<CompilerInstance> Instance,llvm::LLVMContext & LLVMCtx,llvm::Error & Err)116 IncrementalParser::IncrementalParser(std::unique_ptr<CompilerInstance> Instance,
117                                      llvm::LLVMContext &LLVMCtx,
118                                      llvm::Error &Err)
119     : CI(std::move(Instance)) {
120   llvm::ErrorAsOutParameter EAO(&Err);
121   Act = std::make_unique<IncrementalAction>(*CI, LLVMCtx, Err);
122   if (Err)
123     return;
124   CI->ExecuteAction(*Act);
125   Consumer = &CI->getASTConsumer();
126   P.reset(
127       new Parser(CI->getPreprocessor(), CI->getSema(), /*SkipBodies=*/false));
128   P->Initialize();
129 }
130 
~IncrementalParser()131 IncrementalParser::~IncrementalParser() { Act->FinalizeAction(); }
132 
ParseOrWrapTopLevelDecl()133 llvm::Expected<Transaction &> IncrementalParser::ParseOrWrapTopLevelDecl() {
134   DiagnosticsEngine &Diags = getCI()->getDiagnostics();
135 
136   if (Diags.hasErrorOccurred())
137     llvm::report_fatal_error("Previous input had errors, "
138                              "recovery not yet supported",
139                              /*GenCrashDiag=*/false);
140 
141   // Recover resources if we crash before exiting this method.
142   Sema &S = CI->getSema();
143   llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(&S);
144   Sema::GlobalEagerInstantiationScope GlobalInstantiations(S, /*Enabled=*/true);
145   Sema::LocalEagerInstantiationScope LocalInstantiations(S);
146 
147   // Skip previous eof due to last incremental input.
148   if (P->getCurToken().is(tok::eof))
149     P->ConsumeToken();
150 
151   Transactions.emplace_back(Transaction());
152   Transaction &LastTransaction = Transactions.back();
153 
154   Parser::DeclGroupPtrTy ADecl;
155   for (bool AtEOF = P->ParseFirstTopLevelDecl(ADecl); !AtEOF;
156        AtEOF = P->ParseTopLevelDecl(ADecl)) {
157     // If we got a null return and something *was* parsed, ignore it.  This
158     // is due to a top-level semicolon, an action override, or a parse error
159     // skipping something.
160     if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get()))
161       return llvm::make_error<llvm::StringError>("Parsing failed. "
162                                                  "The consumer rejected a decl",
163                                                  std::error_code());
164     LastTransaction.Decls.push_back(ADecl.get());
165   }
166 
167   // Process any TopLevelDecls generated by #pragma weak.
168   for (Decl *D : S.WeakTopLevelDecls()) {
169     DeclGroupRef DGR(D);
170     LastTransaction.Decls.push_back(DGR);
171     Consumer->HandleTopLevelDecl(DGR);
172   }
173 
174   LocalInstantiations.perform();
175   GlobalInstantiations.perform();
176 
177   Consumer->HandleTranslationUnit(S.getASTContext());
178 
179   if (Diags.hasErrorOccurred())
180     return llvm::make_error<llvm::StringError>("Parsing failed.",
181                                                std::error_code());
182 
183   return LastTransaction;
184 }
185 
getCodeGen(FrontendAction * Act)186 static CodeGenerator *getCodeGen(FrontendAction *Act) {
187   IncrementalAction *IncrAct = static_cast<IncrementalAction *>(Act);
188   FrontendAction *WrappedAct = IncrAct->getWrapped();
189   if (!WrappedAct->hasIRSupport())
190     return nullptr;
191   return static_cast<CodeGenAction *>(WrappedAct)->getCodeGenerator();
192 }
193 
Parse(llvm::StringRef input)194 llvm::Expected<Transaction &> IncrementalParser::Parse(llvm::StringRef input) {
195   Preprocessor &PP = CI->getPreprocessor();
196   assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?");
197 
198   std::ostringstream SourceName;
199   SourceName << "input_line_" << InputCount++;
200 
201   // Create an uninitialized memory buffer, copy code in and append "\n"
202   size_t InputSize = input.size(); // don't include trailing 0
203   // MemBuffer size should *not* include terminating zero
204   std::unique_ptr<llvm::MemoryBuffer> MB(
205       llvm::WritableMemoryBuffer::getNewUninitMemBuffer(InputSize + 1,
206                                                         SourceName.str()));
207   char *MBStart = const_cast<char *>(MB->getBufferStart());
208   memcpy(MBStart, input.data(), InputSize);
209   MBStart[InputSize] = '\n';
210 
211   SourceManager &SM = CI->getSourceManager();
212 
213   // FIXME: Create SourceLocation, which will allow clang to order the overload
214   // candidates for example
215   SourceLocation NewLoc = SM.getLocForStartOfFile(SM.getMainFileID());
216 
217   // Create FileID for the current buffer.
218   FileID FID = SM.createFileID(std::move(MB), SrcMgr::C_User, /*LoadedID=*/0,
219                                /*LoadedOffset=*/0, NewLoc);
220 
221   // NewLoc only used for diags.
222   if (PP.EnterSourceFile(FID, /*DirLookup=*/0, NewLoc))
223     return llvm::make_error<llvm::StringError>("Parsing failed. "
224                                                "Cannot enter source file.",
225                                                std::error_code());
226 
227   auto ErrOrTransaction = ParseOrWrapTopLevelDecl();
228   if (auto Err = ErrOrTransaction.takeError())
229     return std::move(Err);
230 
231   if (PP.getLangOpts().DelayedTemplateParsing) {
232     // Microsoft-specific:
233     // Late parsed templates can leave unswallowed "macro"-like tokens.
234     // They will seriously confuse the Parser when entering the next
235     // source file. So lex until we are EOF.
236     Token Tok;
237     do {
238       PP.Lex(Tok);
239     } while (Tok.isNot(tok::eof));
240   }
241 
242   Token AssertTok;
243   PP.Lex(AssertTok);
244   assert(AssertTok.is(tok::eof) &&
245          "Lexer must be EOF when starting incremental parse!");
246 
247   if (CodeGenerator *CG = getCodeGen(Act.get())) {
248     std::unique_ptr<llvm::Module> M(CG->ReleaseModule());
249     CG->StartModule("incr_module_" + std::to_string(Transactions.size()),
250                     M->getContext());
251 
252     ErrOrTransaction->TheModule = std::move(M);
253   }
254 
255   return ErrOrTransaction;
256 }
257 } // end namespace clang
258