1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
//===--- 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/IDE/CodeCompletionContext.h"
#include "swift/IDE/CodeCompletionCache.h"
using namespace swift;
using namespace swift::ide;
std::vector<CodeCompletionResult *>
CodeCompletionContext::sortCompletionResults(
ArrayRef<CodeCompletionResult *> Results) {
std::vector<CodeCompletionResult *> 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<CodeCompletionResult *> 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<bool(const ContextFreeCodeCompletionResult *)>
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);
auto contextualResult = new (*targetSink.Allocator) CodeCompletionResult(
*contextFreeResult, 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<RequestedCachedModule> 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<CodeCompletionCache::ValueRefCntPtr> 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.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());
}
}
|