//===- CIndexCodeCompletion.cpp - Code Completion API hooks ---------------===// // // 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 Clang-C Source Indexing library hooks for // code completion. // //===----------------------------------------------------------------------===// #include "CIndexer.h" #include "CIndexDiagnostic.h" #include "CLog.h" #include "CXCursor.h" #include "CXSourceLocation.h" #include "CXString.h" #include "CXTranslationUnit.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Type.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/SourceManager.h" #include "clang/Frontend/ASTUnit.h" #include "clang/Frontend/CompilerInstance.h" #include "clang/Sema/CodeCompleteConsumer.h" #include "clang/Sema/Sema.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Support/CrashRecoveryContext.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/FormatVariadic.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Program.h" #include "llvm/Support/Timer.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #ifdef UDP_CODE_COMPLETION_LOGGER #include "clang/Basic/Version.h" #include #include #include #include #endif using namespace clang; using namespace clang::cxindex; enum CXCompletionChunkKind clang_getCompletionChunkKind(CXCompletionString completion_string, unsigned chunk_number) { CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; if (!CCStr || chunk_number >= CCStr->size()) return CXCompletionChunk_Text; switch ((*CCStr)[chunk_number].Kind) { case CodeCompletionString::CK_TypedText: return CXCompletionChunk_TypedText; case CodeCompletionString::CK_Text: return CXCompletionChunk_Text; case CodeCompletionString::CK_Optional: return CXCompletionChunk_Optional; case CodeCompletionString::CK_Placeholder: return CXCompletionChunk_Placeholder; case CodeCompletionString::CK_Informative: return CXCompletionChunk_Informative; case CodeCompletionString::CK_ResultType: return CXCompletionChunk_ResultType; case CodeCompletionString::CK_CurrentParameter: return CXCompletionChunk_CurrentParameter; case CodeCompletionString::CK_LeftParen: return CXCompletionChunk_LeftParen; case CodeCompletionString::CK_RightParen: return CXCompletionChunk_RightParen; case CodeCompletionString::CK_LeftBracket: return CXCompletionChunk_LeftBracket; case CodeCompletionString::CK_RightBracket: return CXCompletionChunk_RightBracket; case CodeCompletionString::CK_LeftBrace: return CXCompletionChunk_LeftBrace; case CodeCompletionString::CK_RightBrace: return CXCompletionChunk_RightBrace; case CodeCompletionString::CK_LeftAngle: return CXCompletionChunk_LeftAngle; case CodeCompletionString::CK_RightAngle: return CXCompletionChunk_RightAngle; case CodeCompletionString::CK_Comma: return CXCompletionChunk_Comma; case CodeCompletionString::CK_Colon: return CXCompletionChunk_Colon; case CodeCompletionString::CK_SemiColon: return CXCompletionChunk_SemiColon; case CodeCompletionString::CK_Equal: return CXCompletionChunk_Equal; case CodeCompletionString::CK_HorizontalSpace: return CXCompletionChunk_HorizontalSpace; case CodeCompletionString::CK_VerticalSpace: return CXCompletionChunk_VerticalSpace; } llvm_unreachable("Invalid CompletionKind!"); } CXString clang_getCompletionChunkText(CXCompletionString completion_string, unsigned chunk_number) { CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; if (!CCStr || chunk_number >= CCStr->size()) return cxstring::createNull(); switch ((*CCStr)[chunk_number].Kind) { case CodeCompletionString::CK_TypedText: case CodeCompletionString::CK_Text: case CodeCompletionString::CK_Placeholder: case CodeCompletionString::CK_CurrentParameter: case CodeCompletionString::CK_Informative: case CodeCompletionString::CK_LeftParen: case CodeCompletionString::CK_RightParen: case CodeCompletionString::CK_LeftBracket: case CodeCompletionString::CK_RightBracket: case CodeCompletionString::CK_LeftBrace: case CodeCompletionString::CK_RightBrace: case CodeCompletionString::CK_LeftAngle: case CodeCompletionString::CK_RightAngle: case CodeCompletionString::CK_Comma: case CodeCompletionString::CK_ResultType: case CodeCompletionString::CK_Colon: case CodeCompletionString::CK_SemiColon: case CodeCompletionString::CK_Equal: case CodeCompletionString::CK_HorizontalSpace: case CodeCompletionString::CK_VerticalSpace: return cxstring::createRef((*CCStr)[chunk_number].Text); case CodeCompletionString::CK_Optional: // Note: treated as an empty text block. return cxstring::createEmpty(); } llvm_unreachable("Invalid CodeCompletionString Kind!"); } CXCompletionString clang_getCompletionChunkCompletionString(CXCompletionString completion_string, unsigned chunk_number) { CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; if (!CCStr || chunk_number >= CCStr->size()) return nullptr; switch ((*CCStr)[chunk_number].Kind) { case CodeCompletionString::CK_TypedText: case CodeCompletionString::CK_Text: case CodeCompletionString::CK_Placeholder: case CodeCompletionString::CK_CurrentParameter: case CodeCompletionString::CK_Informative: case CodeCompletionString::CK_LeftParen: case CodeCompletionString::CK_RightParen: case CodeCompletionString::CK_LeftBracket: case CodeCompletionString::CK_RightBracket: case CodeCompletionString::CK_LeftBrace: case CodeCompletionString::CK_RightBrace: case CodeCompletionString::CK_LeftAngle: case CodeCompletionString::CK_RightAngle: case CodeCompletionString::CK_Comma: case CodeCompletionString::CK_ResultType: case CodeCompletionString::CK_Colon: case CodeCompletionString::CK_SemiColon: case CodeCompletionString::CK_Equal: case CodeCompletionString::CK_HorizontalSpace: case CodeCompletionString::CK_VerticalSpace: return nullptr; case CodeCompletionString::CK_Optional: // Note: treated as an empty text block. return (*CCStr)[chunk_number].Optional; } llvm_unreachable("Invalid CompletionKind!"); } unsigned clang_getNumCompletionChunks(CXCompletionString completion_string) { CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; return CCStr? CCStr->size() : 0; } unsigned clang_getCompletionPriority(CXCompletionString completion_string) { CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; return CCStr? CCStr->getPriority() : unsigned(CCP_Unlikely); } enum CXAvailabilityKind clang_getCompletionAvailability(CXCompletionString completion_string) { CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; return CCStr? static_cast(CCStr->getAvailability()) : CXAvailability_Available; } unsigned clang_getCompletionNumAnnotations(CXCompletionString completion_string) { CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; return CCStr ? CCStr->getAnnotationCount() : 0; } CXString clang_getCompletionAnnotation(CXCompletionString completion_string, unsigned annotation_number) { CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; return CCStr ? cxstring::createRef(CCStr->getAnnotation(annotation_number)) : cxstring::createNull(); } CXString clang_getCompletionParent(CXCompletionString completion_string, CXCursorKind *kind) { if (kind) *kind = CXCursor_NotImplemented; CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; if (!CCStr) return cxstring::createNull(); return cxstring::createRef(CCStr->getParentContextName()); } CXString clang_getCompletionBriefComment(CXCompletionString completion_string) { CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; if (!CCStr) return cxstring::createNull(); return cxstring::createRef(CCStr->getBriefComment()); } namespace { /// The CXCodeCompleteResults structure we allocate internally; /// the client only sees the initial CXCodeCompleteResults structure. /// /// Normally, clients of CXString shouldn't care whether or not a CXString is /// managed by a pool or by explicitly malloc'ed memory. But /// AllocatedCXCodeCompleteResults outlives the CXTranslationUnit, so we can /// not rely on the StringPool in the TU. struct AllocatedCXCodeCompleteResults : public CXCodeCompleteResults { AllocatedCXCodeCompleteResults(IntrusiveRefCntPtr FileMgr); ~AllocatedCXCodeCompleteResults(); /// Diagnostics produced while performing code completion. SmallVector Diagnostics; /// Allocated API-exposed wrappters for Diagnostics. SmallVector, 8> DiagnosticsWrappers; IntrusiveRefCntPtr DiagOpts; /// Diag object IntrusiveRefCntPtr Diag; /// Language options used to adjust source locations. LangOptions LangOpts; /// File manager, used for diagnostics. IntrusiveRefCntPtr FileMgr; /// Source manager, used for diagnostics. IntrusiveRefCntPtr SourceMgr; /// Temporary buffers that will be deleted once we have finished with /// the code-completion results. SmallVector TemporaryBuffers; /// Allocator used to store globally cached code-completion results. std::shared_ptr CachedCompletionAllocator; /// Allocator used to store code completion results. std::shared_ptr CodeCompletionAllocator; /// Context under which completion occurred. enum clang::CodeCompletionContext::Kind ContextKind; /// A bitfield representing the acceptable completions for the /// current context. unsigned long long Contexts; /// The kind of the container for the current context for completions. enum CXCursorKind ContainerKind; /// The USR of the container for the current context for completions. std::string ContainerUSR; /// a boolean value indicating whether there is complete information /// about the container unsigned ContainerIsIncomplete; /// A string containing the Objective-C selector entered thus far for a /// message send. std::string Selector; /// Vector of fix-its for each completion result that *must* be applied /// before that result for the corresponding completion item. std::vector> FixItsVector; }; } // end anonymous namespace unsigned clang_getCompletionNumFixIts(CXCodeCompleteResults *results, unsigned completion_index) { AllocatedCXCodeCompleteResults *allocated_results = (AllocatedCXCodeCompleteResults *)results; if (!allocated_results || allocated_results->FixItsVector.size() <= completion_index) return 0; return static_cast(allocated_results->FixItsVector[completion_index].size()); } CXString clang_getCompletionFixIt(CXCodeCompleteResults *results, unsigned completion_index, unsigned fixit_index, CXSourceRange *replacement_range) { AllocatedCXCodeCompleteResults *allocated_results = (AllocatedCXCodeCompleteResults *)results; if (!allocated_results || allocated_results->FixItsVector.size() <= completion_index) { if (replacement_range) *replacement_range = clang_getNullRange(); return cxstring::createNull(); } ArrayRef FixIts = allocated_results->FixItsVector[completion_index]; if (FixIts.size() <= fixit_index) { if (replacement_range) *replacement_range = clang_getNullRange(); return cxstring::createNull(); } const FixItHint &FixIt = FixIts[fixit_index]; if (replacement_range) { *replacement_range = cxloc::translateSourceRange( *allocated_results->SourceMgr, allocated_results->LangOpts, FixIt.RemoveRange); } return cxstring::createRef(FixIt.CodeToInsert.c_str()); } /// Tracks the number of code-completion result objects that are /// currently active. /// /// Used for debugging purposes only. static std::atomic CodeCompletionResultObjects; AllocatedCXCodeCompleteResults::AllocatedCXCodeCompleteResults( IntrusiveRefCntPtr FileMgr) : CXCodeCompleteResults(), DiagOpts(new DiagnosticOptions), Diag(new DiagnosticsEngine( IntrusiveRefCntPtr(new DiagnosticIDs), &*DiagOpts)), FileMgr(std::move(FileMgr)), SourceMgr(new SourceManager(*Diag, *this->FileMgr)), CodeCompletionAllocator( std::make_shared()), Contexts(CXCompletionContext_Unknown), ContainerKind(CXCursor_InvalidCode), ContainerIsIncomplete(1) { if (getenv("LIBCLANG_OBJTRACKING")) fprintf(stderr, "+++ %u completion results\n", ++CodeCompletionResultObjects); } AllocatedCXCodeCompleteResults::~AllocatedCXCodeCompleteResults() { delete [] Results; for (unsigned I = 0, N = TemporaryBuffers.size(); I != N; ++I) delete TemporaryBuffers[I]; if (getenv("LIBCLANG_OBJTRACKING")) fprintf(stderr, "--- %u completion results\n", --CodeCompletionResultObjects); } static unsigned long long getContextsForContextKind( enum CodeCompletionContext::Kind kind, Sema &S) { unsigned long long contexts = 0; switch (kind) { case CodeCompletionContext::CCC_OtherWithMacros: { //We can allow macros here, but we don't know what else is permissible //So we'll say the only thing permissible are macros contexts = CXCompletionContext_MacroName; break; } case CodeCompletionContext::CCC_TopLevel: case CodeCompletionContext::CCC_ObjCIvarList: case CodeCompletionContext::CCC_ClassStructUnion: case CodeCompletionContext::CCC_Type: { contexts = CXCompletionContext_AnyType | CXCompletionContext_ObjCInterface; if (S.getLangOpts().CPlusPlus) { contexts |= CXCompletionContext_EnumTag | CXCompletionContext_UnionTag | CXCompletionContext_StructTag | CXCompletionContext_ClassTag | CXCompletionContext_NestedNameSpecifier; } break; } case CodeCompletionContext::CCC_Statement: { contexts = CXCompletionContext_AnyType | CXCompletionContext_ObjCInterface | CXCompletionContext_AnyValue; if (S.getLangOpts().CPlusPlus) { contexts |= CXCompletionContext_EnumTag | CXCompletionContext_UnionTag | CXCompletionContext_StructTag | CXCompletionContext_ClassTag | CXCompletionContext_NestedNameSpecifier; } break; } case CodeCompletionContext::CCC_Expression: { contexts = CXCompletionContext_AnyValue; if (S.getLangOpts().CPlusPlus) { contexts |= CXCompletionContext_AnyType | CXCompletionContext_ObjCInterface | CXCompletionContext_EnumTag | CXCompletionContext_UnionTag | CXCompletionContext_StructTag | CXCompletionContext_ClassTag | CXCompletionContext_NestedNameSpecifier; } break; } case CodeCompletionContext::CCC_ObjCMessageReceiver: { contexts = CXCompletionContext_ObjCObjectValue | CXCompletionContext_ObjCSelectorValue | CXCompletionContext_ObjCInterface; if (S.getLangOpts().CPlusPlus) { contexts |= CXCompletionContext_CXXClassTypeValue | CXCompletionContext_AnyType | CXCompletionContext_EnumTag | CXCompletionContext_UnionTag | CXCompletionContext_StructTag | CXCompletionContext_ClassTag | CXCompletionContext_NestedNameSpecifier; } break; } case CodeCompletionContext::CCC_DotMemberAccess: { contexts = CXCompletionContext_DotMemberAccess; break; } case CodeCompletionContext::CCC_ArrowMemberAccess: { contexts = CXCompletionContext_ArrowMemberAccess; break; } case CodeCompletionContext::CCC_ObjCPropertyAccess: { contexts = CXCompletionContext_ObjCPropertyAccess; break; } case CodeCompletionContext::CCC_EnumTag: { contexts = CXCompletionContext_EnumTag | CXCompletionContext_NestedNameSpecifier; break; } case CodeCompletionContext::CCC_UnionTag: { contexts = CXCompletionContext_UnionTag | CXCompletionContext_NestedNameSpecifier; break; } case CodeCompletionContext::CCC_ClassOrStructTag: { contexts = CXCompletionContext_StructTag | CXCompletionContext_ClassTag | CXCompletionContext_NestedNameSpecifier; break; } case CodeCompletionContext::CCC_ObjCProtocolName: { contexts = CXCompletionContext_ObjCProtocol; break; } case CodeCompletionContext::CCC_Namespace: { contexts = CXCompletionContext_Namespace; break; } case CodeCompletionContext::CCC_SymbolOrNewName: case CodeCompletionContext::CCC_Symbol: { contexts = CXCompletionContext_NestedNameSpecifier; break; } case CodeCompletionContext::CCC_MacroNameUse: { contexts = CXCompletionContext_MacroName; break; } case CodeCompletionContext::CCC_NaturalLanguage: { contexts = CXCompletionContext_NaturalLanguage; break; } case CodeCompletionContext::CCC_IncludedFile: { contexts = CXCompletionContext_IncludedFile; break; } case CodeCompletionContext::CCC_SelectorName: { contexts = CXCompletionContext_ObjCSelectorName; break; } case CodeCompletionContext::CCC_ParenthesizedExpression: { contexts = CXCompletionContext_AnyType | CXCompletionContext_ObjCInterface | CXCompletionContext_AnyValue; if (S.getLangOpts().CPlusPlus) { contexts |= CXCompletionContext_EnumTag | CXCompletionContext_UnionTag | CXCompletionContext_StructTag | CXCompletionContext_ClassTag | CXCompletionContext_NestedNameSpecifier; } break; } case CodeCompletionContext::CCC_ObjCInstanceMessage: { contexts = CXCompletionContext_ObjCInstanceMessage; break; } case CodeCompletionContext::CCC_ObjCClassMessage: { contexts = CXCompletionContext_ObjCClassMessage; break; } case CodeCompletionContext::CCC_ObjCInterfaceName: { contexts = CXCompletionContext_ObjCInterface; break; } case CodeCompletionContext::CCC_ObjCCategoryName: { contexts = CXCompletionContext_ObjCCategory; break; } case CodeCompletionContext::CCC_Other: case CodeCompletionContext::CCC_ObjCInterface: case CodeCompletionContext::CCC_ObjCImplementation: case CodeCompletionContext::CCC_NewName: case CodeCompletionContext::CCC_MacroName: case CodeCompletionContext::CCC_PreprocessorExpression: case CodeCompletionContext::CCC_PreprocessorDirective: case CodeCompletionContext::CCC_Attribute: case CodeCompletionContext::CCC_TypeQualifiers: { //Only Clang results should be accepted, so we'll set all of the other //context bits to 0 (i.e. the empty set) contexts = CXCompletionContext_Unexposed; break; } case CodeCompletionContext::CCC_Recovery: { //We don't know what the current context is, so we'll return unknown //This is the equivalent of setting all of the other context bits contexts = CXCompletionContext_Unknown; break; } } return contexts; } namespace { class CaptureCompletionResults : public CodeCompleteConsumer { AllocatedCXCodeCompleteResults &AllocatedResults; CodeCompletionTUInfo CCTUInfo; SmallVector StoredResults; CXTranslationUnit *TU; public: CaptureCompletionResults(const CodeCompleteOptions &Opts, AllocatedCXCodeCompleteResults &Results, CXTranslationUnit *TranslationUnit) : CodeCompleteConsumer(Opts), AllocatedResults(Results), CCTUInfo(Results.CodeCompletionAllocator), TU(TranslationUnit) {} ~CaptureCompletionResults() override { Finish(); } void ProcessCodeCompleteResults(Sema &S, CodeCompletionContext Context, CodeCompletionResult *Results, unsigned NumResults) override { StoredResults.reserve(StoredResults.size() + NumResults); if (includeFixIts()) AllocatedResults.FixItsVector.reserve(NumResults); for (unsigned I = 0; I != NumResults; ++I) { CodeCompletionString *StoredCompletion = Results[I].CreateCodeCompletionString(S, Context, getAllocator(), getCodeCompletionTUInfo(), includeBriefComments()); CXCompletionResult R; R.CursorKind = Results[I].CursorKind; R.CompletionString = StoredCompletion; StoredResults.push_back(R); if (includeFixIts()) AllocatedResults.FixItsVector.emplace_back(std::move(Results[I].FixIts)); } enum CodeCompletionContext::Kind contextKind = Context.getKind(); AllocatedResults.ContextKind = contextKind; AllocatedResults.Contexts = getContextsForContextKind(contextKind, S); AllocatedResults.Selector = ""; ArrayRef SelIdents = Context.getSelIdents(); for (ArrayRef::iterator I = SelIdents.begin(), E = SelIdents.end(); I != E; ++I) { if (IdentifierInfo *selIdent = *I) AllocatedResults.Selector += selIdent->getName(); AllocatedResults.Selector += ":"; } QualType baseType = Context.getBaseType(); NamedDecl *D = nullptr; if (!baseType.isNull()) { // Get the declaration for a class/struct/union/enum type if (const TagType *Tag = baseType->getAs()) D = Tag->getDecl(); // Get the @interface declaration for a (possibly-qualified) Objective-C // object pointer type, e.g., NSString* else if (const ObjCObjectPointerType *ObjPtr = baseType->getAs()) D = ObjPtr->getInterfaceDecl(); // Get the @interface declaration for an Objective-C object type else if (const ObjCObjectType *Obj = baseType->getAs()) D = Obj->getInterface(); // Get the class for a C++ injected-class-name else if (const InjectedClassNameType *Injected = baseType->getAs()) D = Injected->getDecl(); } if (D != nullptr) { CXCursor cursor = cxcursor::MakeCXCursor(D, *TU); AllocatedResults.ContainerKind = clang_getCursorKind(cursor); CXString CursorUSR = clang_getCursorUSR(cursor); AllocatedResults.ContainerUSR = clang_getCString(CursorUSR); clang_disposeString(CursorUSR); const Type *type = baseType.getTypePtrOrNull(); if (type) { AllocatedResults.ContainerIsIncomplete = type->isIncompleteType(); } else { AllocatedResults.ContainerIsIncomplete = 1; } } else { AllocatedResults.ContainerKind = CXCursor_InvalidCode; AllocatedResults.ContainerUSR.clear(); AllocatedResults.ContainerIsIncomplete = 1; } } void ProcessOverloadCandidates(Sema &S, unsigned CurrentArg, OverloadCandidate *Candidates, unsigned NumCandidates, SourceLocation OpenParLoc, bool Braced) override { StoredResults.reserve(StoredResults.size() + NumCandidates); for (unsigned I = 0; I != NumCandidates; ++I) { CodeCompletionString *StoredCompletion = Candidates[I].CreateSignatureString(CurrentArg, S, getAllocator(), getCodeCompletionTUInfo(), includeBriefComments(), Braced); CXCompletionResult R; R.CursorKind = CXCursor_OverloadCandidate; R.CompletionString = StoredCompletion; StoredResults.push_back(R); } } CodeCompletionAllocator &getAllocator() override { return *AllocatedResults.CodeCompletionAllocator; } CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo;} private: void Finish() { AllocatedResults.Results = new CXCompletionResult [StoredResults.size()]; AllocatedResults.NumResults = StoredResults.size(); std::memcpy(AllocatedResults.Results, StoredResults.data(), StoredResults.size() * sizeof(CXCompletionResult)); StoredResults.clear(); } }; } static CXCodeCompleteResults * clang_codeCompleteAt_Impl(CXTranslationUnit TU, const char *complete_filename, unsigned complete_line, unsigned complete_column, ArrayRef unsaved_files, unsigned options) { bool IncludeBriefComments = options & CXCodeComplete_IncludeBriefComments; bool SkipPreamble = options & CXCodeComplete_SkipPreamble; bool IncludeFixIts = options & CXCodeComplete_IncludeCompletionsWithFixIts; #ifdef UDP_CODE_COMPLETION_LOGGER #ifdef UDP_CODE_COMPLETION_LOGGER_PORT const llvm::TimeRecord &StartTime = llvm::TimeRecord::getCurrentTime(); #endif #endif bool EnableLogging = getenv("LIBCLANG_CODE_COMPLETION_LOGGING") != nullptr; if (cxtu::isNotUsableTU(TU)) { LOG_BAD_TU(TU); return nullptr; } ASTUnit *AST = cxtu::getASTUnit(TU); if (!AST) return nullptr; CIndexer *CXXIdx = TU->CIdx; if (CXXIdx->isOptEnabled(CXGlobalOpt_ThreadBackgroundPriorityForEditing)) setThreadBackgroundPriority(); ASTUnit::ConcurrencyCheck Check(*AST); // Perform the remapping of source files. SmallVector RemappedFiles; for (auto &UF : unsaved_files) { std::unique_ptr MB = llvm::MemoryBuffer::getMemBufferCopy(getContents(UF), UF.Filename); RemappedFiles.push_back(std::make_pair(UF.Filename, MB.release())); } if (EnableLogging) { // FIXME: Add logging. } // Parse the resulting source file to find code-completion results. AllocatedCXCodeCompleteResults *Results = new AllocatedCXCodeCompleteResults( &AST->getFileManager()); Results->Results = nullptr; Results->NumResults = 0; // Create a code-completion consumer to capture the results. CodeCompleteOptions Opts; Opts.IncludeBriefComments = IncludeBriefComments; Opts.LoadExternal = !SkipPreamble; Opts.IncludeFixIts = IncludeFixIts; CaptureCompletionResults Capture(Opts, *Results, &TU); // Perform completion. std::vector CArgs; for (const auto &Arg : TU->Arguments) CArgs.push_back(Arg.c_str()); std::string CompletionInvocation = llvm::formatv("-code-completion-at={0}:{1}:{2}", complete_filename, complete_line, complete_column) .str(); LibclangInvocationReporter InvocationReporter( *CXXIdx, LibclangInvocationReporter::OperationKind::CompletionOperation, TU->ParsingOptions, CArgs, CompletionInvocation, unsaved_files); AST->CodeComplete(complete_filename, complete_line, complete_column, RemappedFiles, (options & CXCodeComplete_IncludeMacros), (options & CXCodeComplete_IncludeCodePatterns), IncludeBriefComments, Capture, CXXIdx->getPCHContainerOperations(), *Results->Diag, Results->LangOpts, *Results->SourceMgr, *Results->FileMgr, Results->Diagnostics, Results->TemporaryBuffers); Results->DiagnosticsWrappers.resize(Results->Diagnostics.size()); // Keep a reference to the allocator used for cached global completions, so // that we can be sure that the memory used by our code completion strings // doesn't get freed due to subsequent reparses (while the code completion // results are still active). Results->CachedCompletionAllocator = AST->getCachedCompletionAllocator(); #ifdef UDP_CODE_COMPLETION_LOGGER #ifdef UDP_CODE_COMPLETION_LOGGER_PORT const llvm::TimeRecord &EndTime = llvm::TimeRecord::getCurrentTime(); SmallString<256> LogResult; llvm::raw_svector_ostream os(LogResult); // Figure out the language and whether or not it uses PCH. const char *lang = 0; bool usesPCH = false; for (std::vector::iterator I = argv.begin(), E = argv.end(); I != E; ++I) { if (*I == 0) continue; if (strcmp(*I, "-x") == 0) { if (I + 1 != E) { lang = *(++I); continue; } } else if (strcmp(*I, "-include") == 0) { if (I+1 != E) { const char *arg = *(++I); SmallString<512> pchName; { llvm::raw_svector_ostream os(pchName); os << arg << ".pth"; } pchName.push_back('\0'); llvm::sys::fs::file_status stat_results; if (!llvm::sys::fs::status(pchName, stat_results)) usesPCH = true; continue; } } } os << "{ "; os << "\"wall\": " << (EndTime.getWallTime() - StartTime.getWallTime()); os << ", \"numRes\": " << Results->NumResults; os << ", \"diags\": " << Results->Diagnostics.size(); os << ", \"pch\": " << (usesPCH ? "true" : "false"); os << ", \"lang\": \"" << (lang ? lang : "") << '"'; const char *name = getlogin(); os << ", \"user\": \"" << (name ? name : "unknown") << '"'; os << ", \"clangVer\": \"" << getClangFullVersion() << '"'; os << " }"; StringRef res = os.str(); if (res.size() > 0) { do { // Setup the UDP socket. struct sockaddr_in servaddr; bzero(&servaddr, sizeof(servaddr)); servaddr.sin_family = AF_INET; servaddr.sin_port = htons(UDP_CODE_COMPLETION_LOGGER_PORT); if (inet_pton(AF_INET, UDP_CODE_COMPLETION_LOGGER, &servaddr.sin_addr) <= 0) break; int sockfd = socket(AF_INET, SOCK_DGRAM, 0); if (sockfd < 0) break; sendto(sockfd, res.data(), res.size(), 0, (struct sockaddr *)&servaddr, sizeof(servaddr)); close(sockfd); } while (false); } #endif #endif return Results; } CXCodeCompleteResults *clang_codeCompleteAt(CXTranslationUnit TU, const char *complete_filename, unsigned complete_line, unsigned complete_column, struct CXUnsavedFile *unsaved_files, unsigned num_unsaved_files, unsigned options) { LOG_FUNC_SECTION { *Log << TU << ' ' << complete_filename << ':' << complete_line << ':' << complete_column; } if (num_unsaved_files && !unsaved_files) return nullptr; CXCodeCompleteResults *result; auto CodeCompleteAtImpl = [=, &result]() { result = clang_codeCompleteAt_Impl( TU, complete_filename, complete_line, complete_column, llvm::ArrayRef(unsaved_files, num_unsaved_files), options); }; llvm::CrashRecoveryContext CRC; if (!RunSafely(CRC, CodeCompleteAtImpl)) { fprintf(stderr, "libclang: crash detected in code completion\n"); cxtu::getASTUnit(TU)->setUnsafeToFree(true); return nullptr; } else if (getenv("LIBCLANG_RESOURCE_USAGE")) PrintLibclangResourceUsage(TU); return result; } unsigned clang_defaultCodeCompleteOptions(void) { return CXCodeComplete_IncludeMacros; } void clang_disposeCodeCompleteResults(CXCodeCompleteResults *ResultsIn) { if (!ResultsIn) return; AllocatedCXCodeCompleteResults *Results = static_cast(ResultsIn); delete Results; } unsigned clang_codeCompleteGetNumDiagnostics(CXCodeCompleteResults *ResultsIn) { AllocatedCXCodeCompleteResults *Results = static_cast(ResultsIn); if (!Results) return 0; return Results->Diagnostics.size(); } CXDiagnostic clang_codeCompleteGetDiagnostic(CXCodeCompleteResults *ResultsIn, unsigned Index) { AllocatedCXCodeCompleteResults *Results = static_cast(ResultsIn); if (!Results || Index >= Results->Diagnostics.size()) return nullptr; CXStoredDiagnostic *Diag = Results->DiagnosticsWrappers[Index].get(); if (!Diag) Diag = (Results->DiagnosticsWrappers[Index] = std::make_unique( Results->Diagnostics[Index], Results->LangOpts)) .get(); return Diag; } unsigned long long clang_codeCompleteGetContexts(CXCodeCompleteResults *ResultsIn) { AllocatedCXCodeCompleteResults *Results = static_cast(ResultsIn); if (!Results) return 0; return Results->Contexts; } enum CXCursorKind clang_codeCompleteGetContainerKind( CXCodeCompleteResults *ResultsIn, unsigned *IsIncomplete) { AllocatedCXCodeCompleteResults *Results = static_cast(ResultsIn); if (!Results) return CXCursor_InvalidCode; if (IsIncomplete != nullptr) { *IsIncomplete = Results->ContainerIsIncomplete; } return Results->ContainerKind; } CXString clang_codeCompleteGetContainerUSR(CXCodeCompleteResults *ResultsIn) { AllocatedCXCodeCompleteResults *Results = static_cast(ResultsIn); if (!Results) return cxstring::createEmpty(); return cxstring::createRef(Results->ContainerUSR.c_str()); } CXString clang_codeCompleteGetObjCSelector(CXCodeCompleteResults *ResultsIn) { AllocatedCXCodeCompleteResults *Results = static_cast(ResultsIn); if (!Results) return cxstring::createEmpty(); return cxstring::createDup(Results->Selector); } /// Simple utility function that appends a \p New string to the given /// \p Old string, using the \p Buffer for storage. /// /// \param Old The string to which we are appending. This parameter will be /// updated to reflect the complete string. /// /// /// \param New The string to append to \p Old. /// /// \param Buffer A buffer that stores the actual, concatenated string. It will /// be used if the old string is already-non-empty. static void AppendToString(StringRef &Old, StringRef New, SmallString<256> &Buffer) { if (Old.empty()) { Old = New; return; } if (Buffer.empty()) Buffer.append(Old.begin(), Old.end()); Buffer.append(New.begin(), New.end()); Old = Buffer.str(); } /// Get the typed-text blocks from the given code-completion string /// and return them as a single string. /// /// \param String The code-completion string whose typed-text blocks will be /// concatenated. /// /// \param Buffer A buffer used for storage of the completed name. static StringRef GetTypedName(CodeCompletionString *String, SmallString<256> &Buffer) { StringRef Result; for (CodeCompletionString::iterator C = String->begin(), CEnd = String->end(); C != CEnd; ++C) { if (C->Kind == CodeCompletionString::CK_TypedText) AppendToString(Result, C->Text, Buffer); } return Result; } namespace { struct OrderCompletionResults { bool operator()(const CXCompletionResult &XR, const CXCompletionResult &YR) const { CodeCompletionString *X = (CodeCompletionString *)XR.CompletionString; CodeCompletionString *Y = (CodeCompletionString *)YR.CompletionString; SmallString<256> XBuffer; StringRef XText = GetTypedName(X, XBuffer); SmallString<256> YBuffer; StringRef YText = GetTypedName(Y, YBuffer); if (XText.empty() || YText.empty()) return !XText.empty(); int result = XText.compare_insensitive(YText); if (result < 0) return true; if (result > 0) return false; result = XText.compare(YText); return result < 0; } }; } void clang_sortCodeCompletionResults(CXCompletionResult *Results, unsigned NumResults) { std::stable_sort(Results, Results + NumResults, OrderCompletionResults()); }