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