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:
Slava Pestov
2017-04-23 00:43:38 -07:00
parent 7f3c97bf8b
commit 07c189558c
6 changed files with 98 additions and 68 deletions

View File

@@ -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"

View File

@@ -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);

View File

@@ -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);
}
} }

View File

@@ -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}}
} }

View File

@@ -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'?}}
}
}

View File

@@ -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}}
} }