Skip to content

Commit 3b38b9b

Browse files
authored
Merge pull request #42410 from hborla/existential-parameter-fixit
[Diagnostics] When replacing an existential parameter type with its generic equivalent, insert `some` instead of an explicit type parameter.
2 parents b00c07b + 976addc commit 3b38b9b

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
@@ -3970,8 +3970,11 @@ bool InvalidMemberRefOnExistential::diagnoseAsError() {
39703970

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

39763979
if (!PD || !PD->getDeclContext()->getAsDecl())
39773980
return true;
@@ -4020,70 +4023,42 @@ bool InvalidMemberRefOnExistential::diagnoseAsError() {
40204023
if (PD->isInOut())
40214024
return true;
40224025

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

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

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

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

40884063
return true;
40894064
}

0 commit comments

Comments
 (0)