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