xref: /freebsd/contrib/llvm-project/clang/lib/Interpreter/IncrementalParser.cpp (revision e64bea71c21eb42e97aa615188ba91f6cce0d36d)
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                                                            /*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 *>
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 
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