mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Add fix-its when an override has mismatched optionals.
This commit is contained in:
@@ -1329,8 +1329,8 @@ ERROR(override_multiple_decls_arg_mismatch,none,
|
||||
"declaration %0 has different argument names from any potential "
|
||||
"overrides", (DeclName))
|
||||
NOTE(overridden_near_match_here,none,
|
||||
"potential overridden %select{method|initializer}0 %1 here",
|
||||
(bool, DeclName))
|
||||
"potential overridden %0 %1 here",
|
||||
(DescriptiveDeclKind, DeclName))
|
||||
ERROR(override_decl_extension,none,
|
||||
"declarations %select{in extensions|from extensions}0 cannot "
|
||||
"%select{override|be overridden}0 yet", (bool))
|
||||
@@ -1416,6 +1416,12 @@ ERROR(override_throws_objc,none,
|
||||
"overriding a throwing @objc %select{method|initializer}0 with "
|
||||
"a non-throwing %select{method|initializer}0 is not supported", (bool))
|
||||
|
||||
ERROR(override_optional_mismatch,none,
|
||||
"cannot override %0 parameter of type %1 with non-optional type %2",
|
||||
(DescriptiveDeclKind, Type, Type))
|
||||
ERROR(override_optional_result_mismatch,none,
|
||||
"cannot override %0 result type %1 with optional type %2",
|
||||
(DescriptiveDeclKind, Type, Type))
|
||||
WARNING(override_unnecessary_IUO,none,
|
||||
"overriding %0 parameter of type %1 with implicitly unwrapped optional "
|
||||
"type %2",
|
||||
|
||||
@@ -4574,11 +4574,13 @@ public:
|
||||
return FunctionType::get(inputType, resultType, fnType->getExtInfo());
|
||||
}
|
||||
|
||||
/// Diagnose overrides of '(T) -> T?' with '(T!) -> T!'.
|
||||
static void diagnoseUnnecessaryIUOs(TypeChecker &TC,
|
||||
const AbstractFunctionDecl *method,
|
||||
const AbstractFunctionDecl *parentMethod,
|
||||
Type owningTy) {
|
||||
static bool
|
||||
diagnoseMismatchedOptionals(TypeChecker &TC,
|
||||
const AbstractFunctionDecl *method,
|
||||
const AbstractFunctionDecl *parentMethod,
|
||||
Type owningTy,
|
||||
bool treatIUOResultAsError) {
|
||||
bool emittedError = false;
|
||||
Type plainParentTy = adjustSuperclassMemberDeclType(TC, parentMethod,
|
||||
owningTy);
|
||||
const auto *parentTy = plainParentTy->castTo<AnyFunctionType>();
|
||||
@@ -4587,15 +4589,50 @@ public:
|
||||
// Check the parameter types.
|
||||
auto checkParam = [&](const ParamDecl *decl, Type parentParamTy) {
|
||||
Type paramTy = decl->getType();
|
||||
if (!paramTy || !paramTy->getImplicitlyUnwrappedOptionalObjectType())
|
||||
if (!paramTy || !parentParamTy)
|
||||
return;
|
||||
if (!parentParamTy || parentParamTy->getAnyOptionalObjectType())
|
||||
|
||||
OptionalTypeKind paramOTK;
|
||||
(void)paramTy->getAnyOptionalObjectType(paramOTK);
|
||||
if (paramOTK == OTK_Optional)
|
||||
return;
|
||||
|
||||
OptionalTypeKind parentOTK;
|
||||
(void)parentParamTy->getAnyOptionalObjectType(parentOTK);
|
||||
|
||||
TypeLoc TL = decl->getTypeLoc();
|
||||
if (!TL.getTypeRepr())
|
||||
return;
|
||||
|
||||
if (paramOTK == OTK_None) {
|
||||
switch (parentOTK) {
|
||||
case OTK_None:
|
||||
return;
|
||||
case OTK_ImplicitlyUnwrappedOptional:
|
||||
if (!treatIUOResultAsError)
|
||||
return;
|
||||
break;
|
||||
case OTK_Optional:
|
||||
break;
|
||||
}
|
||||
|
||||
emittedError = true;
|
||||
auto diag = TC.diagnose(decl->getStartLoc(),
|
||||
diag::override_optional_mismatch,
|
||||
method->getDescriptiveKind(),
|
||||
parentParamTy, paramTy);
|
||||
if (TL.getTypeRepr()->isSimple()) {
|
||||
diag.fixItInsertAfter(TL.getSourceRange().End, "?");
|
||||
} else {
|
||||
diag.fixItInsert(TL.getSourceRange().Start, "(");
|
||||
diag.fixItInsertAfter(TL.getSourceRange().End, ")?");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (parentOTK != OTK_None)
|
||||
return;
|
||||
|
||||
// Allow silencing this warning using parens.
|
||||
if (isa<ParenType>(TL.getType().getPointer()))
|
||||
return;
|
||||
@@ -4633,14 +4670,39 @@ public:
|
||||
|
||||
auto methodAsFunc = dyn_cast<FuncDecl>(method);
|
||||
if (!methodAsFunc)
|
||||
return;
|
||||
return emittedError;
|
||||
|
||||
// FIXME: This is very nearly the same code as checkParam.
|
||||
auto checkResult = [&](TypeLoc resultTL, Type parentResultTy) {
|
||||
Type resultTy = resultTL.getType();
|
||||
if (!resultTy || !resultTy->getImplicitlyUnwrappedOptionalObjectType())
|
||||
if (!resultTy || !parentResultTy)
|
||||
return;
|
||||
if (!parentResultTy || !parentResultTy->getOptionalObjectType())
|
||||
|
||||
TypeRepr *TR = resultTL.getTypeRepr();
|
||||
if (!TR)
|
||||
return;
|
||||
|
||||
OptionalTypeKind resultOTK;
|
||||
if (!resultTy->getAnyOptionalObjectType(resultOTK))
|
||||
return;
|
||||
|
||||
if (resultOTK == OTK_Optional || treatIUOResultAsError) {
|
||||
if (parentResultTy->getAnyOptionalObjectType())
|
||||
return;
|
||||
emittedError = true;
|
||||
auto diag = TC.diagnose(resultTL.getSourceRange().Start,
|
||||
diag::override_optional_result_mismatch,
|
||||
method->getDescriptiveKind(),
|
||||
parentResultTy, resultTy);
|
||||
if (auto optForm = dyn_cast<OptionalTypeRepr>(TR)) {
|
||||
diag.fixItRemove(optForm->getQuestionLoc());
|
||||
} else if (auto iuoForm =
|
||||
dyn_cast<ImplicitlyUnwrappedOptionalTypeRepr>(TR)) {
|
||||
diag.fixItRemove(iuoForm->getExclamationLoc());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!parentResultTy->getAnyOptionalObjectType())
|
||||
return;
|
||||
|
||||
// Allow silencing this warning using parens.
|
||||
@@ -4652,8 +4714,7 @@ public:
|
||||
method->getDescriptiveKind(), parentResultTy, resultTy)
|
||||
.highlight(resultTL.getSourceRange());
|
||||
|
||||
auto sugaredForm =
|
||||
dyn_cast<ImplicitlyUnwrappedOptionalTypeRepr>(resultTL.getTypeRepr());
|
||||
auto sugaredForm = dyn_cast<ImplicitlyUnwrappedOptionalTypeRepr>(TR);
|
||||
if (sugaredForm) {
|
||||
TC.diagnose(sugaredForm->getExclamationLoc(),
|
||||
diag::override_unnecessary_IUO_use_strict)
|
||||
@@ -4667,6 +4728,7 @@ public:
|
||||
};
|
||||
|
||||
checkResult(methodAsFunc->getBodyResultTypeLoc(), parentTy->getResult());
|
||||
return emittedError;
|
||||
}
|
||||
|
||||
/// Make sure that there is an invalid 'override' attribute on the
|
||||
@@ -4697,6 +4759,71 @@ public:
|
||||
type = fnType->withExtInfo(extInfo);
|
||||
}
|
||||
|
||||
enum class OverrideCheckingAttempt {
|
||||
PerfectMatch,
|
||||
MismatchedOptional,
|
||||
BaseName,
|
||||
BaseNameWithMismatchedOptional,
|
||||
Final
|
||||
};
|
||||
|
||||
friend OverrideCheckingAttempt &operator++(OverrideCheckingAttempt &attempt) {
|
||||
assert(attempt != OverrideCheckingAttempt::Final);
|
||||
attempt = static_cast<OverrideCheckingAttempt>(1+static_cast<int>(attempt));
|
||||
return attempt;
|
||||
}
|
||||
|
||||
struct OverrideMatch {
|
||||
ValueDecl *Decl;
|
||||
bool IsExact;
|
||||
Type SubstType;
|
||||
};
|
||||
|
||||
static void diagnoseGeneralOverrideFailure(TypeChecker &TC,
|
||||
ValueDecl *decl,
|
||||
ArrayRef<OverrideMatch> matches,
|
||||
OverrideCheckingAttempt attempt) {
|
||||
switch (attempt) {
|
||||
case OverrideCheckingAttempt::PerfectMatch:
|
||||
TC.diagnose(decl, diag::override_multiple_decls_base,
|
||||
decl->getFullName());
|
||||
break;
|
||||
case OverrideCheckingAttempt::BaseName:
|
||||
TC.diagnose(decl, diag::override_multiple_decls_arg_mismatch,
|
||||
decl->getFullName());
|
||||
break;
|
||||
case OverrideCheckingAttempt::MismatchedOptional:
|
||||
case OverrideCheckingAttempt::BaseNameWithMismatchedOptional:
|
||||
if (isa<ConstructorDecl>(decl))
|
||||
TC.diagnose(decl, diag::initializer_does_not_override);
|
||||
else if (isa<SubscriptDecl>(decl))
|
||||
TC.diagnose(decl, diag::subscript_does_not_override);
|
||||
else if (isa<VarDecl>(decl))
|
||||
TC.diagnose(decl, diag::property_does_not_override);
|
||||
else
|
||||
TC.diagnose(decl, diag::method_does_not_override);
|
||||
break;
|
||||
case OverrideCheckingAttempt::Final:
|
||||
llvm_unreachable("should have exited already");
|
||||
}
|
||||
|
||||
for (auto match : matches) {
|
||||
auto matchDecl = match.Decl;
|
||||
if (attempt == OverrideCheckingAttempt::PerfectMatch) {
|
||||
TC.diagnose(matchDecl, diag::overridden_here);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto diag = TC.diagnose(matchDecl, diag::overridden_near_match_here,
|
||||
matchDecl->getDescriptiveKind(),
|
||||
matchDecl->getFullName());
|
||||
if (attempt == OverrideCheckingAttempt::BaseName) {
|
||||
TC.fixAbstractFunctionNames(diag, cast<AbstractFunctionDecl>(decl),
|
||||
matchDecl->getFullName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine which method or subscript this method or subscript overrides
|
||||
/// (if any).
|
||||
///
|
||||
@@ -4751,161 +4878,178 @@ public:
|
||||
|
||||
// Look for members with the same name and matching types as this
|
||||
// one.
|
||||
auto attempt = OverrideCheckingAttempt::PerfectMatch;
|
||||
SmallVector<OverrideMatch, 2> matches;
|
||||
auto superclassMetaTy = MetatypeType::get(superclass);
|
||||
bool retried = false;
|
||||
DeclName name = decl->getFullName();
|
||||
|
||||
retry:
|
||||
NameLookupOptions lookupOptions
|
||||
= defaultMemberLookupOptions - NameLookupFlags::DynamicLookup;
|
||||
LookupResult members = TC.lookupMember(decl->getDeclContext(),
|
||||
superclassMetaTy, name,
|
||||
lookupOptions);
|
||||
|
||||
typedef std::tuple<ValueDecl *, bool, Type> MatchType;
|
||||
SmallVector<MatchType, 2> matches;
|
||||
bool hadExactMatch = false;
|
||||
|
||||
for (auto memberResult : members) {
|
||||
auto member = memberResult.Decl;
|
||||
|
||||
if (member->isInvalid())
|
||||
continue;
|
||||
|
||||
if (member->getKind() != decl->getKind())
|
||||
continue;
|
||||
|
||||
if (!member->getDeclContext()->getAsClassOrClassExtensionContext())
|
||||
continue;
|
||||
|
||||
auto parentDecl = cast<ValueDecl>(member);
|
||||
|
||||
// Check whether there are any obvious reasons why the two given
|
||||
// declarations do not have an overriding relationship.
|
||||
if (!areOverrideCompatibleSimple(decl, parentDecl))
|
||||
continue;
|
||||
|
||||
auto parentMethod = dyn_cast<AbstractFunctionDecl>(parentDecl);
|
||||
auto parentStorage = dyn_cast<AbstractStorageDecl>(parentDecl);
|
||||
assert(parentMethod || parentStorage);
|
||||
|
||||
// If both are Objective-C, then match based on selectors or
|
||||
// subscript kind and check the types separately.
|
||||
bool objCMatch = false;
|
||||
if (parentDecl->isObjC() && decl->isObjC()) {
|
||||
if (method) {
|
||||
if (method->getObjCSelector(&TC)
|
||||
== parentMethod->getObjCSelector(&TC))
|
||||
objCMatch = true;
|
||||
} else if (auto *parentSubscript =
|
||||
dyn_cast<SubscriptDecl>(parentStorage)) {
|
||||
// If the subscript kinds don't match, it's not an override.
|
||||
if (subscript->getObjCSubscriptKind(&TC)
|
||||
== parentSubscript->getObjCSubscriptKind(&TC))
|
||||
objCMatch = true;
|
||||
}
|
||||
|
||||
// Properties don't need anything here since they are always
|
||||
// checked by name.
|
||||
}
|
||||
|
||||
// Check whether the types are identical.
|
||||
// FIXME: It's wrong to use the uncurried types here for methods.
|
||||
auto parentDeclTy = adjustSuperclassMemberDeclType(TC, parentDecl,
|
||||
owningTy);
|
||||
parentDeclTy = parentDeclTy->getUnlabeledType(TC.Context);
|
||||
if (method) {
|
||||
parentDeclTy = parentDeclTy->castTo<AnyFunctionType>()->getResult();
|
||||
adjustFunctionTypeForOverride(parentDeclTy);
|
||||
} else {
|
||||
parentDeclTy = parentDeclTy->getReferenceStorageReferent();
|
||||
}
|
||||
|
||||
// Ignore the optionality of initializers when comparing types;
|
||||
// we'll enforce this separately
|
||||
if (ctor) {
|
||||
parentDeclTy = dropResultOptionality(parentDeclTy, 1);
|
||||
|
||||
// Factory methods cannot be overridden.
|
||||
auto parentCtor = cast<ConstructorDecl>(parentDecl);
|
||||
if (parentCtor->isFactoryInit())
|
||||
continue;
|
||||
}
|
||||
|
||||
if (declTy->isEqual(parentDeclTy)) {
|
||||
matches.push_back(std::make_tuple(parentDecl, true, parentDeclTy));
|
||||
hadExactMatch = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this is a property, we accept the match and then reject it below if
|
||||
// the types don't line up, since you can't overload properties based on
|
||||
// types.
|
||||
if (isa<VarDecl>(parentDecl)) {
|
||||
matches.push_back(std::make_tuple(parentDecl, false, parentDeclTy));
|
||||
continue;
|
||||
}
|
||||
|
||||
// Failing that, check for subtyping.
|
||||
auto matchMode = OverrideMatchMode::Strict;
|
||||
if (parentDecl->isObjC())
|
||||
matchMode = OverrideMatchMode::AllowNonOptionalForIUOParam;
|
||||
if (declTy->canOverride(parentDeclTy, matchMode, &TC)) {
|
||||
// If the Objective-C selectors match, always call it exact.
|
||||
matches.push_back(
|
||||
std::make_tuple(parentDecl, objCMatch, parentDeclTy));
|
||||
hadExactMatch |= objCMatch;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Not a match. If we had an Objective-C match, this is a serious problem.
|
||||
if (objCMatch) {
|
||||
if (method) {
|
||||
TC.diagnose(decl, diag::override_objc_type_mismatch_method,
|
||||
method->getObjCSelector(&TC), declTy);
|
||||
} else {
|
||||
TC.diagnose(decl, diag::override_objc_type_mismatch_subscript,
|
||||
static_cast<unsigned>(
|
||||
subscript->getObjCSubscriptKind(&TC)),
|
||||
declTy);
|
||||
}
|
||||
TC.diagnose(parentDecl, diag::overridden_here_with_type,
|
||||
parentDeclTy);
|
||||
|
||||
// Put an invalid 'override' attribute here.
|
||||
makeInvalidOverrideAttr(TC, decl);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If we have no matches.
|
||||
if (matches.empty()) {
|
||||
// If we already re-tried, or if the user didn't indicate that this is
|
||||
// an override, or we don't know what else to look for, try again.
|
||||
if (retried || name.isSimpleName() ||
|
||||
name.getArgumentNames().size() == 0 ||
|
||||
!decl->getAttrs().hasAttribute<OverrideAttr>())
|
||||
do {
|
||||
switch (attempt) {
|
||||
case OverrideCheckingAttempt::PerfectMatch:
|
||||
break;
|
||||
case OverrideCheckingAttempt::MismatchedOptional:
|
||||
// Don't keep looking if the user didn't indicate it's an override.
|
||||
if (!decl->getAttrs().hasAttribute<OverrideAttr>())
|
||||
return false;
|
||||
break;
|
||||
case OverrideCheckingAttempt::BaseName:
|
||||
// Don't keep looking if this is already a simple name, or if there
|
||||
// are no arguments.
|
||||
if (name.isSimpleName() || name.getArgumentNames().empty())
|
||||
return false;
|
||||
name = name.getBaseName();
|
||||
break;
|
||||
case OverrideCheckingAttempt::BaseNameWithMismatchedOptional:
|
||||
break;
|
||||
case OverrideCheckingAttempt::Final:
|
||||
// Give up.
|
||||
return false;
|
||||
}
|
||||
|
||||
// Try looking again, this time using just the base name, so that we'll
|
||||
// catch mismatched names.
|
||||
retried = true;
|
||||
name = name.getBaseName();
|
||||
goto retry;
|
||||
}
|
||||
NameLookupOptions lookupOptions
|
||||
= defaultMemberLookupOptions - NameLookupFlags::DynamicLookup;
|
||||
LookupResult members = TC.lookupMember(decl->getDeclContext(),
|
||||
superclassMetaTy, name,
|
||||
lookupOptions);
|
||||
|
||||
for (auto memberResult : members) {
|
||||
auto member = memberResult.Decl;
|
||||
|
||||
if (member->isInvalid())
|
||||
continue;
|
||||
|
||||
if (member->getKind() != decl->getKind())
|
||||
continue;
|
||||
|
||||
if (!member->getDeclContext()->getAsClassOrClassExtensionContext())
|
||||
continue;
|
||||
|
||||
auto parentDecl = cast<ValueDecl>(member);
|
||||
|
||||
// Check whether there are any obvious reasons why the two given
|
||||
// declarations do not have an overriding relationship.
|
||||
if (!areOverrideCompatibleSimple(decl, parentDecl))
|
||||
continue;
|
||||
|
||||
auto parentMethod = dyn_cast<AbstractFunctionDecl>(parentDecl);
|
||||
auto parentStorage = dyn_cast<AbstractStorageDecl>(parentDecl);
|
||||
assert(parentMethod || parentStorage);
|
||||
|
||||
// If both are Objective-C, then match based on selectors or
|
||||
// subscript kind and check the types separately.
|
||||
bool objCMatch = false;
|
||||
if (parentDecl->isObjC() && decl->isObjC()) {
|
||||
if (method) {
|
||||
if (method->getObjCSelector(&TC)
|
||||
== parentMethod->getObjCSelector(&TC))
|
||||
objCMatch = true;
|
||||
} else if (auto *parentSubscript =
|
||||
dyn_cast<SubscriptDecl>(parentStorage)) {
|
||||
// If the subscript kinds don't match, it's not an override.
|
||||
if (subscript->getObjCSubscriptKind(&TC)
|
||||
== parentSubscript->getObjCSubscriptKind(&TC))
|
||||
objCMatch = true;
|
||||
}
|
||||
|
||||
// Properties don't need anything here since they are always
|
||||
// checked by name.
|
||||
}
|
||||
|
||||
// Check whether the types are identical.
|
||||
// FIXME: It's wrong to use the uncurried types here for methods.
|
||||
auto parentDeclTy = adjustSuperclassMemberDeclType(TC, parentDecl,
|
||||
owningTy);
|
||||
parentDeclTy = parentDeclTy->getUnlabeledType(TC.Context);
|
||||
if (method) {
|
||||
parentDeclTy = parentDeclTy->castTo<AnyFunctionType>()->getResult();
|
||||
adjustFunctionTypeForOverride(parentDeclTy);
|
||||
} else {
|
||||
parentDeclTy = parentDeclTy->getReferenceStorageReferent();
|
||||
}
|
||||
|
||||
// Ignore the optionality of initializers when comparing types;
|
||||
// we'll enforce this separately
|
||||
if (ctor) {
|
||||
parentDeclTy = dropResultOptionality(parentDeclTy, 1);
|
||||
|
||||
// Factory methods cannot be overridden.
|
||||
auto parentCtor = cast<ConstructorDecl>(parentDecl);
|
||||
if (parentCtor->isFactoryInit())
|
||||
continue;
|
||||
}
|
||||
|
||||
if (declTy->isEqual(parentDeclTy)) {
|
||||
matches.push_back({parentDecl, true, parentDeclTy});
|
||||
hadExactMatch = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If this is a property, we accept the match and then reject it below
|
||||
// if the types don't line up, since you can't overload properties based
|
||||
// on types.
|
||||
if (isa<VarDecl>(parentDecl)) {
|
||||
matches.push_back({parentDecl, false, parentDeclTy});
|
||||
continue;
|
||||
}
|
||||
|
||||
// Failing that, check for subtyping.
|
||||
auto matchMode = OverrideMatchMode::Strict;
|
||||
if (attempt == OverrideCheckingAttempt::MismatchedOptional ||
|
||||
attempt == OverrideCheckingAttempt::BaseNameWithMismatchedOptional){
|
||||
matchMode = OverrideMatchMode::AllowTopLevelOptionalMismatch;
|
||||
} else if (parentDecl->isObjC()) {
|
||||
matchMode = OverrideMatchMode::AllowNonOptionalForIUOParam;
|
||||
}
|
||||
|
||||
if (declTy->canOverride(parentDeclTy, matchMode, &TC)) {
|
||||
// If the Objective-C selectors match, always call it exact.
|
||||
matches.push_back({parentDecl, objCMatch, parentDeclTy});
|
||||
hadExactMatch |= objCMatch;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Not a match. If we had an Objective-C match, this is a serious
|
||||
// problem.
|
||||
if (objCMatch) {
|
||||
if (method) {
|
||||
TC.diagnose(decl, diag::override_objc_type_mismatch_method,
|
||||
method->getObjCSelector(&TC), declTy);
|
||||
} else {
|
||||
TC.diagnose(decl, diag::override_objc_type_mismatch_subscript,
|
||||
static_cast<unsigned>(
|
||||
subscript->getObjCSubscriptKind(&TC)),
|
||||
declTy);
|
||||
}
|
||||
TC.diagnose(parentDecl, diag::overridden_here_with_type,
|
||||
parentDeclTy);
|
||||
|
||||
// Put an invalid 'override' attribute here.
|
||||
makeInvalidOverrideAttr(TC, decl);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (!matches.empty())
|
||||
break;
|
||||
|
||||
++attempt;
|
||||
} while (true);
|
||||
|
||||
assert(!matches.empty());
|
||||
|
||||
// If we had an exact match, throw away any non-exact matches.
|
||||
if (hadExactMatch)
|
||||
matches.erase(std::remove_if(matches.begin(), matches.end(),
|
||||
[&](MatchType &match) {
|
||||
return !std::get<1>(match);
|
||||
[&](OverrideMatch &match) {
|
||||
return !match.IsExact;
|
||||
}), matches.end());
|
||||
|
||||
// If we have a single match (exact or not), take it.
|
||||
if (matches.size() == 1) {
|
||||
auto matchDecl = std::get<0>(matches[0]);
|
||||
auto matchType = std::get<2>(matches[0]);
|
||||
auto matchDecl = matches.front().Decl;
|
||||
auto matchType = matches.front().SubstType;
|
||||
bool emittedMatchError = false;
|
||||
|
||||
// If the name of our match differs from the name we were looking for,
|
||||
// complain.
|
||||
@@ -4916,6 +5060,7 @@ public:
|
||||
matchDecl->getFullName());
|
||||
TC.fixAbstractFunctionNames(diag, cast<AbstractFunctionDecl>(decl),
|
||||
matchDecl->getFullName());
|
||||
emittedMatchError = true;
|
||||
}
|
||||
|
||||
// If we have an explicit ownership modifier and our parent doesn't,
|
||||
@@ -4976,13 +5121,16 @@ public:
|
||||
|
||||
} else if (method) {
|
||||
// Private migration help for overrides of Objective-C methods.
|
||||
bool mayHaveMismatchedOptionals =
|
||||
(attempt==OverrideCheckingAttempt::MismatchedOptional ||
|
||||
attempt==OverrideCheckingAttempt::BaseNameWithMismatchedOptional);
|
||||
if ((!isa<FuncDecl>(method) || !cast<FuncDecl>(method)->isAccessor()) &&
|
||||
superclass->getClassOrBoundGenericClass()->isObjC()) {
|
||||
diagnoseUnnecessaryIUOs(TC, method,
|
||||
cast<AbstractFunctionDecl>(matchDecl),
|
||||
owningTy);
|
||||
(matchDecl->isObjC() || mayHaveMismatchedOptionals)) {
|
||||
emittedMatchError |=
|
||||
diagnoseMismatchedOptionals(TC, method,
|
||||
cast<AbstractFunctionDecl>(matchDecl),
|
||||
owningTy, mayHaveMismatchedOptionals);
|
||||
}
|
||||
|
||||
} else if (auto subscript =
|
||||
dyn_cast_or_null<SubscriptDecl>(abstractStorage)) {
|
||||
// Otherwise, if this is a subscript, validate that covariance is ok.
|
||||
@@ -5026,27 +5174,17 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// Catch-all to make sure we don't silently accept something we shouldn't.
|
||||
if (attempt != OverrideCheckingAttempt::PerfectMatch &&
|
||||
!emittedMatchError) {
|
||||
diagnoseGeneralOverrideFailure(TC, decl, matches, attempt);
|
||||
}
|
||||
|
||||
return recordOverride(TC, decl, matchDecl);
|
||||
}
|
||||
|
||||
// We override more than one declaration. Complain.
|
||||
TC.diagnose(decl,
|
||||
retried ? diag::override_multiple_decls_arg_mismatch
|
||||
: diag::override_multiple_decls_base,
|
||||
decl->getFullName());
|
||||
for (auto match : matches) {
|
||||
auto matchDecl = std::get<0>(match);
|
||||
if (retried) {
|
||||
auto diag = TC.diagnose(matchDecl, diag::overridden_near_match_here,
|
||||
isa<ConstructorDecl>(matchDecl),
|
||||
matchDecl->getFullName());
|
||||
TC.fixAbstractFunctionNames(diag, cast<AbstractFunctionDecl>(decl),
|
||||
matchDecl->getFullName());
|
||||
continue;
|
||||
}
|
||||
|
||||
TC.diagnose(std::get<0>(match), diag::overridden_here);
|
||||
}
|
||||
diagnoseGeneralOverrideFailure(TC, decl, matches, attempt);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -258,3 +258,126 @@ _ = C21311590()
|
||||
_ = B21311590()
|
||||
|
||||
|
||||
class MismatchOptionalBase {
|
||||
func param(_: Int?) {}
|
||||
func paramIUO(_: Int!) {}
|
||||
func result() -> Int { return 0 }
|
||||
|
||||
func fixSeveralTypes(a: Int?, b: Int!) -> Int { return 0 }
|
||||
|
||||
func functionParam(x: ((Int) -> Int)?) {}
|
||||
func tupleParam(x: (Int, Int)?) {}
|
||||
|
||||
func nameAndTypeMismatch(label: Int?) {}
|
||||
|
||||
func ambiguousOverride(a: Int, b: Int?) {} // expected-note 2 {{overridden declaration is here}} expected-note {{potential overridden instance method 'ambiguousOverride(a:b:)' here}}
|
||||
func ambiguousOverride(a: Int?, b: Int) {} // expected-note 2 {{overridden declaration is here}} expected-note {{potential overridden instance method 'ambiguousOverride(a:b:)' here}}
|
||||
|
||||
var prop: Int = 0 // expected-note {{attempt to override property here}}
|
||||
var optProp: Int? // expected-note {{attempt to override property here}}
|
||||
|
||||
var getProp: Int { return 0 } // expected-note {{attempt to override property here}}
|
||||
var getOptProp: Int? { return nil }
|
||||
|
||||
init(param: Int?) {}
|
||||
init() {} // expected-note {{non-failable initializer 'init()' overridden here}}
|
||||
|
||||
subscript(a: Int?) -> Void { // expected-note {{attempt to override subscript here}}
|
||||
get { return () }
|
||||
set {}
|
||||
}
|
||||
subscript(b: Void) -> Int { // expected-note {{attempt to override subscript here}}
|
||||
get { return 0 }
|
||||
set {}
|
||||
}
|
||||
|
||||
subscript(get a: Int?) -> Void { // expected-note {{potential overridden subscript 'subscript(get:)' here}}
|
||||
return ()
|
||||
}
|
||||
subscript(get b: Void) -> Int { // expected-note {{potential overridden subscript 'subscript(get:)' here}}
|
||||
return 0
|
||||
}
|
||||
|
||||
subscript(ambiguous a: Int, b: Int?) -> Void { // expected-note {{overridden declaration is here}} expected-note {{potential overridden subscript 'subscript(ambiguous:_:)' here}}
|
||||
return ()
|
||||
}
|
||||
subscript(ambiguous a: Int?, b: Int) -> Void { // expected-note {{overridden declaration is here}} expected-note {{potential overridden subscript 'subscript(ambiguous:_:)' here}}
|
||||
return ()
|
||||
}
|
||||
}
|
||||
|
||||
class MismatchOptional : MismatchOptionalBase {
|
||||
override func param(_: Int) {} // expected-error {{cannot override instance method parameter of type 'Int?' with non-optional type 'Int'}} {{29-29=?}}
|
||||
override func paramIUO(_: Int) {} // expected-error {{cannot override instance method parameter of type 'Int!' with non-optional type 'Int'}} {{32-32=?}}
|
||||
override func result() -> Int? { return nil } // expected-error {{cannot override instance method result type 'Int' with optional type 'Int?'}} {{32-33=}}
|
||||
|
||||
override func fixSeveralTypes(a: Int, b: Int) -> Int! { return nil }
|
||||
// expected-error@-1 {{cannot override instance method parameter of type 'Int?' with non-optional type 'Int'}} {{39-39=?}}
|
||||
// expected-error@-2 {{cannot override instance method parameter of type 'Int!' with non-optional type 'Int'}} {{47-47=?}}
|
||||
// expected-error@-3 {{cannot override instance method result type 'Int' with optional type 'Int!'}} {{55-56=}}
|
||||
|
||||
override func functionParam(x: (Int) -> Int) {} // expected-error {{cannot override instance method parameter of type '((Int) -> Int)?' with non-optional type '(Int) -> Int'}} {{34-34=(}} {{46-46=)?}}
|
||||
override func tupleParam(x: (Int, Int)) {} // expected-error {{cannot override instance method parameter of type '(Int, Int)?' with non-optional type '(Int, Int)'}} {{41-41=?}}
|
||||
|
||||
override func nameAndTypeMismatch(_: Int) {}
|
||||
// expected-error@-1 {{argument names for method 'nameAndTypeMismatch' do not match those of overridden method 'nameAndTypeMismatch(label:)'}} {{37-37=label }}
|
||||
// expected-error@-2 {{cannot override instance method parameter of type 'Int?' with non-optional type 'Int'}} {{43-43=?}}
|
||||
|
||||
override func ambiguousOverride(a: Int?, b: Int?) {} // expected-error {{declaration 'ambiguousOverride(a:b:)' cannot override more than one superclass declaration}} {{none}}
|
||||
override func ambiguousOverride(a: Int, b: Int) {} // expected-error {{method does not override any method from its superclass}} {{none}}
|
||||
|
||||
override var prop: Int? { // expected-error {{property 'prop' with type 'Int?' cannot override a property with type 'Int'}} {{none}}
|
||||
get { return nil }
|
||||
set {}
|
||||
}
|
||||
override var optProp: Int { // expected-error {{cannot override mutable property 'optProp' of type 'Int?' with covariant type 'Int'}} {{none}}
|
||||
get { return 0 }
|
||||
set {}
|
||||
}
|
||||
override var getProp: Int? { return nil } // expected-error {{property 'getProp' with type 'Int?' cannot override a property with type 'Int'}} {{none}}
|
||||
override var getOptProp: Int { return 0 } // okay
|
||||
|
||||
override init(param: Int) {} // expected-error {{cannot override initializer parameter of type 'Int?' with non-optional type 'Int'}}
|
||||
override init?() {} // expected-error {{failable initializer 'init()' cannot override a non-failable initializer}} {{none}}
|
||||
|
||||
override subscript(a: Int) -> Void { // expected-error {{cannot override mutable subscript of type '(Int) -> Void' with covariant type '(Int?) -> Void'}}
|
||||
get { return () }
|
||||
set {}
|
||||
}
|
||||
override subscript(b: Void) -> Int? { // expected-error {{cannot override mutable subscript of type '(Void) -> Int?' with covariant type '(Void) -> Int'}}
|
||||
get { return nil }
|
||||
set {}
|
||||
}
|
||||
|
||||
override subscript(get a: Int) -> Void { // expected-error {{subscript does not override any subscript from its superclass}}
|
||||
return ()
|
||||
}
|
||||
override subscript(get b: Void) -> Int? { // expected-error {{subscript does not override any subscript from its superclass}}
|
||||
return nil
|
||||
}
|
||||
|
||||
override subscript(ambiguous a: Int?, b: Int?) -> Void { // expected-error {{declaration 'subscript(ambiguous:_:)' cannot override more than one superclass declaration}}
|
||||
return ()
|
||||
}
|
||||
override subscript(ambiguous a: Int, b: Int) -> Void { // expected-error {{subscript does not override any subscript from its superclass}}
|
||||
return ()
|
||||
}
|
||||
}
|
||||
|
||||
class MismatchOptional2 : MismatchOptionalBase {
|
||||
override func result() -> Int! { return nil } // expected-error {{cannot override instance method result type 'Int' with optional type 'Int!'}} {{32-33=}}
|
||||
|
||||
// None of these are overrides because we didn't say 'override'. Since they're
|
||||
// not exact matches, they shouldn't result in errors.
|
||||
func param(_: Int) {}
|
||||
func ambiguousOverride(a: Int, b: Int) {}
|
||||
|
||||
// This is covariant, so we still assume it's meant to override.
|
||||
func ambiguousOverride(a: Int?, b: Int?) {} // expected-error {{declaration 'ambiguousOverride(a:b:)' cannot override more than one superclass declaration}}
|
||||
}
|
||||
|
||||
class MismatchOptional3 : MismatchOptionalBase {
|
||||
override func result() -> Optional<Int> { return nil } // expected-error {{cannot override instance method result type 'Int' with optional type 'Optional<Int>'}} {{none}}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -122,8 +122,8 @@ class G {
|
||||
func f6(_: Int, int: Int) { }
|
||||
func f7(_: Int, int: Int) { }
|
||||
|
||||
func g1(_: Int, string: String) { } // expected-note{{potential overridden method 'g1(_:string:)' here}} {{28-28=string }}
|
||||
func g1(_: Int, path: String) { } // expected-note{{potential overridden method 'g1(_:path:)' here}} {{28-28=path }}
|
||||
func g1(_: Int, string: String) { } // expected-note{{potential overridden instance method 'g1(_:string:)' here}} {{28-28=string }}
|
||||
func g1(_: Int, path: String) { } // expected-note{{potential overridden instance method 'g1(_:path:)' here}} {{28-28=path }}
|
||||
}
|
||||
|
||||
class H : G {
|
||||
|
||||
Reference in New Issue
Block a user