219 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8//9// This file implements the class which performs incremental code compilation.10//11//===----------------------------------------------------------------------===//12 13#include "IncrementalParser.h"14#include "IncrementalAction.h"15 16#include "clang/AST/DeclContextInternals.h"17#include "clang/Frontend/CompilerInstance.h"18#include "clang/Interpreter/PartialTranslationUnit.h"19#include "clang/Parse/Parser.h"20#include "clang/Sema/Sema.h"21#include "llvm/IR/Module.h"22#include "llvm/Support/CrashRecoveryContext.h"23#include "llvm/Support/Error.h"24 25#include <sstream>26 27#define DEBUG_TYPE "clang-repl"28 29namespace clang {30 31// IncrementalParser::IncrementalParser() {}32 33IncrementalParser::IncrementalParser(CompilerInstance &Instance,34 IncrementalAction *Act, llvm::Error &Err,35 std::list<PartialTranslationUnit> &PTUs)36 : S(Instance.getSema()), Act(Act), PTUs(PTUs) {37 llvm::ErrorAsOutParameter EAO(&Err);38 Consumer = &S.getASTConsumer();39 P.reset(new Parser(S.getPreprocessor(), S, /*SkipBodies=*/false));40 41 if (ExternalASTSource *External = S.getASTContext().getExternalSource())42 External->StartTranslationUnit(Consumer);43 44 P->Initialize();45}46 47IncrementalParser::~IncrementalParser() { P.reset(); }48 49llvm::Expected<TranslationUnitDecl *>50IncrementalParser::ParseOrWrapTopLevelDecl() {51 // Recover resources if we crash before exiting this method.52 llvm::CrashRecoveryContextCleanupRegistrar<Sema> CleanupSema(&S);53 Sema::GlobalEagerInstantiationScope GlobalInstantiations(S, /*Enabled=*/true,54 /*AtEndOfTU=*/true);55 Sema::LocalEagerInstantiationScope LocalInstantiations(S, /*AtEndOfTU=*/true);56 57 // Add a new PTU.58 ASTContext &C = S.getASTContext();59 C.addTranslationUnitDecl();60 61 // Skip previous eof due to last incremental input.62 if (P->getCurToken().is(tok::annot_repl_input_end)) {63 P->ConsumeAnyToken();64 // FIXME: Clang does not call ExitScope on finalizing the regular TU, we65 // might want to do that around HandleEndOfTranslationUnit.66 P->ExitScope();67 S.CurContext = nullptr;68 // Start a new PTU.69 P->EnterScope(Scope::DeclScope);70 S.ActOnTranslationUnitScope(P->getCurScope());71 }72 73 Parser::DeclGroupPtrTy ADecl;74 Sema::ModuleImportState ImportState;75 for (bool AtEOF = P->ParseFirstTopLevelDecl(ADecl, ImportState); !AtEOF;76 AtEOF = P->ParseTopLevelDecl(ADecl, ImportState)) {77 if (ADecl && !Consumer->HandleTopLevelDecl(ADecl.get()))78 return llvm::make_error<llvm::StringError>("Parsing failed. "79 "The consumer rejected a decl",80 std::error_code());81 }82 83 DiagnosticsEngine &Diags = S.getDiagnostics();84 if (Diags.hasErrorOccurred()) {85 CleanUpPTU(C.getTranslationUnitDecl());86 87 Diags.Reset(/*soft=*/true);88 Diags.getClient()->clear();89 return llvm::make_error<llvm::StringError>("Parsing failed.",90 std::error_code());91 }92 93 // Process any TopLevelDecls generated by #pragma weak.94 for (Decl *D : S.WeakTopLevelDecls()) {95 DeclGroupRef DGR(D);96 Consumer->HandleTopLevelDecl(DGR);97 }98 99 LocalInstantiations.perform();100 GlobalInstantiations.perform();101 102 Consumer->HandleTranslationUnit(C);103 104 return C.getTranslationUnitDecl();105}106 107llvm::Expected<TranslationUnitDecl *>108IncrementalParser::Parse(llvm::StringRef input) {109 Preprocessor &PP = S.getPreprocessor();110 assert(PP.isIncrementalProcessingEnabled() && "Not in incremental mode!?");111 112 std::ostringstream SourceName;113 SourceName << "input_line_" << InputCount++;114 115 // Create an uninitialized memory buffer, copy code in and append "\n"116 size_t InputSize = input.size(); // don't include trailing 0117 // MemBuffer size should *not* include terminating zero118 std::unique_ptr<llvm::MemoryBuffer> MB(119 llvm::WritableMemoryBuffer::getNewUninitMemBuffer(InputSize + 1,120 SourceName.str()));121 char *MBStart = const_cast<char *>(MB->getBufferStart());122 memcpy(MBStart, input.data(), InputSize);123 MBStart[InputSize] = '\n';124 125 SourceManager &SM = S.getSourceManager();126 127 // FIXME: Create SourceLocation, which will allow clang to order the overload128 // candidates for example129 SourceLocation NewLoc = SM.getLocForStartOfFile(SM.getMainFileID());130 131 // Create FileID for the current buffer.132 FileID FID = SM.createFileID(std::move(MB), SrcMgr::C_User, /*LoadedID=*/0,133 /*LoadedOffset=*/0, NewLoc);134 135 // NewLoc only used for diags.136 if (PP.EnterSourceFile(FID, /*DirLookup=*/nullptr, NewLoc))137 return llvm::make_error<llvm::StringError>("Parsing failed. "138 "Cannot enter source file.",139 std::error_code());140 141 auto PTU = ParseOrWrapTopLevelDecl();142 if (!PTU)143 return PTU.takeError();144 145 if (PP.getLangOpts().DelayedTemplateParsing) {146 // Microsoft-specific:147 // Late parsed templates can leave unswallowed "macro"-like tokens.148 // They will seriously confuse the Parser when entering the next149 // source file. So lex until we are EOF.150 Token Tok;151 do {152 PP.Lex(Tok);153 } while (Tok.isNot(tok::annot_repl_input_end));154 } else {155 Token AssertTok;156 PP.Lex(AssertTok);157 assert(AssertTok.is(tok::annot_repl_input_end) &&158 "Lexer must be EOF when starting incremental parse!");159 }160 161 return PTU;162}163 164void IncrementalParser::CleanUpPTU(TranslationUnitDecl *MostRecentTU) {165 if (StoredDeclsMap *Map = MostRecentTU->getPrimaryContext()->getLookupPtr()) {166 for (auto &&[Key, List] : *Map) {167 DeclContextLookupResult R = List.getLookupResult();168 std::vector<NamedDecl *> NamedDeclsToRemove;169 bool RemoveAll = true;170 for (NamedDecl *D : R) {171 if (D->getTranslationUnitDecl() == MostRecentTU)172 NamedDeclsToRemove.push_back(D);173 else174 RemoveAll = false;175 }176 if (LLVM_LIKELY(RemoveAll)) {177 Map->erase(Key);178 } else {179 for (NamedDecl *D : NamedDeclsToRemove)180 List.remove(D);181 }182 }183 }184 185 // FIXME: We should de-allocate MostRecentTU186 for (Decl *D : MostRecentTU->decls()) {187 auto *ND = dyn_cast<NamedDecl>(D);188 if (!ND || ND->getDeclName().isEmpty())189 continue;190 // Check if we need to clean up the IdResolver chain.191 if (ND->getDeclName().getFETokenInfo() && !D->getLangOpts().ObjC &&192 !D->getLangOpts().CPlusPlus)193 S.IdResolver.RemoveDecl(ND);194 }195}196 197PartialTranslationUnit &198IncrementalParser::RegisterPTU(TranslationUnitDecl *TU,199 std::unique_ptr<llvm::Module> M /*={}*/) {200 PTUs.emplace_back(PartialTranslationUnit());201 PartialTranslationUnit &LastPTU = PTUs.back();202 LastPTU.TUPart = TU;203 204 if (!M)205 M = Act->GenModule();206 207 assert((!Act->getCodeGen() || M) && "Must have a llvm::Module at this point");208 209 LastPTU.TheModule = std::move(M);210 LLVM_DEBUG(llvm::dbgs() << "compile-ptu " << PTUs.size() - 1211 << ": [TU=" << LastPTU.TUPart);212 if (LastPTU.TheModule)213 LLVM_DEBUG(llvm::dbgs() << ", M=" << LastPTU.TheModule.get() << " ("214 << LastPTU.TheModule->getName() << ")");215 LLVM_DEBUG(llvm::dbgs() << "]\n");216 return LastPTU;217}218} // end namespace clang219