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
|
// Dynamic Self
|
||||||
ERROR(dynamic_self_non_method,none,
|
ERROR(dynamic_self_non_method,none,
|
||||||
"%select{global|local}0 function cannot return 'Self'", (bool))
|
"%select{global|local}0 function cannot return 'Self'", (bool))
|
||||||
ERROR(dynamic_self_struct_enum,none,
|
|
||||||
"%select{struct|enum}0 method cannot return 'Self'; "
|
ERROR(self_in_nominal,none,
|
||||||
"did you mean to use the %select{struct|enum}0 type %1?",
|
"'Self' is only available in a protocol or as the result of a "
|
||||||
(int, Identifier))
|
"method in a class; did you mean '%0'?", (StringRef))
|
||||||
|
|
||||||
// Duplicate declarations
|
// Duplicate declarations
|
||||||
ERROR(duplicate_enum_element,none,
|
ERROR(duplicate_enum_element,none,
|
||||||
@@ -2912,9 +2912,6 @@ ERROR(array_literal_intrinsics_not_found,none,
|
|||||||
"Array<T>", ())
|
"Array<T>", ())
|
||||||
ERROR(bool_intrinsics_not_found,none,
|
ERROR(bool_intrinsics_not_found,none,
|
||||||
"broken standard library: cannot find intrinsic operations on Bool", ())
|
"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,
|
ERROR(class_super_access,none,
|
||||||
"class %select{must be declared %select{"
|
"class %select{must be declared %select{"
|
||||||
"%select{private|fileprivate|internal|%error|%error}2|private or fileprivate}3"
|
"%select{private|fileprivate|internal|%error|%error}2|private or fileprivate}3"
|
||||||
|
|||||||
@@ -4825,6 +4825,11 @@ public:
|
|||||||
if (!typeRepr)
|
if (!typeRepr)
|
||||||
return false;
|
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
|
// 'Self' on a property accessor is not dynamic 'Self'...even on a read-only
|
||||||
// property. We could implement it as such in the future.
|
// property. We could implement it as such in the future.
|
||||||
if (func->isAccessor())
|
if (func->isAccessor())
|
||||||
@@ -4868,45 +4873,6 @@ public:
|
|||||||
if (simpleRepr->getIdentifier() != TC.Context.Id_Self)
|
if (simpleRepr->getIdentifier() != TC.Context.Id_Self)
|
||||||
return false;
|
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
|
// Note that the function has a dynamic Self return type and set
|
||||||
// the return type component to the dynamic self type.
|
// the return type component to the dynamic self type.
|
||||||
func->setDynamicSelf(true);
|
func->setDynamicSelf(true);
|
||||||
|
|||||||
@@ -481,8 +481,19 @@ Type TypeChecker::resolveTypeInContext(
|
|||||||
for (auto parentDC = fromDC;
|
for (auto parentDC = fromDC;
|
||||||
!parentDC->isModuleScopeContext();
|
!parentDC->isModuleScopeContext();
|
||||||
parentDC = parentDC->getParent()) {
|
parentDC = parentDC->getParent()) {
|
||||||
if (parentDC->getAsNominalTypeOrNominalTypeExtensionContext() == nominalType)
|
auto *parentNominal =
|
||||||
|
parentDC->getAsNominalTypeOrNominalTypeExtensionContext();
|
||||||
|
if (parentNominal == nominalType)
|
||||||
return resolver->resolveTypeOfContext(parentDC);
|
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;
|
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.
|
/// Diagnose a reference to an unknown type.
|
||||||
///
|
///
|
||||||
/// This routine diagnoses a reference to an unknown type, and
|
/// This routine diagnoses a reference to an unknown type, and
|
||||||
@@ -859,26 +901,35 @@ static Type diagnoseUnknownType(TypeChecker &tc, DeclContext *dc,
|
|||||||
UnsatisfiedDependency *unsatisfiedDependency) {
|
UnsatisfiedDependency *unsatisfiedDependency) {
|
||||||
// Unqualified lookup case.
|
// Unqualified lookup case.
|
||||||
if (parentType.isNull()) {
|
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 &&
|
if (comp->getIdentifier() == tc.Context.Id_Self &&
|
||||||
!isa<GenericIdentTypeRepr>(comp) &&
|
!isa<GenericIdentTypeRepr>(comp)) {
|
||||||
(nominalDC = dc->getInnermostTypeContext()) &&
|
DeclContext *nominalDC = nullptr;
|
||||||
(nominal = nominalDC->getAsNominalTypeOrNominalTypeExtensionContext())) {
|
NominalTypeDecl *nominal = nullptr;
|
||||||
// Retrieve the nominal type and resolve it within this context.
|
if ((nominalDC = dc->getInnermostTypeContext()) &&
|
||||||
assert(!isa<ProtocolDecl>(nominal) && "Cannot be a protocol");
|
(nominal = nominalDC->getAsNominalTypeOrNominalTypeExtensionContext())) {
|
||||||
auto type = resolver->resolveTypeOfContext(dc->getInnermostTypeContext());
|
// Attempt to refer to 'Self' within a non-protocol nominal
|
||||||
if (type->hasError())
|
// type. Fix this by replacing 'Self' with the nominal type name.
|
||||||
return type;
|
|
||||||
|
|
||||||
// Produce a Fix-It replacing 'Self' with the nominal type name.
|
// Retrieve the nominal type and resolve it within this context.
|
||||||
tc.diagnose(comp->getIdLoc(), diag::self_in_nominal, nominal->getName())
|
assert(!isa<ProtocolDecl>(nominal) && "Cannot be a protocol");
|
||||||
.fixItReplace(comp->getIdLoc(), nominal->getName().str());
|
auto type = resolver->resolveTypeOfContext(dc->getInnermostTypeContext());
|
||||||
comp->overwriteIdentifier(nominal->getName());
|
if (type->hasError())
|
||||||
comp->setValue(nominal);
|
return type;
|
||||||
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 {
|
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}}
|
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 {
|
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}}
|
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() {
|
func test() {
|
||||||
let _: Claws<Kitten>.Fangs<Puppy> = pets(fur: Puppy())
|
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 {
|
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