Skip to content

Commit dd2820f

Browse files
jrose-appletkremenek
authored andcommitted
Provide fix-its when implementing a protocol method with bridged types. (#2799) (#2823)
This is the protocol version of 7bfdd4a: if a protocol requirement has a newly-bridged type (say, 'URL' instead of 'NSURL'), then any conforming types will need to update their implementations of the requirement. Reuse the override-checking mechanism to do so when we're reasonably confident about it. This slots the checking into the existing protocol diagnostics, which doesn't result in the best user experience. note: protocol requires property 'prop' with type 'Refrigerator?' var prop: Refrigerator? { get } ^ note: candidate has non-matching type 'APPRefrigerator?' var prop: APPRefrigerator? { ^ ~~~~~~~~~~~~~~~~ Refrigerator? But we can come back and improve that later; right now this is better than nothing. rdar://problem/26237030 (cherry picked from commit 258e2bb)
1 parent 3ce7a74 commit dd2820f

File tree

5 files changed

+249
-114
lines changed

5 files changed

+249
-114
lines changed

lib/Sema/MiscDiagnostics.cpp

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,115 @@ bool swift::diagnoseArgumentLabelError(TypeChecker &TC, const Expr *expr,
997997
return true;
998998
}
999999

1000+
bool swift::fixItOverrideDeclarationTypes(TypeChecker &TC,
1001+
InFlightDiagnostic &diag,
1002+
ValueDecl *decl,
1003+
const ValueDecl *base) {
1004+
// For now, just rewrite cases where the base uses a value type and the
1005+
// override uses a reference type, and the value type is bridged to the
1006+
// reference type. This is a way to migrate code that makes use of types
1007+
// that previously were not bridged to value types.
1008+
auto checkType = [&](Type overrideTy, Type baseTy,
1009+
SourceRange typeRange) -> bool {
1010+
if (typeRange.isInvalid())
1011+
return false;
1012+
if (!baseTy) {
1013+
decl->dump();
1014+
base->dump();
1015+
}
1016+
1017+
auto normalizeType = [](Type ty) -> Type {
1018+
ty = ty->getInOutObjectType();
1019+
if (Type unwrappedTy = ty->getAnyOptionalObjectType())
1020+
ty = unwrappedTy;
1021+
return ty;
1022+
};
1023+
1024+
// Is the base type bridged?
1025+
Type normalizedBaseTy = normalizeType(baseTy);
1026+
const DeclContext *DC = decl->getDeclContext();
1027+
Optional<Type> maybeBridged =
1028+
TC.Context.getBridgedToObjC(DC, normalizedBaseTy, &TC);
1029+
1030+
// ...and just knowing that it's bridged isn't good enough if we don't
1031+
// know what it's bridged /to/. Also, don't do this check for trivial
1032+
// bridging---that doesn't count.
1033+
Type bridged = maybeBridged.getValueOr(Type());
1034+
if (!bridged || bridged->isEqual(normalizedBaseTy))
1035+
return false;
1036+
1037+
// ...and is it bridged to the overridden type?
1038+
Type normalizedOverrideTy = normalizeType(overrideTy);
1039+
if (!bridged->isEqual(normalizedOverrideTy)) {
1040+
// If both are nominal types, check again, ignoring generic arguments.
1041+
auto *overrideNominal = normalizedOverrideTy->getAnyNominal();
1042+
if (!overrideNominal || bridged->getAnyNominal() != overrideNominal) {
1043+
return false;
1044+
}
1045+
}
1046+
1047+
Type newOverrideTy = baseTy;
1048+
1049+
// Preserve optionality if we're dealing with a simple type.
1050+
OptionalTypeKind OTK;
1051+
if (Type unwrappedTy = newOverrideTy->getAnyOptionalObjectType())
1052+
newOverrideTy = unwrappedTy;
1053+
if (overrideTy->getAnyOptionalObjectType(OTK))
1054+
newOverrideTy = OptionalType::get(OTK, newOverrideTy);
1055+
1056+
SmallString<32> baseTypeBuf;
1057+
llvm::raw_svector_ostream baseTypeStr(baseTypeBuf);
1058+
PrintOptions options;
1059+
options.SynthesizeSugarOnTypes = true;
1060+
1061+
newOverrideTy->print(baseTypeStr, options);
1062+
diag.fixItReplace(typeRange, baseTypeStr.str());
1063+
return true;
1064+
};
1065+
1066+
if (auto *var = dyn_cast<VarDecl>(decl)) {
1067+
SourceRange typeRange = var->getTypeSourceRangeForDiagnostics();
1068+
return checkType(var->getType(), base->getType(), typeRange);
1069+
}
1070+
1071+
if (auto *fn = dyn_cast<AbstractFunctionDecl>(decl)) {
1072+
auto *baseFn = cast<AbstractFunctionDecl>(base);
1073+
bool fixedAny = false;
1074+
if (fn->getParameterLists().back()->size() ==
1075+
baseFn->getParameterLists().back()->size()) {
1076+
for_each(*fn->getParameterLists().back(),
1077+
*baseFn->getParameterLists().back(),
1078+
[&](ParamDecl *param, const ParamDecl *baseParam) {
1079+
fixedAny |= fixItOverrideDeclarationTypes(TC, diag, param, baseParam);
1080+
});
1081+
}
1082+
if (auto *method = dyn_cast<FuncDecl>(decl)) {
1083+
auto *baseMethod = cast<FuncDecl>(base);
1084+
fixedAny |= checkType(method->getBodyResultType(),
1085+
baseMethod->getResultType(),
1086+
method->getBodyResultTypeLoc().getSourceRange());
1087+
}
1088+
return fixedAny;
1089+
}
1090+
1091+
if (auto *subscript = dyn_cast<SubscriptDecl>(decl)) {
1092+
auto *baseSubscript = cast<SubscriptDecl>(base);
1093+
bool fixedAny = false;
1094+
for_each(*subscript->getIndices(),
1095+
*baseSubscript->getIndices(),
1096+
[&](ParamDecl *param, const ParamDecl *baseParam) {
1097+
fixedAny |= fixItOverrideDeclarationTypes(TC, diag, param, baseParam);
1098+
});
1099+
fixedAny |= checkType(subscript->getElementType(),
1100+
baseSubscript->getElementType(),
1101+
subscript->getElementTypeLoc().getSourceRange());
1102+
return fixedAny;
1103+
}
1104+
1105+
llvm_unreachable("unknown overridable member");
1106+
}
1107+
1108+
10001109

10011110
//===----------------------------------------------------------------------===//
10021111
// Diagnose availability.

lib/Sema/MiscDiagnostics.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,16 @@ void fixItAvailableAttrRename(TypeChecker &TC,
6868
SourceRange referenceRange,
6969
const AvailableAttr *attr,
7070
const CallExpr *CE);
71+
72+
/// Attempt to fix the type of \p decl so that it's a valid override for
73+
/// \p base...but only if we're highly confident that we know what the user
74+
/// should have written.
75+
///
76+
/// \returns true iff any fix-its were attached to \p diag.
77+
bool fixItOverrideDeclarationTypes(TypeChecker &TC,
78+
InFlightDiagnostic &diag,
79+
ValueDecl *decl,
80+
const ValueDecl *base);
7181
} // namespace swift
7282

7383
#endif // SWIFT_SEMA_MISC_DIAGNOSTICS_H

lib/Sema/TypeCheckDecl.cpp

Lines changed: 1 addition & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -4810,115 +4810,6 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
48104810
type = fnType->withExtInfo(extInfo);
48114811
}
48124812

4813-
/// Attempt to fix the type of \p decl so that it's a valid override for
4814-
/// \p base...but only if we're highly confident that we know what the user
4815-
/// should have written.
4816-
///
4817-
/// \returns true iff any fix-its were attached to \p diag.
4818-
static bool fixOverrideDeclarationTypes(InFlightDiagnostic &diag,
4819-
TypeChecker &TC,
4820-
ValueDecl *decl,
4821-
const ValueDecl *base) {
4822-
// For now, just rewrite cases where the base uses a value type and the
4823-
// override uses a reference type, and the value type is bridged to the
4824-
// reference type. This is a way to migrate code that makes use of types
4825-
// that previously were not bridged to value types.
4826-
auto checkType = [&](Type overrideTy, Type baseTy,
4827-
SourceRange typeRange) -> bool {
4828-
if (typeRange.isInvalid())
4829-
return false;
4830-
4831-
auto normalizeType = [](Type ty) -> Type {
4832-
ty = ty->getInOutObjectType();
4833-
if (Type unwrappedTy = ty->getAnyOptionalObjectType())
4834-
ty = unwrappedTy;
4835-
return ty;
4836-
};
4837-
4838-
// Is the base type bridged?
4839-
Type normalizedBaseTy = normalizeType(baseTy);
4840-
const DeclContext *DC = decl->getDeclContext();
4841-
Optional<Type> maybeBridged =
4842-
TC.Context.getBridgedToObjC(DC, normalizedBaseTy, &TC);
4843-
4844-
// ...and just knowing that it's bridged isn't good enough if we don't
4845-
// know what it's bridged /to/. Also, don't do this check for trivial
4846-
// bridging---that doesn't count.
4847-
Type bridged = maybeBridged.getValueOr(Type());
4848-
if (!bridged || bridged->isEqual(normalizedBaseTy))
4849-
return false;
4850-
4851-
// ...and is it bridged to the overridden type?
4852-
Type normalizedOverrideTy = normalizeType(overrideTy);
4853-
if (!bridged->isEqual(normalizedOverrideTy)) {
4854-
// If both are nominal types, check again, ignoring generic arguments.
4855-
auto *overrideNominal = normalizedOverrideTy->getAnyNominal();
4856-
if (!overrideNominal || bridged->getAnyNominal() != overrideNominal) {
4857-
return false;
4858-
}
4859-
}
4860-
4861-
Type newOverrideTy = baseTy;
4862-
4863-
// Preserve optionality if we're dealing with a simple type.
4864-
OptionalTypeKind OTK;
4865-
if (Type unwrappedTy = newOverrideTy->getAnyOptionalObjectType())
4866-
newOverrideTy = unwrappedTy;
4867-
if (overrideTy->getAnyOptionalObjectType(OTK))
4868-
newOverrideTy = OptionalType::get(OTK, newOverrideTy);
4869-
4870-
SmallString<32> baseTypeBuf;
4871-
llvm::raw_svector_ostream baseTypeStr(baseTypeBuf);
4872-
PrintOptions options;
4873-
options.SynthesizeSugarOnTypes = true;
4874-
4875-
newOverrideTy->print(baseTypeStr, options);
4876-
diag.fixItReplace(typeRange, baseTypeStr.str());
4877-
return true;
4878-
};
4879-
4880-
if (auto *var = dyn_cast<VarDecl>(decl)) {
4881-
SourceRange typeRange = var->getTypeSourceRangeForDiagnostics();
4882-
return checkType(var->getType(), base->getType(), typeRange);
4883-
}
4884-
4885-
if (auto *fn = dyn_cast<AbstractFunctionDecl>(decl)) {
4886-
auto *baseFn = cast<AbstractFunctionDecl>(base);
4887-
bool fixedAny = false;
4888-
if (fn->getParameterLists().back()->size() ==
4889-
baseFn->getParameterLists().back()->size()) {
4890-
for_each(*fn->getParameterLists().back(),
4891-
*baseFn->getParameterLists().back(),
4892-
[&](ParamDecl *param, const ParamDecl *baseParam) {
4893-
fixedAny |= fixOverrideDeclarationTypes(diag, TC, param, baseParam);
4894-
});
4895-
}
4896-
if (auto *method = dyn_cast<FuncDecl>(decl)) {
4897-
auto *baseMethod = cast<FuncDecl>(base);
4898-
fixedAny |= checkType(method->getBodyResultType(),
4899-
baseMethod->getBodyResultType(),
4900-
method->getBodyResultTypeLoc().getSourceRange());
4901-
}
4902-
return fixedAny;
4903-
}
4904-
4905-
if (auto *subscript = dyn_cast<SubscriptDecl>(decl)) {
4906-
auto *baseSubscript = cast<SubscriptDecl>(base);
4907-
bool fixedAny = false;
4908-
for_each(*subscript->getIndices(),
4909-
*baseSubscript->getIndices(),
4910-
[&](ParamDecl *param, const ParamDecl *baseParam) {
4911-
fixedAny |= fixOverrideDeclarationTypes(diag, TC, param, baseParam);
4912-
});
4913-
fixedAny |= checkType(subscript->getElementType(),
4914-
baseSubscript->getElementType(),
4915-
subscript->getElementTypeLoc().getSourceRange());
4916-
return fixedAny;
4917-
}
4918-
4919-
llvm_unreachable("unknown overridable member");
4920-
}
4921-
49224813
/// If the difference between the types of \p decl and \p base is something
49234814
/// we feel confident about fixing (even partially), emit a note with fix-its
49244815
/// attached. Otherwise, no note will be emitted.
@@ -4953,7 +4844,7 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
49534844
base->getDescriptiveKind(), baseTy));
49544845
}
49554846

4956-
if (fixOverrideDeclarationTypes(*activeDiag, TC, decl, base))
4847+
if (fixItOverrideDeclarationTypes(TC, *activeDiag, decl, base))
49574848
return true;
49584849
}
49594850

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1733,11 +1733,14 @@ diagnoseMatch(TypeChecker &tc, Module *module,
17331733
// about them.
17341734
break;
17351735

1736-
case MatchKind::TypeConflict:
1737-
tc.diagnose(match.Witness, diag::protocol_witness_type_conflict,
1738-
getTypeForDisplay(tc, module, match.Witness),
1739-
withAssocTypes);
1736+
case MatchKind::TypeConflict: {
1737+
auto diag = tc.diagnose(match.Witness, diag::protocol_witness_type_conflict,
1738+
getTypeForDisplay(tc, module, match.Witness),
1739+
withAssocTypes);
1740+
if (!isa<TypeDecl>(req))
1741+
fixItOverrideDeclarationTypes(tc, diag, match.Witness, req);
17401742
break;
1743+
}
17411744

17421745
case MatchKind::ThrowsConflict:
17431746
tc.diagnose(match.Witness, diag::protocol_witness_throws_conflict);

0 commit comments

Comments
 (0)