| //===--------- 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 <sstream> |
| |
| 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<TranslationUnitDecl *> |
| IncrementalParser::ParseOrWrapTopLevelDecl() { |
| // Recover resources if we crash before exiting this method. |
| llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(&S); |
| Sema::GlobalEagerInstantiationScope GlobalInstantiations(S, /*Enabled=*/true, |
| /*AtEndOfTU=*/true); |
| Sema::LocalEagerInstantiationScope LocalInstantiations(S, /*AtEndOfTU=*/true); |
| |
| // 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<llvm::StringError>("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<llvm::StringError>("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<TranslationUnitDecl *> |
| 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<llvm::MemoryBuffer> MB( |
| llvm::WritableMemoryBuffer::getNewUninitMemBuffer(InputSize + 1, |
| SourceName.str())); |
| char *MBStart = const_cast<char *>(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<llvm::StringError>("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<NamedDecl *> 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<NamedDecl>(D); |
| if (!ND || ND->getDeclName().isEmpty()) |
| 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 |