diff --git a/interpreter/cling/include/cling/Interpreter/CIFactory.h b/interpreter/cling/include/cling/Interpreter/CIFactory.h index 1a77e22b2edd5..7ac85d4a15720 100644 --- a/interpreter/cling/include/cling/Interpreter/CIFactory.h +++ b/interpreter/cling/include/cling/Interpreter/CIFactory.h @@ -38,7 +38,8 @@ namespace cling { clang::CompilerInstance* createCI(llvm::StringRef Code, const InvocationOptions& Opts, const char* LLVMDir, std::unique_ptr consumer, - const ModuleFileExtensions& moduleExtensions); + const ModuleFileExtensions& moduleExtensions, + bool AutoComplete = false); clang::CompilerInstance* createCI(MemBufPtr_t Buffer, int Argc, const char* const* Argv, diff --git a/interpreter/cling/include/cling/Interpreter/ClingCodeCompleteConsumer.h b/interpreter/cling/include/cling/Interpreter/ClingCodeCompleteConsumer.h index 43772da6e61f4..5a4f0b2f8d2bb 100644 --- a/interpreter/cling/include/cling/Interpreter/ClingCodeCompleteConsumer.h +++ b/interpreter/cling/include/cling/Interpreter/ClingCodeCompleteConsumer.h @@ -14,6 +14,10 @@ using namespace clang; +namespace clang { + class CompilerInstance; +} + namespace cling { /// \brief Create a new printing code-completion consumer that prints its /// results to the given raw output stream. @@ -50,6 +54,28 @@ namespace cling { completions = m_Completions; } }; + + struct ClingCodeCompleter { + ClingCodeCompleter() = default; + std::string Prefix; + + /// \param InterpCI [in] The compiler instance that is used to trigger code + /// completion + + /// \param Content [in] The string where code completion is triggered. + + /// \param Line [in] The line number of the code completion point. + + /// \param Col [in] The column number of the code completion point. + + /// \param ParentCI [in] The running interpreter compiler instance that + /// provides ASTContexts. + + /// \param CCResults [out] The completion results. + void codeComplete(CompilerInstance* InterpCI, llvm::StringRef Content, + unsigned Line, unsigned Col, CompilerInstance* ParentCI, + std::vector& CCResults); + }; } #endif diff --git a/interpreter/cling/lib/Interpreter/CIFactory.cpp b/interpreter/cling/lib/Interpreter/CIFactory.cpp index 3d7fcf58231bf..515af868ef192 100644 --- a/interpreter/cling/lib/Interpreter/CIFactory.cpp +++ b/interpreter/cling/lib/Interpreter/CIFactory.cpp @@ -42,6 +42,7 @@ #include "llvm/Config/llvm-config.h" #include "llvm/IR/LLVMContext.h" #include "llvm/Option/ArgList.h" +#include "llvm/Support/Error.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Path.h" @@ -1252,13 +1253,61 @@ namespace { } } + // Goal is to make this as close to the function in + // clang/lib/Interpreter/Interpreter.cpp taking only clang arguments + static llvm::Expected> + CreateCI(const std::vector& ClangArgv, std::string ExeName, + std::unique_ptr& Compilation) { + auto InvocationPtr = std::make_shared(); + + // The compiler invocation is the owner of the diagnostic options. + // Everything else points to them. + DiagnosticOptions& DiagOpts = InvocationPtr->getDiagnosticOpts(); + llvm::IntrusiveRefCntPtr Diags = + SetupDiagnostics(DiagOpts, ExeName); + if (!Diags) { + cling::errs() << "Could not setup diagnostic engine.\n"; + return nullptr; + } + + llvm::Triple TheTriple(llvm::sys::getProcessTriple()); + clang::driver::Driver Drvr(ClangArgv[0], TheTriple.getTriple(), *Diags); + // Drvr.setWarnMissingInput(false); + Drvr.setCheckInputsExist(false); // think foo.C(12) + llvm::ArrayRef RF(&(ClangArgv[0]), ClangArgv.size()); + Compilation.reset(Drvr.BuildCompilation(RF)); + if (!Compilation) { + cling::errs() << "Couldn't create clang::driver::Compilation.\n"; + return nullptr; + } + + const llvm::opt::ArgStringList* CC1Args = + GetCC1Arguments(Compilation.get()); + if (!CC1Args) { + cling::errs() << "Could not get cc1 arguments.\n"; + return nullptr; + } + + clang::CompilerInvocation::CreateFromArgs(*InvocationPtr, *CC1Args, *Diags); + // We appreciate the error message about an unknown flag (or do we? if not + // we should switch to a different DiagEngine for parsing the flags). + // But in general we'll happily go on. + Diags->Reset(); + + // Create and setup a compiler instance. + std::unique_ptr CI(new CompilerInstance()); + CI->setInvocation(InvocationPtr); + CI->setDiagnostics(Diags.get()); // Diags is ref-counted + + return CI; + } + static CompilerInstance* createCIImpl(std::unique_ptr Buffer, - const CompilerOptions& COpts, - const char* LLVMDir, + const CompilerOptions& COpts, const char* LLVMDir, std::unique_ptr customConsumer, const CIFactory::ModuleFileExtensions& moduleExtensions, - bool OnlyLex, bool HasInput = false) { + bool OnlyLex, bool HasInput = false, bool AutoComplete = false) { // Follow clang -v convention of printing version on first line if (COpts.Verbose) cling::log() << "cling version " << ClingStringify(CLING_VERSION) << '\n'; @@ -1405,16 +1454,16 @@ namespace { #endif } #endif - - if (!COpts.HasOutput || !HasInput) { + if ((!COpts.HasOutput || !HasInput) && !AutoComplete) { argvCompile.push_back("-"); } - auto InvocationPtr = std::make_shared(); + if (AutoComplete) { + // Put a dummy C++ file on to ensure there's at least one compile job for + // the driver to construct. + argvCompile.push_back("<<< cling interactive line includer >>>"); + } - // The compiler invocation is the owner of the diagnostic options. - // Everything else points to them. - DiagnosticOptions& DiagOpts = InvocationPtr->getDiagnosticOpts(); // add prefix to diagnostic messages if second compiler instance is existing // e.g. in CUDA mode std::string ExeName = ""; @@ -1422,41 +1471,20 @@ namespace { ExeName = "cling"; if (COpts.CUDADevice) ExeName = "cling-ptx"; - llvm::IntrusiveRefCntPtr Diags = - SetupDiagnostics(DiagOpts, ExeName); - if (!Diags) { - cling::errs() << "Could not setup diagnostic engine.\n"; - return nullptr; - } - llvm::Triple TheTriple(llvm::sys::getProcessTriple()); - clang::driver::Driver Drvr(argv[0], TheTriple.getTriple(), *Diags); - //Drvr.setWarnMissingInput(false); - Drvr.setCheckInputsExist(false); // think foo.C(12) - llvm::ArrayRefRF(&(argvCompile[0]), argvCompile.size()); - std::unique_ptr - Compilation(Drvr.BuildCompilation(RF)); - if (!Compilation) { - cling::errs() << "Couldn't create clang::driver::Compilation.\n"; - return nullptr; - } + std::unique_ptr Compilation; - const llvm::opt::ArgStringList* CC1Args = GetCC1Arguments(Compilation.get()); - if (!CC1Args) { - cling::errs() << "Could not get cc1 arguments.\n"; + auto CIOrErr = CreateCI(argvCompile, ExeName, Compilation); + if (!CIOrErr) { + cling::errs() << "Could not create CI: " << CIOrErr.takeError() << "\n"; return nullptr; } + std::unique_ptr CI = std::move(*CIOrErr); - clang::CompilerInvocation::CreateFromArgs(*InvocationPtr, *CC1Args, *Diags); - // We appreciate the error message about an unknown flag (or do we? if not - // we should switch to a different DiagEngine for parsing the flags). - // But in general we'll happily go on. - Diags->Reset(); + DiagnosticsEngine& Diags = CI->getDiagnostics(); + CompilerInvocation& Invocation = CI->getInvocation(); + DiagnosticOptions& DiagOpts = Invocation.getDiagnosticOpts(); - // Create and setup a compiler instance. - std::unique_ptr CI(new CompilerInstance()); - CI->setInvocation(InvocationPtr); - CI->setDiagnostics(Diags.get()); // Diags is ref-counted if (!OnlyLex) CI->getDiagnosticOpts().ShowColors = llvm::sys::Process::StandardOutIsDisplayed() || @@ -1469,9 +1497,9 @@ namespace { // Copied from CompilerInstance::createDiagnostics: // Chain in -verify checker, if requested. if (DiagOpts.VerifyDiagnostics) - Diags->setClient(new clang::VerifyDiagnosticConsumer(*Diags)); + Diags.setClient(new clang::VerifyDiagnosticConsumer(Diags)); // Configure our handling of diagnostics. - ProcessWarningOptions(*Diags, DiagOpts); + ProcessWarningOptions(Diags, DiagOpts); if (COpts.HasOutput && !OnlyLex) { ActionScan scan(clang::driver::Action::PrecompileJobClass, @@ -1484,14 +1512,13 @@ namespace { if (!SetupCompiler(CI.get(), COpts)) return nullptr; - ProcessWarningOptions(*Diags, DiagOpts); + ProcessWarningOptions(Diags, DiagOpts); return CI.release(); } IntrusiveRefCntPtr Overlay = new llvm::vfs::OverlayFileSystem(llvm::vfs::getRealFileSystem()); CI->createFileManager(Overlay); - clang::CompilerInvocation& Invocation = CI->getInvocation(); std::string& PCHFile = Invocation.getPreprocessorOpts().ImplicitPCHInclude; bool InitLang = true, InitTarget = true; if (!PCHFile.empty()) { @@ -1543,14 +1570,14 @@ namespace { // When running interactively pass on the info that the PCH // has failed so that IncrmentalParser::Initialize won't try again. if (!HasInput && llvm::sys::Process::StandardInIsUserInput()) { - const unsigned ID = Diags->getCustomDiagID( - clang::DiagnosticsEngine::Level::Error, - "Problems loading PCH: '%0'."); - - Diags->Report(ID) << PCHFile; + const unsigned ID = + Diags.getCustomDiagID(clang::DiagnosticsEngine::Level::Error, + "Problems loading PCH: '%0'."); + + Diags.Report(ID) << PCHFile; // If this was the only error, then don't let it stop anything - if (Diags->getClient()->getNumErrors() == 1) - Diags->Reset(true); + if (Diags.getClient()->getNumErrors() == 1) + Diags.Reset(true); // Clear the include so no one else uses it. std::string().swap(PCHFile); } @@ -1612,7 +1639,14 @@ namespace { = const_cast(MainFileFI.getContentCache()); if (!Buffer) Buffer = llvm::MemoryBuffer::getMemBuffer("/*CLING DEFAULT MEMBUF*/;\n"); - MainFileCC.setBuffer(std::move(Buffer)); + if (AutoComplete) { + // Adapted from upstream clang/lib/Interpreter/Interpreter.cpp + // FIXME: Merge with CompilerInstance::ExecuteAction. + llvm::MemoryBuffer* MB = Buffer.release(); + CI->getPreprocessorOpts().addRemappedFile(Filename, MB); + } else { + MainFileCC.setBuffer(std::move(Buffer)); + } // Create TargetInfo for the other side of CUDA and OpenMP compilation. if ((CI->getLangOpts().CUDA || CI->getLangOpts().OpenMPIsTargetDevice) && @@ -1780,16 +1814,17 @@ namespace { namespace cling { -CompilerInstance* -CIFactory::createCI(llvm::StringRef Code, const InvocationOptions& Opts, - const char* LLVMDir, - std::unique_ptr consumer, - const ModuleFileExtensions& moduleExtensions) { - return createCIImpl(llvm::MemoryBuffer::getMemBuffer(Code), Opts.CompilerOpts, - LLVMDir, std::move(consumer), moduleExtensions, - false /*OnlyLex*/, - !Opts.IsInteractive()); -} + CompilerInstance* + CIFactory::createCI(llvm::StringRef Code, const InvocationOptions& Opts, + const char* LLVMDir, + std::unique_ptr consumer, + const ModuleFileExtensions& moduleExtensions, + bool AutoComplete /*false*/) { + return createCIImpl(llvm::MemoryBuffer::getMemBuffer(Code), + Opts.CompilerOpts, LLVMDir, std::move(consumer), + moduleExtensions, false /*OnlyLex*/, + !Opts.IsInteractive(), AutoComplete); + } CompilerInstance* CIFactory::createCI( MemBufPtr_t Buffer, int argc, const char* const* argv, const char* LLVMDir, diff --git a/interpreter/cling/lib/Interpreter/ClingCodeCompleteConsumer.cpp b/interpreter/cling/lib/Interpreter/ClingCodeCompleteConsumer.cpp index 3aeb36ca47bc4..556ad26b2727c 100644 --- a/interpreter/cling/lib/Interpreter/ClingCodeCompleteConsumer.cpp +++ b/interpreter/cling/lib/Interpreter/ClingCodeCompleteConsumer.cpp @@ -9,7 +9,12 @@ #include "cling/Interpreter/ClingCodeCompleteConsumer.h" +#include "clang/AST/ASTImporter.h" +#include "clang/AST/DeclLookups.h" #include "clang/Basic/Diagnostic.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/Sema.h" @@ -78,4 +83,185 @@ namespace cling { default: llvm_unreachable("Unknown code completion result Kind."); } } + + // Code copied from: clang/lib/Interpreter/CodeCompletion.cpp + class IncrementalSyntaxOnlyAction : public clang::SyntaxOnlyAction { + const CompilerInstance* ParentCI; + + public: + IncrementalSyntaxOnlyAction(const CompilerInstance* ParentCI) + : ParentCI(ParentCI) {} + + protected: + void ExecuteAction() override; + }; + + class ExternalSource : public clang::ExternalASTSource { + TranslationUnitDecl* ChildTUDeclCtxt; + ASTContext& ParentASTCtxt; + TranslationUnitDecl* ParentTUDeclCtxt; + + std::unique_ptr Importer; + + public: + ExternalSource(ASTContext& ChildASTCtxt, FileManager& ChildFM, + ASTContext& ParentASTCtxt, FileManager& ParentFM); + bool FindExternalVisibleDeclsByName(const DeclContext* DC, + DeclarationName Name) override; + void completeVisibleDeclsMap( + const clang::DeclContext* childDeclContext) override; + }; + + // This method is intended to set up `ExternalASTSource` to the running + // compiler instance before the super `ExecuteAction` triggers parsing + void IncrementalSyntaxOnlyAction::ExecuteAction() { + clang::CompilerInstance& CI = getCompilerInstance(); + ExternalSource* myExternalSource = + new ExternalSource(CI.getASTContext(), CI.getFileManager(), + ParentCI->getASTContext(), + ParentCI->getFileManager()); + llvm::IntrusiveRefCntPtr astContextExternalSource( + myExternalSource); + CI.getASTContext().setExternalSource(astContextExternalSource); + CI.getASTContext().getTranslationUnitDecl()->setHasExternalVisibleStorage( + true); + + // Load all external decls into current context. Under the hood, it calls + // ExternalSource::completeVisibleDeclsMap, which make all decls on the + // redecl chain visible. + // + // This is crucial to code completion on dot members, since a bound variable + // before "." would be otherwise treated out-of-scope. + // + // clang-repl> Foo f1; + // clang-repl> f1. + CI.getASTContext().getTranslationUnitDecl()->lookups(); + SyntaxOnlyAction::ExecuteAction(); + } + + ExternalSource::ExternalSource(ASTContext& ChildASTCtxt, FileManager& ChildFM, + ASTContext& ParentASTCtxt, + FileManager& ParentFM) + : ChildTUDeclCtxt(ChildASTCtxt.getTranslationUnitDecl()), + ParentASTCtxt(ParentASTCtxt), + ParentTUDeclCtxt(ParentASTCtxt.getTranslationUnitDecl()) { + clang::ASTImporter* importer = + new clang::ASTImporter(ChildASTCtxt, ChildFM, ParentASTCtxt, ParentFM, + /*MinimalImport : ON*/ true); + Importer.reset(importer); + } + + bool ExternalSource::FindExternalVisibleDeclsByName(const DeclContext* DC, + DeclarationName Name) { + + IdentifierTable& ParentIdTable = ParentASTCtxt.Idents; + + auto ParentDeclName = + DeclarationName(&(ParentIdTable.get(Name.getAsString()))); + + DeclContext::lookup_result lookup_result = + ParentTUDeclCtxt->lookup(ParentDeclName); + + if (!lookup_result.empty()) { + return true; + } + return false; + } + + void + ExternalSource::completeVisibleDeclsMap(const DeclContext* ChildDeclContext) { + assert(ChildDeclContext && ChildDeclContext == ChildTUDeclCtxt && + "No child decl context!"); + + if (!ChildDeclContext->hasExternalVisibleStorage()) + return; + + for (auto* DeclCtxt = ParentTUDeclCtxt; DeclCtxt != nullptr; + DeclCtxt = DeclCtxt->getPreviousDecl()) { + for (auto& IDeclContext : DeclCtxt->decls()) { + if (!llvm::isa(IDeclContext)) + continue; + + NamedDecl* Decl = llvm::cast(IDeclContext); + + auto DeclOrErr = Importer->Import(Decl); + if (!DeclOrErr) { + // if an error happens, it usually means the decl has already been + // imported or the decl is a result of a failed import. But in our + // case, every import is fresh each time code completion is + // triggered. So Import usually doesn't fail. If it does, it just + // means the related decl can't be used in code completion and we can + // safely drop it. + llvm::consumeError(DeclOrErr.takeError()); + continue; + } + + if (!llvm::isa(*DeclOrErr)) + continue; + + NamedDecl* importedNamedDecl = llvm::cast(*DeclOrErr); + + SetExternalVisibleDeclsForName(ChildDeclContext, + importedNamedDecl->getDeclName(), + importedNamedDecl); + + if (!llvm::isa(importedNamedDecl)) + continue; + + auto* Record = llvm::cast(importedNamedDecl); + + if (auto Err = Importer->ImportDefinition(Decl)) { + // the same as above + llvm::consumeError(std::move(Err)); + continue; + } + + Record->setHasLoadedFieldsFromExternalStorage(true); + for (auto* Meth : Record->methods()) + SetExternalVisibleDeclsForName(ChildDeclContext, Meth->getDeclName(), + Meth); + } + ChildDeclContext->setHasExternalLexicalStorage(false); + } + } + + // Copied and adapted from: clang/lib/Interpreter/CodeCompletion.cpp + void ClingCodeCompleter::codeComplete(clang::CompilerInstance* InterpCI, + llvm::StringRef Content, unsigned Line, + unsigned Col, + clang::CompilerInstance* ParentCI, + std::vector& CCResults) { + const std::string CodeCompletionFileName = "input_line_[Completion]"; + auto DiagOpts = DiagnosticOptions(); + auto consumer = + ClingCodeCompleteConsumer(ParentCI->getFrontendOpts().CodeCompleteOpts, + CCResults); + ; + + auto diag = InterpCI->getDiagnosticsPtr(); + std::unique_ptr AU( + clang::ASTUnit::LoadFromCompilerInvocationAction( + InterpCI->getInvocationPtr(), + std::make_shared(), diag)); + llvm::SmallVector sd = {}; + llvm::SmallVector tb = {}; + InterpCI->getFrontendOpts().Inputs[0] = + FrontendInputFile(CodeCompletionFileName, Language::CXX, + InputKind::Source); + auto Act = std::unique_ptr( + new IncrementalSyntaxOnlyAction(ParentCI)); + std::unique_ptr MB = + llvm::MemoryBuffer::getMemBufferCopy(Content, CodeCompletionFileName); + llvm::SmallVector RemappedFiles; + + RemappedFiles.push_back(std::make_pair(CodeCompletionFileName, MB.get())); + // we don't want the AU destructor to release the memory buffer that MB + // owns twice, because MB handles its resource on its own. + AU->setOwnsRemappedFileBuffers(false); + AU->CodeComplete(CodeCompletionFileName, 1, Col, RemappedFiles, false, + false, false, consumer, + std::make_shared(), *diag, + InterpCI->getLangOpts(), InterpCI->getSourceManager(), + InterpCI->getFileManager(), sd, tb, std::move(Act)); + } } diff --git a/interpreter/cling/lib/Interpreter/Interpreter.cpp b/interpreter/cling/lib/Interpreter/Interpreter.cpp index ddf1332a56150..2fbd36f833d41 100644 --- a/interpreter/cling/lib/Interpreter/Interpreter.cpp +++ b/interpreter/cling/lib/Interpreter/Interpreter.cpp @@ -14,6 +14,7 @@ #endif #include "ClingUtils.h" +#include "DeclCollector.h" #include "DynamicLookup.h" #include "EnterUserCodeRAII.h" #include "ExternalInterpreterSource.h" @@ -41,6 +42,7 @@ #include "cling/Utils/Output.h" #include "cling/Utils/SourceNormalization.h" +#include "cling/Interpreter/CIFactory.h" #include "clang/AST/ASTContext.h" #include "clang/AST/GlobalDecl.h" #include "clang/Basic/SourceManager.h" @@ -1026,7 +1028,6 @@ namespace cling { Interpreter::codeComplete(const std::string& line, size_t& cursor, std::vector& completions) const { - const char * const argV = "cling"; std::string resourceDir = this->getCI()->getHeaderSearchOpts().ResourceDir; // Remove the extra 3 directory names "/lib/clang/3.9.0" StringRef parentResourceDir = llvm::sys::path::parent_path( @@ -1034,44 +1035,18 @@ namespace cling { llvm::sys::path::parent_path(resourceDir))); std::string llvmDir = parentResourceDir.str(); - Interpreter childInterpreter(*this, 1, &argV, llvmDir.c_str()); - if (!childInterpreter.isValid()) - return kFailure; - - auto childCI = childInterpreter.getCI(); - clang::Sema &childSemaRef = childCI->getSema(); - - // Create the CodeCompleteConsumer with InterpreterCallbacks - // from the parent interpreter and set the consumer for the child - // interpreter. - ClingCodeCompleteConsumer* consumer = new ClingCodeCompleteConsumer( - getCI()->getFrontendOpts().CodeCompleteOpts, completions); - // Child interpreter CI will own consumer! - childCI->setCodeCompletionConsumer(consumer); - childSemaRef.CodeCompleter = consumer; - - // Ignore diagnostics when we tab complete. - // This is because we get redefinition errors due to the import of the decls. - clang::IgnoringDiagConsumer* ignoringDiagConsumer = - new clang::IgnoringDiagConsumer(); - childSemaRef.getDiagnostics().setClient(ignoringDiagConsumer, true); - DiagnosticsEngine& parentDiagnostics = this->getCI()->getSema().getDiagnostics(); - - std::unique_ptr ownerDiagConsumer = - parentDiagnostics.takeClient(); - auto clientDiagConsumer = parentDiagnostics.getClient(); - parentDiagnostics.setClient(ignoringDiagConsumer, /*owns*/ false); - - // The child will desirialize decls from *this. We need a transaction RAII. - PushTransactionRAII RAII(this); + // arguments for constructing CI + auto declCollector = std::make_unique(); + const ModuleFileExtensions& moduleExtensions = {}; - // Triger the code completion. - childInterpreter.CodeCompleteInternal(line, cursor); + auto InterpCI = std::unique_ptr( + CIFactory::createCI("\n", getOptions(), llvmDir.c_str(), + std::move(declCollector), moduleExtensions, + /*AutoComplete=*/true)); - // Restore the original diagnostics client for parent interpreter. - parentDiagnostics.setClient(clientDiagConsumer, - ownerDiagConsumer.release() != nullptr); - parentDiagnostics.Reset(/*soft=*/true); + auto CC = ClingCodeCompleter(); + CC.codeComplete(InterpCI.get(), line, 1U, cursor + 1, this->getCI(), + completions); return kSuccess; } diff --git a/interpreter/cling/test/AutoComplete/AutoComplete.C b/interpreter/cling/test/AutoComplete/AutoComplete.C new file mode 100644 index 0000000000000..d180e2bd51cd9 --- /dev/null +++ b/interpreter/cling/test/AutoComplete/AutoComplete.C @@ -0,0 +1,31 @@ +//------------------------------------------------------------------------------ +// CLING - the C++ LLVM-based InterpreterG :) +// +// This file is dual-licensed: you can choose to license it under the University +// of Illinois Open Source License or the GNU Lesser General Public License. See +// LICENSE.TXT for details. +//------------------------------------------------------------------------------ + +// RUN: cat %s | %cling 2>&1 | FileCheck %s + +// Test to check autocomplete functionality (adapted from CppInterOp) + +#include "cling/Interpreter/Interpreter.h" + +gCling->process("int foo = 12;"); +std::vector cc; +size_t cursor = 1; +gCling->codeComplete("f", cursor, cc); + +// We check only for 'float' and 'foo', because they +// must be present in the result. Other hints may appear +// there, depending on the implementation, but these two +// are required to say that the test is working. +size_t cnt = 0; +for (auto& r : cc) { + if (r == "float" || r == "[#int#]foo") cnt++; +} + +cnt +// CHECK: (unsigned long) 2 +.q