mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Sema: Improve the fixit for 'Self' in invalid places
In an extension of a nested type, the extended type must be fully qualified. Also clean up the diagnostic logic a little bit and centralize it in diagnoseUnknownType(). Fixes <https://bugs.swift.org/browse/SR-4379>.
This commit is contained in:
@@ -1959,10 +1959,10 @@ ERROR(no_equal_overload_for_int,none,
|
||||
// Dynamic Self
|
||||
ERROR(dynamic_self_non_method,none,
|
||||
"%select{global|local}0 function cannot return 'Self'", (bool))
|
||||
ERROR(dynamic_self_struct_enum,none,
|
||||
"%select{struct|enum}0 method cannot return 'Self'; "
|
||||
"did you mean to use the %select{struct|enum}0 type %1?",
|
||||
(int, Identifier))
|
||||
|
||||
ERROR(self_in_nominal,none,
|
||||
"'Self' is only available in a protocol or as the result of a "
|
||||
"method in a class; did you mean '%0'?", (StringRef))
|
||||
|
||||
// Duplicate declarations
|
||||
ERROR(duplicate_enum_element,none,
|
||||
@@ -2912,9 +2912,6 @@ ERROR(array_literal_intrinsics_not_found,none,
|
||||
"Array<T>", ())
|
||||
ERROR(bool_intrinsics_not_found,none,
|
||||
"broken standard library: cannot find intrinsic operations on Bool", ())
|
||||
ERROR(self_in_nominal,none,
|
||||
"'Self' is only available in a protocol or as the result of a "
|
||||
"method in a class; did you mean %0?", (Identifier))
|
||||
ERROR(class_super_access,none,
|
||||
"class %select{must be declared %select{"
|
||||
"%select{private|fileprivate|internal|%error|%error}2|private or fileprivate}3"
|
||||
|
||||
@@ -4825,6 +4825,11 @@ public:
|
||||
if (!typeRepr)
|
||||
return false;
|
||||
|
||||
// 'Self' on a free function is not dynamic 'Self'.
|
||||
if (!func->getDeclContext()->getAsClassOrClassExtensionContext() &&
|
||||
!isa<ProtocolDecl>(func->getDeclContext()))
|
||||
return false;
|
||||
|
||||
// 'Self' on a property accessor is not dynamic 'Self'...even on a read-only
|
||||
// property. We could implement it as such in the future.
|
||||
if (func->isAccessor())
|
||||
@@ -4868,45 +4873,6 @@ public:
|
||||
if (simpleRepr->getIdentifier() != TC.Context.Id_Self)
|
||||
return false;
|
||||
|
||||
// 'Self' in protocol extensions is not dynamic 'Self'.
|
||||
DeclContext *dc = func->getDeclContext();
|
||||
for (auto parentDC = dc; !parentDC->isModuleScopeContext();
|
||||
parentDC = parentDC->getParent()) {
|
||||
if (parentDC->getAsProtocolExtensionContext()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Dynamic 'Self' is only permitted on methods.
|
||||
if (!dc->isTypeContext()) {
|
||||
TC.diagnose(simpleRepr->getIdLoc(), diag::dynamic_self_non_method,
|
||||
dc->isLocalContext());
|
||||
simpleRepr->setInvalid();
|
||||
return true;
|
||||
}
|
||||
|
||||
// 'Self' is only a dynamic self on class methods and
|
||||
// protocol requirements.
|
||||
auto declaredType = dc->getDeclaredTypeOfContext();
|
||||
if (declaredType->hasError())
|
||||
return false;
|
||||
|
||||
auto nominal = declaredType->getAnyNominal();
|
||||
if (!isa<ClassDecl>(nominal) && !isa<ProtocolDecl>(nominal)) {
|
||||
int which;
|
||||
if (isa<StructDecl>(nominal))
|
||||
which = 0;
|
||||
else if (isa<EnumDecl>(nominal))
|
||||
which = 1;
|
||||
else
|
||||
llvm_unreachable("Unknown nominal type");
|
||||
TC.diagnose(simpleRepr->getIdLoc(), diag::dynamic_self_struct_enum,
|
||||
which, nominal->getName())
|
||||
.fixItReplace(simpleRepr->getIdLoc(), nominal->getName().str());
|
||||
simpleRepr->setInvalid();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Note that the function has a dynamic Self return type and set
|
||||
// the return type component to the dynamic self type.
|
||||
func->setDynamicSelf(true);
|
||||
|
||||
@@ -481,8 +481,19 @@ Type TypeChecker::resolveTypeInContext(
|
||||
for (auto parentDC = fromDC;
|
||||
!parentDC->isModuleScopeContext();
|
||||
parentDC = parentDC->getParent()) {
|
||||
if (parentDC->getAsNominalTypeOrNominalTypeExtensionContext() == nominalType)
|
||||
auto *parentNominal =
|
||||
parentDC->getAsNominalTypeOrNominalTypeExtensionContext();
|
||||
if (parentNominal == nominalType)
|
||||
return resolver->resolveTypeOfContext(parentDC);
|
||||
if (isa<ExtensionDecl>(parentDC)) {
|
||||
auto *extendedType = parentNominal;
|
||||
while (extendedType != nullptr) {
|
||||
if (extendedType == nominalType)
|
||||
return resolver->resolveTypeOfDecl(extendedType);
|
||||
extendedType = extendedType->getParent()
|
||||
->getAsNominalTypeOrNominalTypeExtensionContext();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -839,6 +850,37 @@ static Type resolveTypeDecl(TypeChecker &TC, TypeDecl *typeDecl, SourceLoc loc,
|
||||
return type;
|
||||
}
|
||||
|
||||
static std::string getDeclNameFromContext(DeclContext *dc,
|
||||
NominalTypeDecl *nominal) {
|
||||
// We don't allow an unqualified reference to a type inside an
|
||||
// extension if the type is itself nested inside another type,
|
||||
// eg:
|
||||
//
|
||||
// extension A.B { ... B ... }
|
||||
//
|
||||
// Instead, you must write 'A.B'. Calculate the right name to use
|
||||
// for fixits.
|
||||
if (!isa<ExtensionDecl>(dc)) {
|
||||
SmallVector<Identifier, 2> idents;
|
||||
auto *parentNominal = nominal;
|
||||
while (parentNominal != nullptr) {
|
||||
idents.push_back(parentNominal->getName());
|
||||
parentNominal = parentNominal->getDeclContext()
|
||||
->getAsNominalTypeOrNominalTypeExtensionContext();
|
||||
}
|
||||
std::reverse(idents.begin(), idents.end());
|
||||
std::string result;
|
||||
for (auto ident : idents) {
|
||||
if (!result.empty())
|
||||
result += ".";
|
||||
result += ident.str();
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
return nominal->getName().str();
|
||||
}
|
||||
}
|
||||
|
||||
/// Diagnose a reference to an unknown type.
|
||||
///
|
||||
/// This routine diagnoses a reference to an unknown type, and
|
||||
@@ -859,26 +901,35 @@ static Type diagnoseUnknownType(TypeChecker &tc, DeclContext *dc,
|
||||
UnsatisfiedDependency *unsatisfiedDependency) {
|
||||
// Unqualified lookup case.
|
||||
if (parentType.isNull()) {
|
||||
// Attempt to refer to 'Self' within a non-protocol nominal
|
||||
// type. Fix this by replacing 'Self' with the nominal type name.
|
||||
DeclContext *nominalDC = nullptr;
|
||||
NominalTypeDecl *nominal = nullptr;
|
||||
if (comp->getIdentifier() == tc.Context.Id_Self &&
|
||||
!isa<GenericIdentTypeRepr>(comp) &&
|
||||
(nominalDC = dc->getInnermostTypeContext()) &&
|
||||
(nominal = nominalDC->getAsNominalTypeOrNominalTypeExtensionContext())) {
|
||||
// Retrieve the nominal type and resolve it within this context.
|
||||
assert(!isa<ProtocolDecl>(nominal) && "Cannot be a protocol");
|
||||
auto type = resolver->resolveTypeOfContext(dc->getInnermostTypeContext());
|
||||
if (type->hasError())
|
||||
return type;
|
||||
!isa<GenericIdentTypeRepr>(comp)) {
|
||||
DeclContext *nominalDC = nullptr;
|
||||
NominalTypeDecl *nominal = nullptr;
|
||||
if ((nominalDC = dc->getInnermostTypeContext()) &&
|
||||
(nominal = nominalDC->getAsNominalTypeOrNominalTypeExtensionContext())) {
|
||||
// Attempt to refer to 'Self' within a non-protocol nominal
|
||||
// type. Fix this by replacing 'Self' with the nominal type name.
|
||||
|
||||
// Produce a Fix-It replacing 'Self' with the nominal type name.
|
||||
tc.diagnose(comp->getIdLoc(), diag::self_in_nominal, nominal->getName())
|
||||
.fixItReplace(comp->getIdLoc(), nominal->getName().str());
|
||||
comp->overwriteIdentifier(nominal->getName());
|
||||
comp->setValue(nominal);
|
||||
return type;
|
||||
// Retrieve the nominal type and resolve it within this context.
|
||||
assert(!isa<ProtocolDecl>(nominal) && "Cannot be a protocol");
|
||||
auto type = resolver->resolveTypeOfContext(dc->getInnermostTypeContext());
|
||||
if (type->hasError())
|
||||
return type;
|
||||
|
||||
// Produce a Fix-It replacing 'Self' with the nominal type name.
|
||||
auto name = getDeclNameFromContext(dc, nominal);
|
||||
tc.diagnose(comp->getIdLoc(), diag::self_in_nominal, name)
|
||||
.fixItReplace(comp->getIdLoc(), name);
|
||||
comp->overwriteIdentifier(nominal->getName());
|
||||
comp->setValue(nominal);
|
||||
return type;
|
||||
} else {
|
||||
// Attempt to refer to 'Self' from a free function.
|
||||
tc.diagnose(comp->getIdLoc(), diag::dynamic_self_non_method,
|
||||
dc->getParent()->isLocalContext());
|
||||
|
||||
return ErrorType::get(tc.Context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,13 +10,13 @@ func inFunction() {
|
||||
}
|
||||
|
||||
struct S0 {
|
||||
func f() -> Self { } // expected-error{{struct method cannot return 'Self'; did you mean to use the struct type 'S0'?}}{{15-19=S0}}
|
||||
func f() -> Self { } // expected-error{{'Self' is only available in a protocol or as the result of a method in a class; did you mean 'S0'?}}{{15-19=S0}}
|
||||
|
||||
func g(_ ds: Self) { } // expected-error{{'Self' is only available in a protocol or as the result of a method in a class; did you mean 'S0'?}}{{16-20=S0}}
|
||||
}
|
||||
|
||||
enum E0 {
|
||||
func f() -> Self { } // expected-error{{enum method cannot return 'Self'; did you mean to use the enum type 'E0'?}}{{15-19=E0}}
|
||||
func f() -> Self { } // expected-error{{'Self' is only available in a protocol or as the result of a method in a class; did you mean 'E0'?}}{{15-19=E0}}
|
||||
|
||||
func g(_ ds: Self) { } // expected-error{{'Self' is only available in a protocol or as the result of a method in a class; did you mean 'E0'?}}{{16-20=E0}}
|
||||
}
|
||||
|
||||
@@ -388,3 +388,19 @@ func pets<T>(fur: T) -> Claws<Kitten>.Fangs<T> {
|
||||
func test() {
|
||||
let _: Claws<Kitten>.Fangs<Puppy> = pets(fur: Puppy())
|
||||
}
|
||||
|
||||
// https://bugs.swift.org/browse/SR-4379
|
||||
extension OuterGeneric.MidNonGeneric {
|
||||
func doStuff() -> OuterGeneric {
|
||||
return OuterGeneric()
|
||||
}
|
||||
|
||||
func doMoreStuff() -> OuterGeneric.MidNonGeneric {
|
||||
return OuterGeneric.MidNonGeneric()
|
||||
}
|
||||
|
||||
func doMoreStuffWrong() -> Self {
|
||||
// expected-error@-1 {{'Self' is only available in a protocol or as the result of a method in a class; did you mean 'OuterGeneric.MidNonGeneric'?}}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,5 +23,5 @@ extension X {
|
||||
}
|
||||
|
||||
extension X.Inner {
|
||||
func foo(_ other: Self) { } // expected-error{{'Self' is only available in a protocol or as the result of a method in a class; did you mean 'Inner'?}}{{21-25=Inner}}
|
||||
func foo(_ other: Self) { } // expected-error{{'Self' is only available in a protocol or as the result of a method in a class; did you mean 'X.Inner'?}}{{21-25=X.Inner}}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user