[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:
Rintaro Ishizaki
2019-05-15 10:07:59 -07:00
parent 60ee9527bb
commit 14d2f7c0a7
3 changed files with 143 additions and 84 deletions

View File

@@ -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);
}