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