//===--------- IncrementalParser.cpp - Incremental Compilation -----------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file implements the class which performs incremental code compilation. // //===----------------------------------------------------------------------===// #include "IncrementalParser.h" #include "clang/AST/DeclContextInternals.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Interpreter/PartialTranslationUnit.h" #include "clang/Parse/Parser.h" #include "clang/Sema/Sema.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/Error.h" #include namespace clang { // IncrementalParser::IncrementalParser() {} IncrementalParser::IncrementalParser(CompilerInstance &Instance, llvm::Error &Err) : S(Instance.getSema()) { llvm::ErrorAsOutParameter EAO(&Err); Consumer = &S.getASTConsumer(); P.reset(new Parser(S.getPreprocessor(), S, /*SkipBodies=*/false)); P->Initialize(); } IncrementalParser::~IncrementalParser() { P.reset(); } llvm::Expected IncrementalParser::ParseOrWrapTopLevelDecl() { // Recover resources if we crash before exiting this method. llvm::CrashRecoveryContextCleanupRegistrar CleanupSema(&S); Sema::GlobalEagerInstantiationScope GlobalInstantiations(S, /*Enabled=*/true); Sema::LocalEagerInstantiationScope LocalInstantiations(S); // Add a new PTU. ASTContext &C = S.getASTContext(); C.addTranslationUnitDecl(); // Skip previous eof due to last incremental input. if (P->getCurToken().is(tok::annot_repl_input_end)) { P->ConsumeAnyToken(); // FIXME: Clang does not call ExitScope on finalizing the regular TU, we // might want to do that around HandleEndOfTranslationUnit. P->ExitScope(); S.CurContext = nullptr; // Start a new PTU. P->EnterScope(Scope::DeclScope); S.ActOnTranslationUnitScope(P->getCurScope()); } Parser::DeclGroupPtrTy ADecl; Sema::ModuleImportState ImportState; for (bool AtEOF = P->ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF; AtEOF = P->ParseTopLevelDecl(ADecl, ImportState)) { if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get())) return llvm::make_error("Parsing failed. " "The consumer rejected a decl", std::error_code()); } DiagnosticsEngine &Diags = S.getDiagnostics(); if (Diags.hasErrorOccurred()) { CleanUpPTU(C.getTranslationUnitDecl()); Diags.Reset(/*soft=*/true); Diags.getClient()->clear(); return llvm::make_error("Parsing failed.", std::error_code()); } // Process any TopLevelDecls generated by #pragma weak. for (Decl *D : S.WeakTopLevelDecls()) { DeclGroupRef DGR(D); Consumer->HandleTopLevelDecl(DGR); } LocalInstantiations.perform(); GlobalInstantiations.perform(); Consumer->HandleTranslationUnit(C); return C.getTranslationUnitDecl(); } llvm::Expected IncrementalParser::Parse(llvm::StringRef input) { Preprocessor &PP = S.getPreprocessor(); assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?"); std::ostringstream SourceName; SourceName << "input_line_" << InputCount++; // Create an uninitialized memory buffer, copy code in and append "\n" size_t InputSize = input.size(); // don't include trailing 0 // MemBuffer size should *not* include terminating zero std::unique_ptr MB( llvm::WritableMemoryBuffer::getNewUninitMemBuffer(InputSize + 1, SourceName.str())); char *MBStart = const_cast(MB->getBufferStart()); memcpy(MBStart, input.data(), InputSize); MBStart[InputSize] = '\n'; SourceManager &SM = S.getSourceManager(); // FIXME: Create SourceLocation, which will allow clang to order the overload // candidates for example SourceLocation NewLoc = SM.getLocForStartOfFile(SM.getMainFileID()); // Create FileID for the current buffer. FileID FID = SM.createFileID(std::move(MB), SrcMgr::C_User, /*LoadedID=*/0, /*LoadedOffset=*/0, NewLoc); // NewLoc only used for diags. if (PP.EnterSourceFile(FID, /*DirLookup=*/nullptr, NewLoc)) return llvm::make_error("Parsing failed. " "Cannot enter source file.", std::error_code()); auto PTU = ParseOrWrapTopLevelDecl(); if (!PTU) return PTU.takeError(); if (PP.getLangOpts().DelayedTemplateParsing) { // Microsoft-specific: // Late parsed templates can leave unswallowed "macro"-like tokens. // They will seriously confuse the Parser when entering the next // source file. So lex until we are EOF. Token Tok; do { PP.Lex(Tok); } while (Tok.isNot(tok::annot_repl_input_end)); } else { Token AssertTok; PP.Lex(AssertTok); assert(AssertTok.is(tok::annot_repl_input_end) && "Lexer must be EOF when starting incremental parse!"); } return PTU; } void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) { if (StoredDeclsMap *Map = MostRecentTU->getPrimaryContext()->getLookupPtr()) { for (auto &&[Key, List] : *Map) { DeclContextLookupResult R = List.getLookupResult(); std::vector NamedDeclsToRemove; bool RemoveAll = true; for (NamedDecl *D : R) { if (D->getTranslationUnitDecl() == MostRecentTU) NamedDeclsToRemove.push_back(D); else RemoveAll = false; } if (LLVM_LIKELY(RemoveAll)) { Map->erase(Key); } else { for (NamedDecl *D : NamedDeclsToRemove) List.remove(D); } } } // FIXME: We should de-allocate MostRecentTU for (Decl *D : MostRecentTU->decls()) { auto *ND = dyn_cast(D); if (!ND) continue; // Check if we need to clean up the IdResolver chain. if (ND->getDeclName().getFETokenInfo() && !D->getLangOpts().ObjC && !D->getLangOpts().CPlusPlus) S.IdResolver.RemoveDecl(ND); } } } // end namespace clang