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