mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[CodeCompletion] Move CompletionOverrideLookup to its own file
This commit is contained in:
@@ -32,10 +32,11 @@
|
||||
#include "swift/ClangImporter/ClangImporter.h"
|
||||
#include "swift/ClangImporter/ClangModule.h"
|
||||
#include "swift/Frontend/FrontendOptions.h"
|
||||
#include "swift/IDE/CodeCompletionStringPrinter.h"
|
||||
#include "swift/IDE/CompletionLookup.h"
|
||||
#include "swift/IDE/CodeCompletionCache.h"
|
||||
#include "swift/IDE/CodeCompletionResultPrinter.h"
|
||||
#include "swift/IDE/CodeCompletionStringPrinter.h"
|
||||
#include "swift/IDE/CompletionLookup.h"
|
||||
#include "swift/IDE/CompletionOverrideLookup.h"
|
||||
#include "swift/IDE/Utils.h"
|
||||
#include "swift/Parse/CodeCompletionCallbacks.h"
|
||||
#include "swift/Sema/CodeCompletionTypeChecking.h"
|
||||
@@ -324,584 +325,6 @@ private:
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
namespace {
|
||||
|
||||
class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
|
||||
CodeCompletionResultSink &Sink;
|
||||
ASTContext &Ctx;
|
||||
const DeclContext *CurrDeclContext;
|
||||
SmallVectorImpl<StringRef> &ParsedKeywords;
|
||||
SourceLoc introducerLoc;
|
||||
|
||||
bool hasFuncIntroducer = false;
|
||||
bool hasVarIntroducer = false;
|
||||
bool hasTypealiasIntroducer = false;
|
||||
bool hasInitializerModifier = false;
|
||||
bool hasAccessModifier = false;
|
||||
bool hasOverride = false;
|
||||
bool hasOverridabilityModifier = false;
|
||||
bool hasStaticOrClass = false;
|
||||
|
||||
public:
|
||||
CompletionOverrideLookup(CodeCompletionResultSink &Sink, ASTContext &Ctx,
|
||||
const DeclContext *CurrDeclContext,
|
||||
SmallVectorImpl<StringRef> &ParsedKeywords,
|
||||
SourceLoc introducerLoc)
|
||||
: Sink(Sink), Ctx(Ctx), CurrDeclContext(CurrDeclContext),
|
||||
ParsedKeywords(ParsedKeywords), introducerLoc(introducerLoc) {
|
||||
hasFuncIntroducer = isKeywordSpecified("func");
|
||||
hasVarIntroducer = isKeywordSpecified("var") ||
|
||||
isKeywordSpecified("let");
|
||||
hasTypealiasIntroducer = isKeywordSpecified("typealias");
|
||||
hasInitializerModifier = isKeywordSpecified("required") ||
|
||||
isKeywordSpecified("convenience");
|
||||
hasAccessModifier = isKeywordSpecified("private") ||
|
||||
isKeywordSpecified("fileprivate") ||
|
||||
isKeywordSpecified("internal") ||
|
||||
isKeywordSpecified("public") ||
|
||||
isKeywordSpecified("open");
|
||||
hasOverride = isKeywordSpecified("override");
|
||||
hasOverridabilityModifier = isKeywordSpecified("final") ||
|
||||
isKeywordSpecified("open");
|
||||
hasStaticOrClass = isKeywordSpecified(getTokenText(tok::kw_class)) ||
|
||||
isKeywordSpecified(getTokenText(tok::kw_static));
|
||||
}
|
||||
|
||||
bool isKeywordSpecified(StringRef Word) {
|
||||
return std::find(ParsedKeywords.begin(), ParsedKeywords.end(), Word)
|
||||
!= ParsedKeywords.end();
|
||||
}
|
||||
|
||||
bool missingOverride(DeclVisibilityKind Reason) {
|
||||
return !hasOverride && Reason == DeclVisibilityKind::MemberOfSuper &&
|
||||
!CurrDeclContext->getSelfProtocolDecl();
|
||||
}
|
||||
|
||||
/// Add an access modifier (i.e. `public`) to \p Builder is necessary.
|
||||
/// Returns \c true if the modifier is actually added, \c false otherwise.
|
||||
bool addAccessControl(const ValueDecl *VD,
|
||||
CodeCompletionResultBuilder &Builder) {
|
||||
auto CurrentNominal = CurrDeclContext->getSelfNominalTypeDecl();
|
||||
assert(CurrentNominal);
|
||||
|
||||
auto AccessOfContext = CurrentNominal->getFormalAccess();
|
||||
if (AccessOfContext < AccessLevel::Public)
|
||||
return false;
|
||||
|
||||
auto Access = VD->getFormalAccess();
|
||||
// Use the greater access between the protocol requirement and the witness.
|
||||
// In case of:
|
||||
//
|
||||
// public protocol P { func foo() }
|
||||
// public class B { func foo() {} }
|
||||
// public class C: B, P {
|
||||
// <complete>
|
||||
// }
|
||||
//
|
||||
// 'VD' is 'B.foo()' which is implicitly 'internal'. But as the overriding
|
||||
// declaration, the user needs to write both 'public' and 'override':
|
||||
//
|
||||
// public class C: B {
|
||||
// public override func foo() {}
|
||||
// }
|
||||
if (Access < AccessLevel::Public &&
|
||||
!isa<ProtocolDecl>(VD->getDeclContext())) {
|
||||
for (auto Conformance : CurrentNominal->getAllConformances()) {
|
||||
Conformance->getRootConformance()->forEachValueWitness(
|
||||
[&](ValueDecl *req, Witness witness) {
|
||||
if (witness.getDecl() == VD)
|
||||
Access = std::max(
|
||||
Access, Conformance->getProtocol()->getFormalAccess());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Access = std::min(Access, AccessOfContext);
|
||||
// Only emit 'public', not needed otherwise.
|
||||
if (Access < AccessLevel::Public)
|
||||
return false;
|
||||
|
||||
Builder.addAccessControlKeyword(Access);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Return type if the result type if \p VD should be represented as opaque
|
||||
/// result type.
|
||||
Type getOpaqueResultType(const ValueDecl *VD, DeclVisibilityKind Reason,
|
||||
DynamicLookupInfo dynamicLookupInfo) {
|
||||
if (Reason !=
|
||||
DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal)
|
||||
return nullptr;
|
||||
|
||||
auto currTy = CurrDeclContext->getDeclaredTypeInContext();
|
||||
if (!currTy)
|
||||
return nullptr;
|
||||
|
||||
Type ResultT;
|
||||
if (auto *FD = dyn_cast<FuncDecl>(VD)) {
|
||||
if (FD->getGenericParams()) {
|
||||
// Generic function cannot have opaque result type.
|
||||
return nullptr;
|
||||
}
|
||||
ResultT = FD->getResultInterfaceType();
|
||||
} else if (auto *SD = dyn_cast<SubscriptDecl>(VD)) {
|
||||
if (SD->getGenericParams()) {
|
||||
// Generic subscript cannot have opaque result type.
|
||||
return nullptr;
|
||||
}
|
||||
ResultT = SD->getElementInterfaceType();
|
||||
} else if (auto *VarD = dyn_cast<VarDecl>(VD)) {
|
||||
ResultT = VarD->getInterfaceType();
|
||||
} else {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!ResultT->is<DependentMemberType>() ||
|
||||
!ResultT->castTo<DependentMemberType>()->getAssocType())
|
||||
// The result is not a valid associatedtype.
|
||||
return nullptr;
|
||||
|
||||
// Try substitution to see if the associated type is resolved to concrete
|
||||
// type.
|
||||
auto substMap = currTy->getMemberSubstitutionMap(
|
||||
CurrDeclContext->getParentModule(), VD);
|
||||
if (!ResultT.subst(substMap)->is<DependentMemberType>())
|
||||
// If resolved print it.
|
||||
return nullptr;
|
||||
|
||||
auto genericSig = VD->getDeclContext()->getGenericSignatureOfContext();
|
||||
|
||||
if (genericSig->isConcreteType(ResultT))
|
||||
// If it has same type requrement, we will emit the concrete type.
|
||||
return nullptr;
|
||||
|
||||
// Collect requirements on the associatedtype.
|
||||
SmallVector<Type, 2> opaqueTypes;
|
||||
bool hasExplicitAnyObject = false;
|
||||
if (auto superTy = genericSig->getSuperclassBound(ResultT))
|
||||
opaqueTypes.push_back(superTy);
|
||||
for (const auto proto : genericSig->getRequiredProtocols(ResultT))
|
||||
opaqueTypes.push_back(proto->getDeclaredInterfaceType());
|
||||
if (auto layout = genericSig->getLayoutConstraint(ResultT))
|
||||
hasExplicitAnyObject = layout->isClass();
|
||||
|
||||
if (!hasExplicitAnyObject) {
|
||||
if (opaqueTypes.empty())
|
||||
return nullptr;
|
||||
if (opaqueTypes.size() == 1)
|
||||
return opaqueTypes.front();
|
||||
}
|
||||
return ProtocolCompositionType::get(
|
||||
VD->getASTContext(), opaqueTypes, hasExplicitAnyObject);
|
||||
}
|
||||
|
||||
void addValueOverride(const ValueDecl *VD, DeclVisibilityKind Reason,
|
||||
DynamicLookupInfo dynamicLookupInfo,
|
||||
CodeCompletionResultBuilder &Builder,
|
||||
bool hasDeclIntroducer) {
|
||||
Type opaqueResultType = getOpaqueResultType(VD, Reason, dynamicLookupInfo);
|
||||
|
||||
class DeclPrinter : public CodeCompletionStringPrinter {
|
||||
Type OpaqueBaseTy;
|
||||
|
||||
public:
|
||||
DeclPrinter(CodeCompletionResultBuilder &Builder, Type OpaqueBaseTy)
|
||||
: CodeCompletionStringPrinter(Builder), OpaqueBaseTy(OpaqueBaseTy) { }
|
||||
|
||||
// As for FuncDecl, SubscriptDecl, and VarDecl, substitute the result type
|
||||
// with 'OpaqueBaseTy' if specified.
|
||||
void printDeclResultTypePre(ValueDecl *VD, TypeLoc &TL) override {
|
||||
if (!OpaqueBaseTy.isNull()) {
|
||||
setNextChunkKind(ChunkKind::Keyword);
|
||||
printText("some ");
|
||||
setNextChunkKind(ChunkKind::Text);
|
||||
TL = TypeLoc::withoutLoc(OpaqueBaseTy);
|
||||
}
|
||||
CodeCompletionStringPrinter::printDeclResultTypePre(VD, TL);
|
||||
}
|
||||
};
|
||||
|
||||
DeclPrinter Printer(Builder, opaqueResultType);
|
||||
Printer.startPreamble();
|
||||
|
||||
bool modifierAdded = false;
|
||||
|
||||
// 'public' if needed.
|
||||
modifierAdded |= !hasAccessModifier && addAccessControl(VD, Builder);
|
||||
|
||||
// 'override' if needed
|
||||
if (missingOverride(Reason)) {
|
||||
Builder.addOverrideKeyword();
|
||||
modifierAdded |= true;
|
||||
}
|
||||
|
||||
// Erase existing introducer (e.g. 'func') if any modifiers are added.
|
||||
if (hasDeclIntroducer && modifierAdded) {
|
||||
auto dist = Ctx.SourceMgr.getByteDistance(
|
||||
introducerLoc, Ctx.SourceMgr.getCodeCompletionLoc());
|
||||
if (dist <= CodeCompletionResult::MaxNumBytesToErase) {
|
||||
Builder.setNumBytesToErase(dist);
|
||||
hasDeclIntroducer = false;
|
||||
}
|
||||
}
|
||||
|
||||
PrintOptions PO;
|
||||
if (auto transformType = CurrDeclContext->getDeclaredTypeInContext())
|
||||
PO.setBaseType(transformType);
|
||||
PO.PrintPropertyAccessors = false;
|
||||
PO.PrintSubscriptAccessors = false;
|
||||
|
||||
PO.SkipUnderscoredKeywords = true;
|
||||
PO.PrintImplicitAttrs = false;
|
||||
PO.ExclusiveAttrList.push_back(TAK_escaping);
|
||||
PO.ExclusiveAttrList.push_back(TAK_autoclosure);
|
||||
// Print certain modifiers only when the introducer is not written.
|
||||
// Otherwise, the user can add it after the completion.
|
||||
if (!hasDeclIntroducer) {
|
||||
PO.ExclusiveAttrList.push_back(DAK_Nonisolated);
|
||||
}
|
||||
|
||||
PO.PrintAccess = false;
|
||||
PO.PrintOverrideKeyword = false;
|
||||
PO.PrintSelfAccessKindKeyword = false;
|
||||
|
||||
PO.PrintStaticKeyword = !hasStaticOrClass && !hasDeclIntroducer;
|
||||
PO.SkipIntroducerKeywords = hasDeclIntroducer;
|
||||
VD->print(Printer, PO);
|
||||
}
|
||||
|
||||
void addMethodOverride(const FuncDecl *FD, DeclVisibilityKind Reason,
|
||||
DynamicLookupInfo dynamicLookupInfo) {
|
||||
CodeCompletionResultBuilder Builder(Sink,
|
||||
CodeCompletionResultKind::Declaration,
|
||||
SemanticContextKind::Super, {});
|
||||
Builder.setResultTypeNotApplicable();
|
||||
Builder.setAssociatedDecl(FD);
|
||||
addValueOverride(FD, Reason, dynamicLookupInfo, Builder, hasFuncIntroducer);
|
||||
Builder.addBraceStmtWithCursor();
|
||||
}
|
||||
|
||||
void addVarOverride(const VarDecl *VD, DeclVisibilityKind Reason,
|
||||
DynamicLookupInfo dynamicLookupInfo) {
|
||||
// Overrides cannot use 'let', but if the 'override' keyword is specified
|
||||
// then the intention is clear, so provide the results anyway. The compiler
|
||||
// can then provide an error telling you to use 'var' instead.
|
||||
// If we don't need override then it's a protocol requirement, so show it.
|
||||
if (missingOverride(Reason) && hasVarIntroducer &&
|
||||
isKeywordSpecified("let"))
|
||||
return;
|
||||
|
||||
CodeCompletionResultBuilder Builder(Sink,
|
||||
CodeCompletionResultKind::Declaration,
|
||||
SemanticContextKind::Super, {});
|
||||
Builder.setAssociatedDecl(VD);
|
||||
addValueOverride(VD, Reason, dynamicLookupInfo, Builder, hasVarIntroducer);
|
||||
}
|
||||
|
||||
void addSubscriptOverride(const SubscriptDecl *SD, DeclVisibilityKind Reason,
|
||||
DynamicLookupInfo dynamicLookupInfo) {
|
||||
CodeCompletionResultBuilder Builder(Sink,
|
||||
CodeCompletionResultKind::Declaration,
|
||||
SemanticContextKind::Super, {});
|
||||
Builder.setResultTypeNotApplicable();
|
||||
Builder.setAssociatedDecl(SD);
|
||||
addValueOverride(SD, Reason, dynamicLookupInfo, Builder, false);
|
||||
Builder.addBraceStmtWithCursor();
|
||||
}
|
||||
|
||||
void addTypeAlias(const AssociatedTypeDecl *ATD, DeclVisibilityKind Reason,
|
||||
DynamicLookupInfo dynamicLookupInfo) {
|
||||
CodeCompletionResultBuilder Builder(Sink,
|
||||
CodeCompletionResultKind::Declaration,
|
||||
SemanticContextKind::Super, {});
|
||||
Builder.setResultTypeNotApplicable();
|
||||
Builder.setAssociatedDecl(ATD);
|
||||
if (!hasTypealiasIntroducer && !hasAccessModifier)
|
||||
(void)addAccessControl(ATD, Builder);
|
||||
if (!hasTypealiasIntroducer)
|
||||
Builder.addDeclIntroducer("typealias ");
|
||||
Builder.addBaseName(ATD->getName().str());
|
||||
Builder.addTextChunk(" = ");
|
||||
Builder.addSimpleNamedParameter("Type");
|
||||
}
|
||||
|
||||
void addConstructor(const ConstructorDecl *CD, DeclVisibilityKind Reason,
|
||||
DynamicLookupInfo dynamicLookupInfo) {
|
||||
CodeCompletionResultBuilder Builder(Sink,
|
||||
CodeCompletionResultKind::Declaration,
|
||||
SemanticContextKind::Super, {});
|
||||
Builder.setResultTypeNotApplicable();
|
||||
Builder.setAssociatedDecl(CD);
|
||||
|
||||
CodeCompletionStringPrinter printer(Builder);
|
||||
printer.startPreamble();
|
||||
|
||||
if (!hasAccessModifier)
|
||||
(void)addAccessControl(CD, Builder);
|
||||
|
||||
if (missingOverride(Reason) && CD->isDesignatedInit() && !CD->isRequired())
|
||||
Builder.addOverrideKeyword();
|
||||
|
||||
// Emit 'required' if we're in class context, 'required' is not specified,
|
||||
// and 1) this is a protocol conformance and the class is not final, or 2)
|
||||
// this is subclass and the initializer is marked as required.
|
||||
bool needRequired = false;
|
||||
auto C = CurrDeclContext->getSelfClassDecl();
|
||||
if (C && !isKeywordSpecified("required")) {
|
||||
switch (Reason) {
|
||||
case DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal:
|
||||
case DeclVisibilityKind::MemberOfProtocolDerivedByCurrentNominal:
|
||||
if (!C->isSemanticallyFinal())
|
||||
needRequired = true;
|
||||
break;
|
||||
case DeclVisibilityKind::MemberOfSuper:
|
||||
if (CD->isRequired())
|
||||
needRequired = true;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
if (needRequired)
|
||||
Builder.addRequiredKeyword();
|
||||
|
||||
{
|
||||
PrintOptions Options;
|
||||
if (auto transformType = CurrDeclContext->getDeclaredTypeInContext())
|
||||
Options.setBaseType(transformType);
|
||||
Options.PrintImplicitAttrs = false;
|
||||
Options.SkipAttributes = true;
|
||||
CD->print(printer, Options);
|
||||
}
|
||||
printer.flush();
|
||||
|
||||
Builder.addBraceStmtWithCursor();
|
||||
}
|
||||
|
||||
// Implement swift::VisibleDeclConsumer.
|
||||
void foundDecl(ValueDecl *D, DeclVisibilityKind Reason,
|
||||
DynamicLookupInfo dynamicLookupInfo) override {
|
||||
if (Reason == DeclVisibilityKind::MemberOfCurrentNominal)
|
||||
return;
|
||||
|
||||
if (D->shouldHideFromEditor())
|
||||
return;
|
||||
|
||||
if (D->isSemanticallyFinal())
|
||||
return;
|
||||
|
||||
bool hasIntroducer = hasFuncIntroducer ||
|
||||
hasVarIntroducer ||
|
||||
hasTypealiasIntroducer;
|
||||
|
||||
if (hasStaticOrClass && !D->isStatic())
|
||||
return;
|
||||
|
||||
// As per the current convention, only instance members are
|
||||
// suggested if an introducer is not accompanied by a 'static' or
|
||||
// 'class' modifier.
|
||||
if (hasIntroducer && !hasStaticOrClass && D->isStatic())
|
||||
return;
|
||||
|
||||
if (auto *FD = dyn_cast<FuncDecl>(D)) {
|
||||
// We cannot override operators as members.
|
||||
if (FD->isBinaryOperator() || FD->isUnaryOperator())
|
||||
return;
|
||||
|
||||
// We cannot override individual accessors.
|
||||
if (isa<AccessorDecl>(FD))
|
||||
return;
|
||||
|
||||
if (hasFuncIntroducer || (!hasIntroducer && !hasInitializerModifier))
|
||||
addMethodOverride(FD, Reason, dynamicLookupInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto *VD = dyn_cast<VarDecl>(D)) {
|
||||
if (hasVarIntroducer || (!hasIntroducer && !hasInitializerModifier))
|
||||
addVarOverride(VD, Reason, dynamicLookupInfo);
|
||||
return;
|
||||
}
|
||||
|
||||
if (auto *SD = dyn_cast<SubscriptDecl>(D)) {
|
||||
if (!hasIntroducer && !hasInitializerModifier)
|
||||
addSubscriptOverride(SD, Reason, dynamicLookupInfo);
|
||||
}
|
||||
|
||||
if (auto *CD = dyn_cast<ConstructorDecl>(D)) {
|
||||
if (!isa<ProtocolDecl>(CD->getDeclContext()))
|
||||
return;
|
||||
if (hasIntroducer || hasOverride || hasOverridabilityModifier ||
|
||||
hasStaticOrClass)
|
||||
return;
|
||||
if (CD->isRequired() || CD->isDesignatedInit())
|
||||
addConstructor(CD, Reason, dynamicLookupInfo);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void addDesignatedInitializers(NominalTypeDecl *NTD) {
|
||||
if (hasFuncIntroducer || hasVarIntroducer || hasTypealiasIntroducer ||
|
||||
hasOverridabilityModifier || hasStaticOrClass)
|
||||
return;
|
||||
|
||||
const auto *CD = dyn_cast<ClassDecl>(NTD);
|
||||
if (!CD)
|
||||
return;
|
||||
if (!CD->hasSuperclass())
|
||||
return;
|
||||
CD = CD->getSuperclassDecl();
|
||||
for (const auto *Member : CD->getMembers()) {
|
||||
const auto *Constructor = dyn_cast<ConstructorDecl>(Member);
|
||||
if (!Constructor)
|
||||
continue;
|
||||
if (Constructor->hasStubImplementation())
|
||||
continue;
|
||||
if (Constructor->isDesignatedInit())
|
||||
addConstructor(Constructor, DeclVisibilityKind::MemberOfSuper, {});
|
||||
}
|
||||
}
|
||||
|
||||
void addAssociatedTypes(NominalTypeDecl *NTD) {
|
||||
if (!hasTypealiasIntroducer &&
|
||||
(hasFuncIntroducer || hasVarIntroducer || hasInitializerModifier ||
|
||||
hasOverride || hasOverridabilityModifier || hasStaticOrClass))
|
||||
return;
|
||||
|
||||
for (auto Conformance : NTD->getAllConformances()) {
|
||||
auto Proto = Conformance->getProtocol();
|
||||
if (!Proto->isAccessibleFrom(CurrDeclContext))
|
||||
continue;
|
||||
for (auto *ATD : Proto->getAssociatedTypeMembers()) {
|
||||
// FIXME: Also exclude the type alias that has already been specified.
|
||||
if (!Conformance->hasTypeWitness(ATD) ||
|
||||
ATD->hasDefaultDefinitionType())
|
||||
continue;
|
||||
addTypeAlias(
|
||||
ATD,
|
||||
DeclVisibilityKind::MemberOfProtocolConformedToByCurrentNominal,
|
||||
{});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static StringRef getResultBuilderDocComment(
|
||||
ResultBuilderBuildFunction function) {
|
||||
switch (function) {
|
||||
case ResultBuilderBuildFunction::BuildArray:
|
||||
return "Enables support for..in loops in a result builder by "
|
||||
"combining the results of all iterations into a single result";
|
||||
|
||||
case ResultBuilderBuildFunction::BuildBlock:
|
||||
return "Required by every result builder to build combined results "
|
||||
"from statement blocks";
|
||||
|
||||
case ResultBuilderBuildFunction::BuildEitherFirst:
|
||||
return "With buildEither(second:), enables support for 'if-else' and "
|
||||
"'switch' statements by folding conditional results into a single "
|
||||
"result";
|
||||
|
||||
case ResultBuilderBuildFunction::BuildEitherSecond:
|
||||
return "With buildEither(first:), enables support for 'if-else' and "
|
||||
"'switch' statements by folding conditional results into a single "
|
||||
"result";
|
||||
|
||||
case ResultBuilderBuildFunction::BuildExpression:
|
||||
return "If declared, provides contextual type information for statement "
|
||||
"expressions to translate them into partial results";
|
||||
|
||||
case ResultBuilderBuildFunction::BuildFinalResult:
|
||||
return "If declared, this will be called on the partial result from the "
|
||||
"outermost block statement to produce the final returned result";
|
||||
|
||||
case ResultBuilderBuildFunction::BuildLimitedAvailability:
|
||||
return "If declared, this will be called on the partial result of "
|
||||
"an 'if #available' block to allow the result builder to erase "
|
||||
"type information";
|
||||
|
||||
case ResultBuilderBuildFunction::BuildOptional:
|
||||
return "Enables support for `if` statements that do not have an `else`";
|
||||
}
|
||||
}
|
||||
|
||||
void addResultBuilderBuildCompletion(
|
||||
NominalTypeDecl *builder, Type componentType,
|
||||
ResultBuilderBuildFunction function) {
|
||||
CodeCompletionResultBuilder Builder(Sink, CodeCompletionResultKind::Pattern,
|
||||
SemanticContextKind::CurrentNominal,
|
||||
{});
|
||||
Builder.setResultTypeNotApplicable();
|
||||
|
||||
if (!hasFuncIntroducer) {
|
||||
if (!hasAccessModifier &&
|
||||
builder->getFormalAccess() >= AccessLevel::Public)
|
||||
Builder.addAccessControlKeyword(AccessLevel::Public);
|
||||
|
||||
if (!hasStaticOrClass)
|
||||
Builder.addTextChunk("static ");
|
||||
|
||||
Builder.addTextChunk("func ");
|
||||
}
|
||||
|
||||
std::string declStringWithoutFunc;
|
||||
{
|
||||
llvm::raw_string_ostream out(declStringWithoutFunc);
|
||||
printResultBuilderBuildFunction(
|
||||
builder, componentType, function, None, out);
|
||||
}
|
||||
Builder.addTextChunk(declStringWithoutFunc);
|
||||
Builder.addBraceStmtWithCursor();
|
||||
Builder.setBriefDocComment(getResultBuilderDocComment(function));
|
||||
}
|
||||
|
||||
/// Add completions for the various "build" functions in a result builder.
|
||||
void addResultBuilderBuildCompletions(NominalTypeDecl *builder) {
|
||||
Type componentType = inferResultBuilderComponentType(builder);
|
||||
|
||||
addResultBuilderBuildCompletion(
|
||||
builder, componentType, ResultBuilderBuildFunction::BuildBlock);
|
||||
addResultBuilderBuildCompletion(
|
||||
builder, componentType, ResultBuilderBuildFunction::BuildExpression);
|
||||
addResultBuilderBuildCompletion(
|
||||
builder, componentType, ResultBuilderBuildFunction::BuildOptional);
|
||||
addResultBuilderBuildCompletion(
|
||||
builder, componentType, ResultBuilderBuildFunction::BuildEitherFirst);
|
||||
addResultBuilderBuildCompletion(
|
||||
builder, componentType, ResultBuilderBuildFunction::BuildEitherSecond);
|
||||
addResultBuilderBuildCompletion(
|
||||
builder, componentType, ResultBuilderBuildFunction::BuildArray);
|
||||
addResultBuilderBuildCompletion(
|
||||
builder, componentType,
|
||||
ResultBuilderBuildFunction::BuildLimitedAvailability);
|
||||
addResultBuilderBuildCompletion(
|
||||
builder, componentType, ResultBuilderBuildFunction::BuildFinalResult);
|
||||
}
|
||||
|
||||
void getOverrideCompletions(SourceLoc Loc) {
|
||||
if (!CurrDeclContext->isTypeContext())
|
||||
return;
|
||||
if (isa<ProtocolDecl>(CurrDeclContext))
|
||||
return;
|
||||
|
||||
Type CurrTy = CurrDeclContext->getSelfTypeInContext();
|
||||
auto *NTD = CurrDeclContext->getSelfNominalTypeDecl();
|
||||
if (CurrTy && !CurrTy->is<ErrorType>()) {
|
||||
// Look for overridable static members too.
|
||||
Type Meta = MetatypeType::get(CurrTy);
|
||||
lookupVisibleMemberDecls(*this, Meta, CurrDeclContext,
|
||||
/*includeInstanceMembers=*/true,
|
||||
/*includeDerivedRequirements*/true,
|
||||
/*includeProtocolExtensionMembers*/false);
|
||||
addDesignatedInitializers(NTD);
|
||||
addAssociatedTypes(NTD);
|
||||
}
|
||||
|
||||
if (NTD && NTD->getAttrs().hasAttribute<ResultBuilderAttr>()) {
|
||||
addResultBuilderBuildCompletions(NTD);
|
||||
}
|
||||
}
|
||||
};
|
||||
} // end anonymous namespace
|
||||
|
||||
static void addSelectorModifierKeywords(CodeCompletionResultSink &sink) {
|
||||
auto addKeyword = [&](StringRef Name, CodeCompletionKeywordKind Kind) {
|
||||
CodeCompletionResultBuilder Builder(sink, CodeCompletionResultKind::Keyword,
|
||||
|
||||
Reference in New Issue
Block a user