Skip to content

Commit 6f95d07

Browse files
authored
Merge pull request #42413 from hborla/5.7-existential-parameter-fixit
[5.7][Diagnostics] When replacing an existential parameter type with its generic equivalent, insert some instead of an explicit type parameter.
2 parents 4400ce4 + 3af616f commit 6f95d07

File tree

2 files changed

+72
-97
lines changed

2 files changed

+72
-97
lines changed

lib/Sema/CSDiagnostics.cpp

Lines changed: 36 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -3972,8 +3972,11 @@ bool InvalidMemberRefOnExistential::diagnoseAsError() {
39723972

39733973
// If the base expression is a reference to a function or subscript
39743974
// parameter, offer a fixit that replaces the existential parameter type with
3975-
// its generic equivalent, e.g. func foo(p: any P) → func foo<T: P>(p: T).
3976-
// FIXME: Add an option to use 'some' vs. an explicit generic parameter.
3975+
// its generic equivalent, e.g. func foo(p: any P) → func foo(p: some P).
3976+
// Replacing 'any' with 'some' allows the code to compile without further
3977+
// changes, such as naming an explicit type parameter, and is future-proofed
3978+
// for same-type requirements on primary associated types instead of needing
3979+
// a where clause.
39773980

39783981
if (!PD || !PD->getDeclContext()->getAsDecl())
39793982
return true;
@@ -4022,70 +4025,42 @@ bool InvalidMemberRefOnExistential::diagnoseAsError() {
40224025
if (PD->isInOut())
40234026
return true;
40244027

4025-
constexpr StringRef GPNamePlaceholder = "<#generic parameter name#>";
4026-
SourceRange TyReplacementRange;
4027-
SourceRange RemoveAnyRange;
4028-
SourceLoc GPDeclLoc;
4029-
std::string GPDeclStr;
4030-
{
4031-
llvm::raw_string_ostream OS(GPDeclStr);
4032-
auto *const GC = PD->getDeclContext()->getAsDecl()->getAsGenericContext();
4033-
if (GC->getParsedGenericParams()) {
4034-
GPDeclLoc = GC->getParsedGenericParams()->getRAngleLoc();
4035-
OS << ", ";
4036-
} else {
4037-
GPDeclLoc =
4038-
isa<AbstractFunctionDecl>(GC)
4039-
? cast<AbstractFunctionDecl>(GC)->getParameters()->getLParenLoc()
4040-
: cast<SubscriptDecl>(GC)->getIndices()->getLParenLoc();
4041-
OS << "<";
4042-
}
4043-
OS << GPNamePlaceholder << ": ";
4044-
4045-
auto *TR = PD->getTypeRepr()->getWithoutParens();
4046-
if (auto *STR = dyn_cast<SpecifierTypeRepr>(TR)) {
4047-
TR = STR->getBase()->getWithoutParens();
4048-
}
4049-
if (auto *ETR = dyn_cast<ExistentialTypeRepr>(TR)) {
4050-
TR = ETR->getConstraint();
4051-
RemoveAnyRange = SourceRange(ETR->getAnyLoc(), TR->getStartLoc());
4052-
TR = TR->getWithoutParens();
4053-
}
4054-
if (auto *MTR = dyn_cast<MetatypeTypeRepr>(TR)) {
4055-
TR = MTR->getBase();
4056-
4057-
// (P & Q).Type -> T.Type
4058-
// (P).Type -> (T).Type
4059-
// ((P & Q)).Type -> ((T)).Type
4060-
if (auto *TTR = dyn_cast<TupleTypeRepr>(TR)) {
4061-
assert(TTR->isParenType());
4062-
if (!isa<CompositionTypeRepr>(TTR->getElementType(0))) {
4063-
TR = TR->getWithoutParens();
4064-
}
4065-
}
4066-
}
4067-
TyReplacementRange = TR->getSourceRange();
4068-
4069-
// Strip any remaining parentheses and print the conformance constraint.
4070-
TR->getWithoutParens()->print(OS);
4071-
4072-
if (!GC->getParsedGenericParams()) {
4073-
OS << ">";
4074-
}
4028+
auto *typeRepr = PD->getTypeRepr()->getWithoutParens();
4029+
if (auto *STR = dyn_cast<SpecifierTypeRepr>(typeRepr)) {
4030+
typeRepr = STR->getBase()->getWithoutParens();
40754031
}
40764032

4077-
// First, replace the constraint type with the generic parameter type
4078-
// placeholder.
4079-
Diag.fixItReplace(TyReplacementRange, GPNamePlaceholder);
4033+
SourceRange anyRange;
4034+
TypeRepr *constraintRepr = typeRepr;
4035+
if (auto *existentialRepr = dyn_cast<ExistentialTypeRepr>(typeRepr)) {
4036+
constraintRepr = existentialRepr->getConstraint()->getWithoutParens();
4037+
auto anyStart = existentialRepr->getAnyLoc();
4038+
auto anyEnd = existentialRepr->getConstraint()->getStartLoc();
4039+
anyRange = SourceRange(anyStart, anyEnd);
4040+
}
40804041

4081-
// Remove 'any' if needed, using a character-based removal to pick up
4082-
// whitespaces between it and its constraint repr.
4083-
if (RemoveAnyRange.isValid()) {
4084-
Diag.fixItRemoveChars(RemoveAnyRange.Start, RemoveAnyRange.End);
4042+
bool needsParens = false;
4043+
while (auto *metatype = dyn_cast<MetatypeTypeRepr>(constraintRepr)) {
4044+
// The generic equivalent of 'any P.Type' is '(some P).Type'
4045+
constraintRepr = metatype->getBase()->getWithoutParens();
4046+
if (isa<SimpleIdentTypeRepr>(constraintRepr))
4047+
needsParens = !isa<TupleTypeRepr>(metatype->getBase());
40854048
}
40864049

4087-
// Finally, insert the generic parameter declaration.
4088-
Diag.fixItInsert(GPDeclLoc, GPDeclStr);
4050+
std::string fix;
4051+
llvm::raw_string_ostream OS(fix);
4052+
if (needsParens)
4053+
OS << "(";
4054+
OS << "some ";
4055+
constraintRepr->print(OS);
4056+
if (needsParens)
4057+
OS << ")";
4058+
4059+
// When removing 'any', use a character-based removal to pick up
4060+
// whitespaces between it and its constraint repr.
4061+
Diag
4062+
.fixItReplace(constraintRepr->getSourceRange(), fix)
4063+
.fixItRemoveChars(anyRange.Start, anyRange.End);
40894064

40904065
return true;
40914066
}

0 commit comments

Comments
 (0)