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#include "IncrementalAction.h"
15
16#include "clang/AST/DeclContextInternals.h"
17#include "clang/Frontend/CompilerInstance.h"
18#include "clang/Interpreter/PartialTranslationUnit.h"
19#include "clang/Parse/Parser.h"
20#include "clang/Sema/Sema.h"
21#include "llvm/IR/Module.h"
22#include "llvm/Support/CrashRecoveryContext.h"
23#include "llvm/Support/Error.h"
24
25#include <sstream>
26
27#define DEBUG_TYPE "clang-repl"
28
29namespace clang {
30
31// IncrementalParser::IncrementalParser() {}
32
33IncrementalParser::IncrementalParser(CompilerInstance &Instance,
34 IncrementalAction *Act, llvm::Error &Err,
35 std::list<PartialTranslationUnit> &PTUs)
36 : S(Instance.getSema()), Act(Act), PTUs(PTUs) {
37 llvm::ErrorAsOutParameter EAO(&Err);
38 Consumer = &S.getASTConsumer();
39 P.reset(p: new Parser(S.getPreprocessor(), S, /*SkipBodies=*/false));
40
41 if (ExternalASTSource *External = S.getASTContext().getExternalSource())
42 External->StartTranslationUnit(Consumer);
43
44 P->Initialize();
45}
46
47IncrementalParser::~IncrementalParser() { P.reset(); }
48
49llvm::Expected<TranslationUnitDecl *>
50IncrementalParser::ParseOrWrapTopLevelDecl() {
51 // Recover resources if we crash before exiting this method.
52 llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(&S);
53 Sema::GlobalEagerInstantiationScope GlobalInstantiations(S, /*Enabled=*/true,
54 /*AtEndOfTU=*/true);
55 Sema::LocalEagerInstantiationScope LocalInstantiations(S, /*AtEndOfTU=*/true);
56
57 // Add a new PTU.
58 ASTContext &C = S.getASTContext();
59 C.addTranslationUnitDecl();
60
61 // Skip previous eof due to last incremental input.
62 if (P->getCurToken().is(K: tok::annot_repl_input_end)) {
63 P->ConsumeAnyToken();
64 // FIXME: Clang does not call ExitScope on finalizing the regular TU, we
65 // might want to do that around HandleEndOfTranslationUnit.
66 P->ExitScope();
67 S.CurContext = nullptr;
68 // Start a new PTU.
69 P->EnterScope(ScopeFlags: Scope::DeclScope);
70 S.ActOnTranslationUnitScope(S: P->getCurScope());
71 }
72
73 Parser::DeclGroupPtrTy ADecl;
74 Sema::ModuleImportState ImportState;
75 for (bool AtEOF = P->ParseFirstTopLevelDecl(Result&: ADecl, ImportState); !AtEOF;
76 AtEOF = P->ParseTopLevelDecl(Result&: ADecl, ImportState)) {
77 if (ADecl && !Consumer->HandleTopLevelDecl(D: ADecl.get()))
78 return llvm::make_error<llvm::StringError>(Args: "Parsing failed. "
79 "The consumer rejected a decl",
80 Args: std::error_code());
81 }
82
83 DiagnosticsEngine &Diags = S.getDiagnostics();
84 if (Diags.hasErrorOccurred()) {
85 CleanUpPTU(MostRecentTU: C.getTranslationUnitDecl());
86
87 Diags.Reset(/*soft=*/true);
88 Diags.getClient()->clear();
89 return llvm::make_error<llvm::StringError>(Args: "Parsing failed.",
90 Args: std::error_code());
91 }
92
93 // Process any TopLevelDecls generated by #pragma weak.
94 for (Decl *D : S.WeakTopLevelDecls()) {
95 DeclGroupRef DGR(D);
96 Consumer->HandleTopLevelDecl(D: DGR);
97 }
98
99 LocalInstantiations.perform();
100 GlobalInstantiations.perform();
101
102 Consumer->HandleTranslationUnit(Ctx&: C);
103
104 return C.getTranslationUnitDecl();
105}
106
107llvm::Expected<TranslationUnitDecl *>
108IncrementalParser::Parse(llvm::StringRef input) {
109 Preprocessor &PP = S.getPreprocessor();
110 assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?");
111
112 std::ostringstream SourceName;
113 SourceName << "input_line_" << InputCount++;
114
115 // Create an uninitialized memory buffer, copy code in and append "\n"
116 size_t InputSize = input.size(); // don't include trailing 0
117 // MemBuffer size should *not* include terminating zero
118 std::unique_ptr<llvm::MemoryBuffer> MB(
119 llvm::WritableMemoryBuffer::getNewUninitMemBuffer(Size: InputSize + 1,
120 BufferName: SourceName.str()));
121 char *MBStart = const_cast<char *>(MB->getBufferStart());
122 memcpy(dest: MBStart, src: input.data(), n: InputSize);
123 MBStart[InputSize] = '\n';
124
125 SourceManager &SM = S.getSourceManager();
126
127 // FIXME: Create SourceLocation, which will allow clang to order the overload
128 // candidates for example
129 SourceLocation NewLoc = SM.getLocForStartOfFile(FID: SM.getMainFileID());
130
131 // Create FileID for the current buffer.
132 FileID FID = SM.createFileID(Buffer: std::move(MB), FileCharacter: SrcMgr::C_User, /*LoadedID=*/0,
133 /*LoadedOffset=*/0, IncludeLoc: NewLoc);
134
135 // NewLoc only used for diags.
136 if (PP.EnterSourceFile(FID, /*DirLookup=*/Dir: nullptr, Loc: NewLoc))
137 return llvm::make_error<llvm::StringError>(Args: "Parsing failed. "
138 "Cannot enter source file.",
139 Args: std::error_code());
140
141 auto PTU = ParseOrWrapTopLevelDecl();
142 if (!PTU)
143 return PTU.takeError();
144
145 if (PP.getLangOpts().DelayedTemplateParsing) {
146 // Microsoft-specific:
147 // Late parsed templates can leave unswallowed "macro"-like tokens.
148 // They will seriously confuse the Parser when entering the next
149 // source file. So lex until we are EOF.
150 Token Tok;
151 do {
152 PP.Lex(Result&: Tok);
153 } while (Tok.isNot(K: tok::annot_repl_input_end));
154 } else {
155 Token AssertTok;
156 PP.Lex(Result&: AssertTok);
157 assert(AssertTok.is(tok::annot_repl_input_end) &&
158 "Lexer must be EOF when starting incremental parse!");
159 }
160
161 return PTU;
162}
163
164void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {
165 if (StoredDeclsMap *Map = MostRecentTU->getPrimaryContext()->getLookupPtr()) {
166 for (auto &&[Key, List] : *Map) {
167 DeclContextLookupResult R = List.getLookupResult();
168 std::vector<NamedDecl *> NamedDeclsToRemove;
169 bool RemoveAll = true;
170 for (NamedDecl *D : R) {
171 if (D->getTranslationUnitDecl() == MostRecentTU)
172 NamedDeclsToRemove.push_back(x: D);
173 else
174 RemoveAll = false;
175 }
176 if (LLVM_LIKELY(RemoveAll)) {
177 Map->erase(Val: Key);
178 } else {
179 for (NamedDecl *D : NamedDeclsToRemove)
180 List.remove(D);
181 }
182 }
183 }
184
185 // FIXME: We should de-allocate MostRecentTU
186 for (Decl *D : MostRecentTU->decls()) {
187 auto *ND = dyn_cast<NamedDecl>(Val: D);
188 if (!ND || ND->getDeclName().isEmpty())
189 continue;
190 // Check if we need to clean up the IdResolver chain.
191 if (ND->getDeclName().getFETokenInfo() && !D->getLangOpts().ObjC &&
192 !D->getLangOpts().CPlusPlus)
193 S.IdResolver.RemoveDecl(D: ND);
194 }
195}
196
197PartialTranslationUnit &
198IncrementalParser::RegisterPTU(TranslationUnitDecl *TU,
199 std::unique_ptr<llvm::Module> M /*={}*/) {
200 PTUs.emplace_back(args: PartialTranslationUnit());
201 PartialTranslationUnit &LastPTU = PTUs.back();
202 LastPTU.TUPart = TU;
203
204 if (!M)
205 M = Act->GenModule();
206
207 assert((!Act->getCodeGen() || M) && "Must have a llvm::Module at this point");
208
209 LastPTU.TheModule = std::move(M);
210 LLVM_DEBUG(llvm::dbgs() << "compile-ptu " << PTUs.size() - 1
211 << ": [TU=" << LastPTU.TUPart);
212 if (LastPTU.TheModule)
213 LLVM_DEBUG(llvm::dbgs() << ", M=" << LastPTU.TheModule.get() << " ("
214 << LastPTU.TheModule->getName() << ")");
215 LLVM_DEBUG(llvm::dbgs() << "]\n");
216 return LastPTU;
217}
218} // end namespace clang
219