//===--- CodeCompletionContext.cpp ----------------------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2022 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// #include "swift/Basic/Assertions.h" #include "swift/IDE/CodeCompletionContext.h" #include "swift/IDE/CodeCompletionCache.h" using namespace swift; using namespace swift::ide; std::vector CodeCompletionContext::sortCompletionResults( ArrayRef Results) { std::vector SortedResults(Results.begin(), Results.end()); std::sort(SortedResults.begin(), SortedResults.end(), [](const auto &LHS, const auto &RHS) { int Result = StringRef(LHS->getFilterName()) .compare_insensitive(RHS->getFilterName()); // If the case insensitive comparison is equal, then secondary // sort order should be case sensitive. if (Result == 0) Result = LHS->getFilterName().compare(RHS->getFilterName()); return Result < 0; }); return SortedResults; } static MutableArrayRef copyCodeCompletionResults( CodeCompletionResultSink &targetSink, CodeCompletionCache::Value &source, CodeCompletionFilter filter, const ExpectedTypeContext *TypeContext, const DeclContext *DC, bool CanCurrDeclContextHandleAsync) { assert(filter && "Should never have an empty filter"); // We will be adding foreign results (from another sink) into TargetSink. // TargetSink should have an owning pointer to the allocator that keeps the // results alive. targetSink.ForeignAllocators.push_back(source.Allocator); auto startSize = targetSink.Results.size(); CodeCompletionMacroRoles expectedMacroRoles = getCompletionMacroRoles(filter); std::function shouldIncludeResult = [filter, expectedMacroRoles]( const ContextFreeCodeCompletionResult *R) -> bool { if (R->getKind() != CodeCompletionResultKind::Declaration) return false; switch (R->getAssociatedDeclKind()) { case CodeCompletionDeclKind::EnumElement: case CodeCompletionDeclKind::Constructor: case CodeCompletionDeclKind::Destructor: case CodeCompletionDeclKind::Subscript: case CodeCompletionDeclKind::StaticMethod: case CodeCompletionDeclKind::InstanceMethod: case CodeCompletionDeclKind::PrefixOperatorFunction: case CodeCompletionDeclKind::PostfixOperatorFunction: case CodeCompletionDeclKind::InfixOperatorFunction: case CodeCompletionDeclKind::FreeFunction: case CodeCompletionDeclKind::StaticVar: case CodeCompletionDeclKind::InstanceVar: case CodeCompletionDeclKind::LocalVar: case CodeCompletionDeclKind::GlobalVar: return filter.contains(CodeCompletionFilterFlag::Expr); case CodeCompletionDeclKind::Module: case CodeCompletionDeclKind::Class: case CodeCompletionDeclKind::Actor: case CodeCompletionDeclKind::Struct: case CodeCompletionDeclKind::Enum: case CodeCompletionDeclKind::Protocol: case CodeCompletionDeclKind::TypeAlias: case CodeCompletionDeclKind::AssociatedType: case CodeCompletionDeclKind::GenericTypeParam: return filter.contains(CodeCompletionFilterFlag::Type); case CodeCompletionDeclKind::PrecedenceGroup: return filter.contains(CodeCompletionFilterFlag::PrecedenceGroup); case CodeCompletionDeclKind::Macro: return (bool)(R->getMacroRoles() & expectedMacroRoles); } llvm_unreachable("Unhandled associated decl kind"); }; USRBasedTypeContext USRTypeContext(TypeContext, source.USRTypeArena); for (auto contextFreeResult : source.Results) { if (!shouldIncludeResult(contextFreeResult)) { continue; } CodeCompletionResultTypeRelation typeRelation = contextFreeResult->calculateContextualTypeRelation(DC, TypeContext, &USRTypeContext); ContextualNotRecommendedReason notRecommendedReason = contextFreeResult->calculateContextualNotRecommendedReason( ContextualNotRecommendedReason::None, CanCurrDeclContextHandleAsync); ASTContext *Ctx = DC ? &DC->getASTContext() : nullptr; auto contextualResult = new (*targetSink.Allocator) CodeCompletionResult( *contextFreeResult, Ctx, SemanticContextKind::OtherModule, CodeCompletionFlair(), /*numBytesToErase=*/0, typeRelation, notRecommendedReason); targetSink.Results.push_back(contextualResult); } return llvm::MutableArrayRef(targetSink.Results.data() + startSize, targetSink.Results.size() - startSize); } void CodeCompletionContext::addResultsFromModules( ArrayRef RequestedModules, const ExpectedTypeContext &TypeContext, const DeclContext *DC, bool CanCurrDeclContextHandleAsync) { // Use the current SourceFile as the DeclContext so that we can use it to // perform qualified lookup, and to get the correct visibility for // @testable imports. Also it cannot use 'DC' since it would apply decl // context changes to cached results. const SourceFile *SF = DC->getParentSourceFile(); for (auto &R : RequestedModules) { // FIXME(thread-safety): lock the whole AST context. We might load a // module. std::optional V = Cache.get(R.Key); if (!V.has_value()) { // No cached results found. Fill the cache. V = Cache.createValue(); // Temporary sink in which we gather the result. The cache value retains // the sink's allocator. CodeCompletionResultSink Sink; Sink.annotateResult = getAnnotateResult(); Sink.addInitsToTopLevel = getAddInitsToTopLevel(); Sink.includeObjectLiterals = includeObjectLiterals(); Sink.addCallWithNoDefaultArgs = addCallWithNoDefaultArgs(); Sink.verifyUSRToDecl = verifyUSRToDecl(); Sink.setProduceContextFreeResults((*V)->USRTypeArena); lookupCodeCompletionResultsFromModule(Sink, R.TheModule, R.Key.AccessPath, R.Key.ResultsHaveLeadingDot, SF); (*V)->Allocator = Sink.Allocator; auto &CachedResults = (*V)->Results; CachedResults.reserve(Sink.Results.size()); // Instead of copying the context free results out of the sink's allocator // retain the sink's entire allocator (which also includes the contextual // properities) and simply store pointers to the context free results that // back the contextual results. for (auto Result : Sink.Results) { assert( Result->getContextFreeResult().getResultType().isBackedByUSRs() && "Results stored in the cache should have their result types backed " "by a USR because the cache might outlive the ASTContext the " "results were created from."); CachedResults.push_back(&Result->getContextFreeResult()); } Cache.set(R.Key, *V); } assert(V.has_value()); auto newItems = copyCodeCompletionResults(getResultSink(), **V, R.Filter, &TypeContext, DC, CanCurrDeclContextHandleAsync); postProcessCompletionResults(newItems, CodeCompletionKind, DC, &getResultSink()); } }