mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[CodeCompletion] Enable context type analysis for implict member expression
At argument part of implict member expression, we need context type analysis to complete arguments. rdar://problem/50696432
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
#include "swift/AST/Initializer.h"
|
||||
#include "swift/AST/LazyResolver.h"
|
||||
#include "swift/AST/Module.h"
|
||||
#include "swift/AST/ParameterList.h"
|
||||
#include "swift/AST/Pattern.h"
|
||||
#include "swift/AST/Stmt.h"
|
||||
#include "swift/AST/Type.h"
|
||||
@@ -289,7 +290,9 @@ static void collectPossibleCalleesByQualifiedLookup(
|
||||
Type declaredMemberType = VD->getInterfaceType();
|
||||
if (VD->getDeclContext()->isTypeContext()) {
|
||||
if (isa<FuncDecl>(VD)) {
|
||||
if (!isOnMetaType)
|
||||
if (!isOnMetaType && VD->isStatic())
|
||||
continue;
|
||||
if (isOnMetaType == VD->isStatic())
|
||||
declaredMemberType =
|
||||
declaredMemberType->castTo<AnyFunctionType>()->getResult();
|
||||
} else if (isa<ConstructorDecl>(VD)) {
|
||||
@@ -407,6 +410,31 @@ static bool collectPossibleCalleesForSubscript(
|
||||
return !candidates.empty();
|
||||
}
|
||||
|
||||
/// For the given \p unresolvedMemberExpr, collect possible callee types and
|
||||
/// declarations.
|
||||
static bool collectPossibleCalleesForUnresolvedMember(
|
||||
DeclContext &DC, UnresolvedMemberExpr *unresolvedMemberExpr,
|
||||
SmallVectorImpl<FunctionTypeAndDecl> &candidates) {
|
||||
auto currModule = DC.getParentModule();
|
||||
auto baseName = unresolvedMemberExpr->getName().getBaseName();
|
||||
|
||||
// Get the context of the expression itself.
|
||||
ExprContextInfo contextInfo(&DC, unresolvedMemberExpr);
|
||||
for (auto expectedTy : contextInfo.getPossibleTypes()) {
|
||||
if (!expectedTy->mayHaveMembers())
|
||||
continue;
|
||||
SmallVector<FunctionTypeAndDecl, 2> members;
|
||||
collectPossibleCalleesByQualifiedLookup(DC, MetatypeType::get(expectedTy),
|
||||
baseName, members);
|
||||
for (auto member : members) {
|
||||
if (isReferenceableByImplicitMemberExpr(currModule, &DC, expectedTy,
|
||||
member.second))
|
||||
candidates.push_back(member);
|
||||
}
|
||||
}
|
||||
return !candidates.empty();
|
||||
}
|
||||
|
||||
/// Get index of \p CCExpr in \p Args. \p Args is usually a \c TupleExpr
|
||||
/// or \c ParenExpr.
|
||||
/// \returns \c true if success, \c false if \p CCExpr is not a part of \p Args.
|
||||
@@ -470,6 +498,11 @@ class ExprContextAnalyzer {
|
||||
if (!collectPossibleCalleesForSubscript(*DC, subscriptExpr, Candidates))
|
||||
return false;
|
||||
Arg = subscriptExpr->getIndex();
|
||||
} else if (auto *unresolvedMemberExpr = dyn_cast<UnresolvedMemberExpr>(E)) {
|
||||
if (!collectPossibleCalleesForUnresolvedMember(*DC, unresolvedMemberExpr,
|
||||
Candidates))
|
||||
return false;
|
||||
Arg = unresolvedMemberExpr->getArgument();
|
||||
} else {
|
||||
llvm_unreachable("unexpected expression kind");
|
||||
}
|
||||
@@ -484,7 +517,8 @@ class ExprContextAnalyzer {
|
||||
// Collect possible types (or labels) at the position.
|
||||
{
|
||||
bool MayNeedName = !HasName && !E->isImplicit() &&
|
||||
(isa<CallExpr>(E) | isa<SubscriptExpr>(E));
|
||||
(isa<CallExpr>(E) | isa<SubscriptExpr>(E) ||
|
||||
isa<UnresolvedMemberExpr>(E));
|
||||
SmallPtrSet<TypeBase *, 4> seenTypes;
|
||||
SmallPtrSet<Identifier, 4> seenNames;
|
||||
for (auto &typeAndDecl : Candidates) {
|
||||
@@ -493,18 +527,30 @@ class ExprContextAnalyzer {
|
||||
memberDC = typeAndDecl.second->getInnermostDeclContext();
|
||||
|
||||
auto Params = typeAndDecl.first->getParams();
|
||||
if (Position >= Params.size())
|
||||
continue;
|
||||
const auto &Param = Params[Position];
|
||||
if (Param.hasLabel() && MayNeedName) {
|
||||
if (seenNames.insert(Param.getLabel()).second)
|
||||
recordPossibleName(Param.getLabel().str());
|
||||
} else {
|
||||
Type ty = Param.getOldType();
|
||||
if (memberDC && ty->hasTypeParameter())
|
||||
ty = memberDC->mapTypeIntoContext(ty);
|
||||
if (seenTypes.insert(ty.getPointer()).second)
|
||||
recordPossibleType(ty);
|
||||
ParameterList *paramList = nullptr;
|
||||
if (auto VD = typeAndDecl.second) {
|
||||
if (auto FD = dyn_cast<AbstractFunctionDecl>(VD))
|
||||
paramList = FD->getParameters();
|
||||
else if (auto SD = dyn_cast<SubscriptDecl>(VD))
|
||||
paramList = SD->getIndices();
|
||||
if (paramList && paramList->size() != Params.size())
|
||||
paramList = nullptr;
|
||||
}
|
||||
for (auto Pos = Position; Pos < Params.size(); ++Pos) {
|
||||
const auto &Param = Params[Pos];
|
||||
if (Param.hasLabel() && MayNeedName) {
|
||||
if (seenNames.insert(Param.getLabel()).second)
|
||||
recordPossibleName(Param.getLabel().str());
|
||||
if (paramList && paramList->get(Position)->isDefaultArgument())
|
||||
continue;
|
||||
} else {
|
||||
Type ty = Param.getOldType();
|
||||
if (memberDC && ty->hasTypeParameter())
|
||||
ty = memberDC->mapTypeIntoContext(ty);
|
||||
if (seenTypes.insert(ty.getPointer()).second)
|
||||
recordPossibleType(ty);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -515,6 +561,7 @@ class ExprContextAnalyzer {
|
||||
switch (Parent->getKind()) {
|
||||
case ExprKind::Call:
|
||||
case ExprKind::Subscript:
|
||||
case ExprKind::UnresolvedMember:
|
||||
case ExprKind::Binary:
|
||||
case ExprKind::PrefixUnary: {
|
||||
analyzeApplyExpr(Parent);
|
||||
@@ -706,11 +753,14 @@ public:
|
||||
case ExprKind::Assign:
|
||||
case ExprKind::Array:
|
||||
return true;
|
||||
case ExprKind::UnresolvedMember:
|
||||
return true;
|
||||
case ExprKind::Tuple: {
|
||||
auto ParentE = Parent.getAsExpr();
|
||||
return !ParentE ||
|
||||
(!isa<CallExpr>(ParentE) && !isa<SubscriptExpr>(ParentE) &&
|
||||
!isa<BinaryExpr>(ParentE));
|
||||
!isa<BinaryExpr>(ParentE) &&
|
||||
!isa<UnresolvedMemberExpr>(ParentE));
|
||||
}
|
||||
case ExprKind::Closure:
|
||||
return isSingleExpressionBodyForCodeCompletion(
|
||||
@@ -779,3 +829,75 @@ ExprContextInfo::ExprContextInfo(DeclContext *DC, Expr *TargetExpr) {
|
||||
PossibleCallees, singleExpressionBody);
|
||||
Analyzer.Analyze();
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// isReferenceableByImplicitMemberExpr(ModuleD, DeclContext, Type, ValueDecl)
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
bool swift::ide::isReferenceableByImplicitMemberExpr(
|
||||
ModuleDecl *CurrModule, DeclContext *DC, Type T, ValueDecl *VD) {
|
||||
|
||||
if (VD->isOperator())
|
||||
return false;
|
||||
|
||||
if (!VD->hasInterfaceType())
|
||||
return false;
|
||||
|
||||
if (T->getOptionalObjectType() &&
|
||||
VD->getModuleContext()->isStdlibModule()) {
|
||||
// In optional context, ignore '.init(<some>)', 'init(nilLiteral:)',
|
||||
if (isa<ConstructorDecl>(VD))
|
||||
return false;
|
||||
// TODO: Ignore '.some(<Wrapped>)' and '.none' too *in expression
|
||||
// context*. They are useful in pattern context though.
|
||||
}
|
||||
|
||||
// Enum element decls can always be referenced by implicit member
|
||||
// expression.
|
||||
if (isa<EnumElementDecl>(VD))
|
||||
return true;
|
||||
|
||||
// Only non-failable constructors are implicitly referenceable.
|
||||
if (auto CD = dyn_cast<ConstructorDecl>(VD)) {
|
||||
switch (CD->getFailability()) {
|
||||
case OTK_None:
|
||||
case OTK_ImplicitlyUnwrappedOptional:
|
||||
return true;
|
||||
case OTK_Optional:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, check the result type matches the contextual type.
|
||||
auto declTy = T->getTypeOfMember(CurrModule, VD);
|
||||
if (declTy->is<ErrorType>())
|
||||
return false;
|
||||
|
||||
// Member types can also be implicitly referenceable as long as it's
|
||||
// convertible to the contextual type.
|
||||
if (auto CD = dyn_cast<TypeDecl>(VD)) {
|
||||
declTy = declTy->getMetatypeInstanceType();
|
||||
|
||||
// Emit construction for the same type via typealias doesn't make sense
|
||||
// because we are emitting all `.init()`s.
|
||||
if (declTy->isEqual(T))
|
||||
return false;
|
||||
return swift::isConvertibleTo(declTy, T, *DC);
|
||||
}
|
||||
|
||||
// Only static member can be referenced.
|
||||
if (!VD->isStatic())
|
||||
return false;
|
||||
|
||||
if (isa<FuncDecl>(VD)) {
|
||||
// Strip '(Self.Type) ->' and parameters.
|
||||
declTy = declTy->castTo<AnyFunctionType>()->getResult();
|
||||
declTy = declTy->castTo<AnyFunctionType>()->getResult();
|
||||
} else if (auto FT = declTy->getAs<AnyFunctionType>()) {
|
||||
// The compiler accepts 'static var factory: () -> T' for implicit
|
||||
// member expression.
|
||||
// FIXME: This emits just 'factory'. We should emit 'factory()' instead.
|
||||
declTy = FT->getResult();
|
||||
}
|
||||
return declTy->isEqual(T) || swift::isConvertibleTo(declTy, T, *DC);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user