mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
[CSSimplify] Detect and record optional base infered base for unresolved member that could be an unwraped base member
This commit is contained in:
@@ -7109,6 +7109,95 @@ fixMemberRef(ConstraintSystem &cs, Type baseTy,
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/// Diagnose if the base type is optional, we're referring to a nominal
|
||||
/// type member via the dot syntax and the member name matches
|
||||
/// Optional<T>.{member} or a .none member inferred as non-optional static
|
||||
/// member e.g. let _ : Foo? = .none where Foo has a static member none.
|
||||
static bool attemptUnresolvedMemberFix(ConstraintSystem &cs,
|
||||
ConstraintKind kind, Type baseTy,
|
||||
DeclNameRef member,
|
||||
FunctionRefKind functionRefKind,
|
||||
ConstraintLocator *locator,
|
||||
MemberLookupResult result) {
|
||||
|
||||
if (kind != ConstraintKind::UnresolvedValueMember)
|
||||
return false;
|
||||
|
||||
// None or only one viable candidate, there is no ambiguity.
|
||||
if (result.ViableCandidates.size() <= 1)
|
||||
return false;
|
||||
|
||||
// Only diagnose those situations for static members.
|
||||
if (!baseTy->is<MetatypeType>())
|
||||
return false;
|
||||
|
||||
// Don't diagnose for function members e.g. Foo? = .none(0).
|
||||
if (functionRefKind != FunctionRefKind::Unapplied)
|
||||
return false;
|
||||
|
||||
Type underlyingBaseType = baseTy->getMetatypeInstanceType();
|
||||
if (!underlyingBaseType->getNominalOrBoundGenericNominal())
|
||||
return false;
|
||||
|
||||
if (!underlyingBaseType->getOptionalObjectType())
|
||||
return false;
|
||||
|
||||
auto unwrappedType = underlyingBaseType->lookThroughAllOptionalTypes();
|
||||
bool allOptionalBaseCandidates = true;
|
||||
auto filterViableCandidates =
|
||||
[&](SmallVector<OverloadChoice, 4> &candidates,
|
||||
SmallVector<OverloadChoice, 4> &viableCandidates,
|
||||
bool &allOptionalBase) {
|
||||
for (OverloadChoice choice : candidates) {
|
||||
if (!choice.isDecl())
|
||||
continue;
|
||||
|
||||
auto memberDecl = choice.getDecl();
|
||||
if (isa<FuncDecl>(memberDecl))
|
||||
continue;
|
||||
if (memberDecl->isInstanceMember())
|
||||
continue;
|
||||
|
||||
allOptionalBase &= bool(choice.getBaseType()
|
||||
->getMetatypeInstanceType()
|
||||
->getOptionalObjectType());
|
||||
|
||||
if (auto EED = dyn_cast<EnumElementDecl>(memberDecl)) {
|
||||
if (!EED->hasAssociatedValues())
|
||||
viableCandidates.push_back(choice);
|
||||
} else if (auto VD = dyn_cast<VarDecl>(memberDecl)) {
|
||||
if (unwrappedType->hasTypeVariable() ||
|
||||
VD->getInterfaceType()->isEqual(unwrappedType))
|
||||
viableCandidates.push_back(choice);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
SmallVector<OverloadChoice, 4> viableCandidates;
|
||||
filterViableCandidates(result.ViableCandidates, viableCandidates,
|
||||
allOptionalBaseCandidates);
|
||||
|
||||
// Also none or only one viable candidate after filtering candidates, there is
|
||||
// no ambiguity.
|
||||
if (viableCandidates.size() <= 1)
|
||||
return false;
|
||||
|
||||
// Right now, name lookup only unwraps a single layer of optionality, which
|
||||
// for cases where base type is a multi-optional type e.g. Foo?? so, it only
|
||||
// finds optional base candidates. To produce the correct warning perform an
|
||||
// extra lookup on unwrapped type is required.
|
||||
if (!allOptionalBaseCandidates)
|
||||
return true;
|
||||
|
||||
MemberLookupResult unwrappedResult = cs.performMemberLookup(
|
||||
kind, member, MetatypeType::get(unwrappedType), functionRefKind, locator,
|
||||
/*includeInaccessibleMembers*/ false);
|
||||
SmallVector<OverloadChoice, 4> unwrappedViableCandidates;
|
||||
filterViableCandidates(unwrappedResult.ViableCandidates,
|
||||
unwrappedViableCandidates, allOptionalBaseCandidates);
|
||||
return !unwrappedViableCandidates.empty();
|
||||
}
|
||||
|
||||
ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
|
||||
ConstraintKind kind, Type baseTy, DeclNameRef member, Type memberTy,
|
||||
DeclContext *useDC, FunctionRefKind functionRefKind,
|
||||
@@ -7290,7 +7379,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
|
||||
}
|
||||
|
||||
if (!result.UnviableCandidates.empty()) {
|
||||
// Generate constraints for unvailable choices if they have a fix,
|
||||
// Generate constraints for unavailable choices if they have a fix,
|
||||
// and disable them by default, they'd get picked up in the "salvage" mode.
|
||||
generateConstraints(
|
||||
candidates, memberTy, result.UnviableCandidates, useDC, locator,
|
||||
@@ -7301,6 +7390,21 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
|
||||
});
|
||||
}
|
||||
|
||||
// Attempt to record a warning where the unresolved member could be
|
||||
// ambiguous with optional member. e.g.
|
||||
// enum Foo {
|
||||
// case none
|
||||
// }
|
||||
//
|
||||
// let _: Foo? = .none // Although base is inferred as Optional.none
|
||||
// it could be also.
|
||||
if (attemptUnresolvedMemberFix(*this, kind, baseObjTy, member,
|
||||
functionRefKind, locator, result)) {
|
||||
auto *fix = SpecifyBaseTypeForOptionalUnresolvedMember::create(
|
||||
*this, member, locator);
|
||||
(void)recordFix(fix);
|
||||
}
|
||||
|
||||
if (!candidates.empty()) {
|
||||
addOverloadSet(candidates, locator);
|
||||
return SolutionKind::Solved;
|
||||
@@ -10234,7 +10338,8 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
|
||||
case FixKind::AllowKeyPathWithoutComponents:
|
||||
case FixKind::IgnoreInvalidResultBuilderBody:
|
||||
case FixKind::SpecifyContextualTypeForNil:
|
||||
case FixKind::AllowRefToInvalidDecl: {
|
||||
case FixKind::AllowRefToInvalidDecl:
|
||||
case FixKind::SpecifyBaseTypeForOptionalUnresolvedMember: {
|
||||
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
|
||||
}
|
||||
|
||||
@@ -10341,7 +10446,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
|
||||
if (auto *fnType1 = type1->getAs<FunctionType>()) {
|
||||
// If this is a contextual mismatch between two
|
||||
// function types which we couldn't find a more
|
||||
// speficit fix for. Let's assume that such types
|
||||
// specific fix for. Let's assume that such types
|
||||
// are competely disjoint and adjust impact of
|
||||
// the fix accordingly.
|
||||
if (auto *fnType2 = type2->getAs<FunctionType>()) {
|
||||
|
||||
Reference in New Issue
Block a user