Cling patch for code completion.

This commit is contained in:
CristinaCristescu 2016-06-23 18:09:06 +02:00 committed by sftnight
parent 8ef4931a12
commit cc5da51f21
11 changed files with 218 additions and 23 deletions

View File

@ -62,6 +62,12 @@ namespace cling {
///
unsigned CheckPointerValidity : 1;
///\brief Offset into the input line to enable the setting of the
/// code completion point.
/// -1 diasables code completion.
///
int CodeCompletionOffset = -1;
CompilationOptions() {
DeclarationExtraction = 0;
ValuePrinting = VPDisabled;
@ -84,7 +90,8 @@ namespace cling {
CodeGeneration == Other.CodeGeneration &&
CodeGenerationForModule == Other.CodeGenerationForModule &&
IgnorePromptDiags == Other.IgnorePromptDiags &&
CheckPointerValidity == Other.CheckPointerValidity;
CheckPointerValidity == Other.CheckPointerValidity &&
CodeCompletionOffset == Other.CodeCompletionOffset;
}
bool operator!=(CompilationOptions Other) const {
@ -97,7 +104,8 @@ namespace cling {
CodeGeneration != Other.CodeGeneration ||
CodeGenerationForModule != Other.CodeGenerationForModule ||
IgnorePromptDiags != Other.IgnorePromptDiags ||
CheckPointerValidity != Other.CheckPointerValidity;
CheckPointerValidity != Other.CheckPointerValidity ||
CodeCompletionOffset != Other.CodeCompletionOffset;
}
};
} // end namespace cling

View File

@ -303,7 +303,7 @@ namespace cling {
///\param[in] llvmdir - ???
///\param[in] noRuntime - flag to control the presence of runtime universe
///
Interpreter(Interpreter &parentInterpreter,int argc, const char* const *argv,
Interpreter(const Interpreter &parentInterpreter,int argc, const char* const *argv,
const char* llvmdir = 0, bool noRuntime = true);
virtual ~Interpreter();
@ -467,6 +467,18 @@ namespace cling {
///
CompilationResult parseForModule(const std::string& input);
///\brief Code completes user input.
///
/// The interface circumvents the most of the extra work necessary to
/// code complete code.
///
/// @param[in] input - The input containing the string to be completed.
/// @param[out] T - The cling::Transaction of the input
///
///\returns Whether the operation was fully successful.
///
CompilationResult codeComplete(const std::string& input, unsigned offset);
///\brief Compiles input line, which doesn't contain statements.
///
/// The interface circumvents the most of the extra work necessary to

View File

@ -30,6 +30,7 @@ namespace clang {
class Token;
class FileEntry;
class Module;
class CodeCompleteConsumer;
}
namespace cling {
@ -161,6 +162,13 @@ namespace cling {
/// for instance when throwing interpreter exceptions.
virtual void PrintStackTrace() {}
///\brief Cling creates the code complete consumer for its child interp.
virtual void CreateCodeCompleteConsumer(Interpreter* child) const;
///\brief Get the resukts of the code completion.
virtual void GetCompletionResults(Interpreter* child,
std::vector<std::string>&) const;
///\brief DynamicScopes only! Set to true if it is currently evaluating a
/// dynamic expr.
///

View File

@ -109,6 +109,8 @@ namespace cling {
const Interpreter& getInterpreter() const { return m_Interp; }
Interpreter& getInterpreter() { return m_Interp; }
///\brief Get the output stream used by the MetaProcessor for its output.
/// (in contrast to the interpreter's output which is redirected using
/// setStdStream()).

View File

@ -3,9 +3,37 @@
using namespace clang;
namespace {
class ClingASTImporter : public ASTImporter {
private:
cling::ASTImportSource& m_source;
public:
ClingASTImporter(ASTContext &ToContext, FileManager &ToFileManager,
ASTContext &FromContext, FileManager &FromFileManager,
bool MinimalImport, cling::ASTImportSource& source) :
ASTImporter(ToContext, ToFileManager, FromContext,
FromFileManager, MinimalImport), m_source(source) {}
Decl *Imported(Decl *From, Decl *To) override {
ASTImporter::Imported(From, To);
// Put the name of the Decl imported with the
// DeclarationName coming from the parent, in my map.
if (DeclContext *declContextParent = llvm::dyn_cast<DeclContext>(From)) {
DeclContext *declContextChild = llvm::dyn_cast<DeclContext>(To);
m_source.addToDeclContext(declContextChild, declContextParent);
}
return To;
}
};
}
namespace cling {
ASTImportSource::ASTImportSource(cling::Interpreter *parent_interpreter,
ASTImportSource::ASTImportSource(const cling::Interpreter *parent_interpreter,
cling::Interpreter *child_interpreter) :
m_parent_Interp(parent_interpreter), m_child_Interp(child_interpreter) {
@ -88,10 +116,10 @@ namespace cling {
FileManager &child_FM = m_child_Interp->getCI()->getFileManager();
FileManager &parent_FM = m_parent_Interp->getCI()->getFileManager();
// Clang's ASTImporter
ASTImporter importer(to_ASTContext, child_FM,
// Cling's ASTImporter
ClingASTImporter importer(to_ASTContext, child_FM,
from_ASTContext, parent_FM,
/*MinimalImport : ON*/ true);
/*MinimalImport : ON*/ true, *this);
for (DeclContext::lookup_iterator I = lookup_result.begin(),
E = lookup_result.end();
@ -120,6 +148,8 @@ namespace cling {
bool ASTImportSource::FindExternalVisibleDeclsByName(
const DeclContext *childCurrentDeclContext, DeclarationName childDeclName) {
assert(childDeclName && "Child Decl name is empty");
assert(childCurrentDeclContext->hasExternalVisibleStorage() &&
"DeclContext has no visible decls in storage");
@ -132,21 +162,24 @@ namespace cling {
} else {
// Get the identifier info from the parent interpreter
// for this Name.
llvm::StringRef name(childDeclName.getAsString());
IdentifierTable &parentIdentifierTable =
m_parent_Interp->getCI()->getASTContext().Idents;
IdentifierInfo &parentIdentifierInfo = parentIdentifierTable.get(name);
DeclarationName parentDeclNameTemp(&parentIdentifierInfo);
parentDeclName = parentDeclNameTemp;
std::string name = childDeclName.getAsString();
if (!name.empty()) {
llvm::StringRef nameRef(name);
IdentifierTable &parentIdentifierTable =
m_parent_Interp->getCI()->getASTContext().Idents;
IdentifierInfo &parentIdentifierInfo =
parentIdentifierTable.get(nameRef);
parentDeclName = DeclarationName(&parentIdentifierInfo);
}
}
// Search in the map of the stored Decl Contexts for this
// Decl Context.
std::map<const clang::DeclContext *, clang::DeclContext *>::iterator I;
// If childCurrentDeclContext was found before and is already in the map,
// then do the lookup using the stored pointer.
if ((I = m_DeclContexts_map.find(childCurrentDeclContext))
!= m_DeclContexts_map.end()) {
// If childCurrentDeclContext was found before and is already in the map,
// then do the lookup using the stored pointer.
DeclContext *parentDeclContext = I->second;
Decl *fromDeclContext = Decl::castFromDeclContext(parentDeclContext);
@ -168,4 +201,55 @@ namespace cling {
}
return false;
}
///\brief Make available to child all decls in parent's decl context
/// that corresponds to child decl context.
void ASTImportSource::completeVisibleDeclsMap(
const clang::DeclContext *childDeclContext) {
assert (childDeclContext && "No child decl context!");
if (!childDeclContext->hasExternalVisibleStorage())
return;
// Search in the map of the stored Decl Contexts for this
// Decl Context.
std::map<const clang::DeclContext *, clang::DeclContext *>::iterator I;
// If childCurrentDeclContext was found before and is already in the map,
// then do the lookup using the stored pointer.
if ((I = m_DeclContexts_map.find(childDeclContext))
!= m_DeclContexts_map.end()) {
DeclContext *parentDeclContext = I->second;
Decl *fromDeclContext = Decl::castFromDeclContext(parentDeclContext);
ASTContext &from_ASTContext = fromDeclContext->getASTContext();
Decl *toDeclContext = Decl::castFromDeclContext(childDeclContext);
ASTContext &to_ASTContext = toDeclContext->getASTContext();
// Prepare to import the Decl(Context) we found in the
// child interpreter by getting the file managers from
// each interpreter.
FileManager &child_FM = m_child_Interp->getCI()->getFileManager();
FileManager &parent_FM = m_parent_Interp->getCI()->getFileManager();
// Cling's ASTImporter
ClingASTImporter importer(to_ASTContext, child_FM,
from_ASTContext, parent_FM,
/*MinimalImport : ON*/ true, *this);
for (DeclContext::decl_iterator I_decl = parentDeclContext->decls_begin(),
E_decl = parentDeclContext->decls_end(); I_decl != E_decl; ++I_decl) {
if (NamedDecl* parentNamedDecl = llvm::dyn_cast<NamedDecl>(*I_decl)) {
DeclarationName newDeclName = parentNamedDecl->getDeclName();
ImportDecl(parentNamedDecl, importer, newDeclName, newDeclName,
childDeclContext);
}
}
const_cast<DeclContext *>(childDeclContext)->
setHasExternalVisibleStorage(false);
}
}
} // end namespace cling

View File

@ -35,7 +35,7 @@ namespace cling {
class ASTImportSource : public clang::ExternalASTSource {
private:
cling::Interpreter *m_parent_Interp;
const cling::Interpreter *m_parent_Interp;
cling::Interpreter *m_child_Interp;
clang::Sema *m_Sema;
@ -56,11 +56,13 @@ namespace cling {
std::map <clang::DeclarationName, clang::DeclarationName > m_DeclName_map;
public:
ASTImportSource(cling::Interpreter *parent_interpreter,
ASTImportSource(const cling::Interpreter *parent_interpreter,
cling::Interpreter *child_interpreter);
~ASTImportSource() { };
void completeVisibleDeclsMap(const clang::DeclContext *DC) override;
bool FindExternalVisibleDeclsByName(const clang::DeclContext *childCurrentDeclContext,
clang::DeclarationName childDeclName) override;
@ -86,6 +88,11 @@ namespace cling {
clang::DeclarationName &childDeclName,
clang::DeclarationName &parentDeclName,
const clang::DeclContext *childCurrentDeclContext);
void addToDeclContext(clang::DeclContext *child,
clang::DeclContext *parent) {
m_DeclContexts_map[child] = parent;
}
};
} // end namespace cling
#endif //CLING_ASTIMPORTSOURCE_H

View File

@ -26,6 +26,7 @@ add_cling_library(clingInterpreter
CheckEmptyTransactionTransformer.cpp
CIFactory.cpp
ClangInternalState.cpp
ClingCodeCompleteConsumer.cpp
ClingPragmas.cpp
DeclCollector.cpp
DeclExtractor.cpp

View File

@ -713,10 +713,20 @@ namespace cling {
SourceLocation NewLoc = getLastMemoryBufferEndLoc().getLocWithOffset(1);
llvm::MemoryBuffer* MBNonOwn = MB.get();
// Create FileID for the current buffer
FileID FID = SM.createFileID(std::move(MB), SrcMgr::C_User,
/*LoadedID*/0,
/*LoadedOffset*/0, NewLoc);
// Create FileEntry and FileID for the current buffer
const clang::FileEntry* FE
= SM.getFileManager().getVirtualFile("vfile for " + source_name.str(),
InputSize, 0 /* mod time*/);
SM.overrideFileContents(FE, std::move(MB));
FileID FID = SM.createFileID(FE, NewLoc, SrcMgr::C_User);
// Set the code completion point if completion is enabled.
if (CO.CodeCompletionOffset != -1) {
printf("Is it really the completion point?\n");
PP.SetCodeCompletionPoint(FE, 1, 45 + CO.CodeCompletionOffset);
m_Interpreter->unload(1);
}
m_MemoryBuffers.push_back(std::make_pair(MBNonOwn, FID));
@ -767,6 +777,11 @@ namespace cling {
m_Consumer->HandleTopLevelDecl(ADecl.get());
};
if (CO.CodeCompletionOffset != -1) {
assert(PP.isCodeCompletionReached() && "Code completion set but not reached!");
return kSuccess;
}
#ifdef LLVM_ON_WIN32
// Microsoft-specific:
// Late parsed templates can leave unswallowed "macro"-like tokens.

View File

@ -237,7 +237,7 @@ namespace cling {
///\brief Constructor for the child Interpreter.
/// Passing the parent Interpreter as an argument.
///
Interpreter::Interpreter(Interpreter &parentInterpreter, int argc,
Interpreter::Interpreter(const Interpreter &parentInterpreter, int argc,
const char* const *argv,
const char* llvmdir /*= 0*/, bool noRuntime) :
Interpreter(argc, argv, llvmdir, noRuntime, /* isChildInterp */ true) {
@ -255,7 +255,7 @@ namespace cling {
// Inform the Translation Unit Decl of I2 that it has to search somewhere
// else to find the declarations.
getCI()->getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage();
getCI()->getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage(true);
// Give my IncrementalExecutor a pointer to the Incremental executor of the
// parent Interpreter.
@ -639,6 +639,34 @@ namespace cling {
return Result;
}
Interpreter::CompilationResult
Interpreter::codeComplete(const std::string& input, unsigned offset) {
CompilationOptions CO;
CO.DeclarationExtraction = 0;
CO.ValuePrinting = 0;
CO.ResultEvaluation = 0;
CO.DynamicScoping = isDynamicLookupEnabled();
CO.Debug = isPrintingDebug();
CO.CheckPointerValidity = 0;
CO.CodeCompletionOffset = offset;
std::string wrappedInput = input;
std::string wrapperName;
if (ShouldWrapInput(input))
WrapInput(wrappedInput, wrapperName);
StateDebuggerRAII stateDebugger(this);
// This triggers the FileEntry to be created and the completion
// point to be set in clang.
m_IncrParser->Parse(wrappedInput, CO);
return kSuccess;
}
Interpreter::CompilationResult
Interpreter::declare(const std::string& input, Transaction** T/*=0 */) {
CompilationOptions CO;

View File

@ -9,9 +9,11 @@
#include "cling/Interpreter/InterpreterCallbacks.h"
#include "cling/Interpreter/ClingCodeCompleteConsumer.h"
#include "cling/Interpreter/Interpreter.h"
#include "clang/AST/ASTContext.h"
#include "clang/Sema/CodeCompleteConsumer.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Sema/Sema.h"
@ -239,6 +241,27 @@ namespace cling {
return false;
}
void InterpreterCallbacks::CreateCodeCompleteConsumer(Interpreter* child) const {
ClingCodeCompleteConsumer* consumer = new ClingCodeCompleteConsumer(
m_Interpreter->getCI()->getFrontendOpts().CodeCompleteOpts,
llvm::outs());
CompilerInstance* childCI = child->getCI();
//codeCompletionCI will own consumer!
childCI->setCodeCompletionConsumer(consumer);
clang::Sema& childSemaRef = childCI->getSema();
childSemaRef.CodeCompleter = consumer;
}
void InterpreterCallbacks::GetCompletionResults(Interpreter* child,
std::vector<std::string>& completions) const {
// Which cast to use , fno-rtti
ClingCodeCompleteConsumer* consumer =
(ClingCodeCompleteConsumer*)(
child->getCI()->getSema().CodeCompleter);
if (consumer)
consumer->getCompletions(completions);
}
void InterpreterCallbacks::UpdateWithNewDecls(const DeclContext *DC,
DeclarationName Name,
llvm::ArrayRef<NamedDecl*> Decls) {

View File

@ -25,6 +25,8 @@
#include "clang/Basic/LangOptions.h"
#include "clang/Frontend/CompilerInstance.h"
#include "ClingTabCompletion.h"
// Fragment copied from LLVM's raw_ostream.cpp
#if defined(HAVE_UNISTD_H)
# include <unistd.h>
@ -118,6 +120,11 @@ namespace cling {
std::unique_ptr<TerminalDisplay> D(TerminalDisplay::Create());
TextInput TI(*R, *D, histfilePath.empty() ? 0 : histfilePath.c_str());
// Inform text input about the code complete consumer
ClingTabCompletion* CompletionConsumer = new ClingTabCompletion(m_MetaProcessor->getInterpreter());
TI.SetCompletion(CompletionConsumer);
TI.SetPrompt("[cling]$ ");
std::string line;
while (true) {