blob: 6343f17ed822a5bc58c1c7adb27b6efdcb36d5b0 [file] [log] [blame]
//===--------- 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