Skip to content

Commit 258e2bb

Browse files
committed
Provide fix-its when implementing a protocol method with bridged types. (swiftlang#2799)
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
1 parent 3a4f4dd commit 258e2bb

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 ApplyExpr *call);
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
@@ -4783,115 +4783,6 @@ class DeclChecker : public DeclVisitor<DeclChecker> {
47834783
type = fnType->withExtInfo(extInfo);
47844784
}
47854785

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

4929-
if (fixOverrideDeclarationTypes(*activeDiag, TC, decl, base))
4820+
if (fixItOverrideDeclarationTypes(TC, *activeDiag, decl, base))
49304821
return true;
49314822
}
49324823

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)