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