Skip to content

Commit 33d09aa

Browse files
committed
TypeCheckType: Fix missing any fix-it for compositions
1 parent 48d4887 commit 33d09aa

File tree

3 files changed

+60
-17
lines changed

3 files changed

+60
-17
lines changed

lib/Sema/TypeCheckType.cpp

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6047,40 +6047,58 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
60476047
}
60486048

60496049
void emitInsertAnyFixit(InFlightDiagnostic &diag, DeclRefTypeRepr *T) const {
6050-
TypeRepr *replaceRepr = T;
6050+
TypeRepr *replacementSource = T;
60516051

60526052
// Insert parens in expression context for '(any P).self'
60536053
bool needsParens = (exprCount != 0);
6054+
6055+
// Compute the replacement source (the node to which to apply `any`).
60546056
if (reprStack.size() > 1) {
6055-
auto parentIt = reprStack.end() - 2;
6056-
needsParens = existentialNeedsParens(*parentIt);
6057-
6058-
// Expand to include parenthesis before checking if the parent needs
6059-
// to be replaced.
6060-
while (parentIt != reprStack.begin() &&
6061-
(*parentIt)->getWithoutParens() != *parentIt)
6062-
parentIt -= 1;
6063-
6064-
// For existential metatypes, 'any' is applied to the metatype.
6065-
if ((*parentIt)->getKind() == TypeReprKind::Metatype) {
6066-
replaceRepr = *parentIt;
6067-
if (parentIt != reprStack.begin())
6068-
needsParens = existentialNeedsParens(*(parentIt - 1));
6057+
auto it = reprStack.end() - 1;
6058+
auto replacementSourceIt = it;
6059+
6060+
// Backtrack the stack and expand the replacement source to any parent
6061+
// compositions or `.Type` metatypes, skipping only parentheses.
6062+
//
6063+
// - `P` → `any P`.
6064+
// - `P.Type` → `any P.Type`
6065+
// - `X & P` → `any X & P`, not `X & any P`.
6066+
// - `(X & P).Type` → `any (X & P).Type`, not `(any X & P).Type`.
6067+
do {
6068+
--it;
6069+
if ((*it)->getWithoutParens() != *it) {
6070+
continue;
6071+
}
6072+
6073+
if (isa<CompositionTypeRepr>(*it) || isa<MetatypeTypeRepr>(*it)) {
6074+
replacementSourceIt = it;
6075+
continue;
6076+
}
6077+
6078+
break;
6079+
} while (it != reprStack.begin());
6080+
6081+
// Whether parentheses are necessary is determined by the immediate parent
6082+
// of the replacement source.
6083+
if (replacementSourceIt != reprStack.begin()) {
6084+
needsParens = existentialNeedsParens(*(replacementSourceIt - 1));
60696085
}
6086+
6087+
replacementSource = *replacementSourceIt;
60706088
}
60716089

60726090
llvm::SmallString<64> fix;
60736091
{
60746092
llvm::raw_svector_ostream OS(fix);
60756093
if (needsParens)
60766094
OS << "(";
6077-
ExistentialTypeRepr existential(SourceLoc(), replaceRepr);
6095+
ExistentialTypeRepr existential(SourceLoc(), replacementSource);
60786096
existential.print(OS);
60796097
if (needsParens)
60806098
OS << ")";
60816099
}
60826100

6083-
diag.fixItReplace(replaceRepr->getSourceRange(), fix);
6101+
diag.fixItReplace(replacementSource->getSourceRange(), fix);
60846102
}
60856103

60866104
void checkDeclRefTypeRepr(DeclRefTypeRepr *T) const {

test/type/explicit_existential.swift

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,8 @@ func testAnyFixIt() {
301301
let _: (HasAssoc).Type = ConformingType.self
302302
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-27=any ((HasAssoc)).Type}}
303303
let _: ((HasAssoc)).Type = ConformingType.self
304+
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-32=any ((HasAssoc).Type).Type}}
305+
let _: ((HasAssoc).Type).Type
304306
// expected-error@+2 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-18=(any HasAssoc)}}
305307
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{30-38=(any HasAssoc)}}
306308
let _: HasAssoc.Protocol = HasAssoc.self
@@ -335,6 +337,17 @@ func testAnyFixIt() {
335337
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{37-45=any HasAssoc}}
336338
func f2(_: some ((HasAssocGeneric<HasAssoc>)) & (HasAssoc)) {}
337339

340+
// https://github.com/apple/swift/issues/65027
341+
342+
// expected-error@+2:10 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-29=any HasAssoc & HasAssoc}}
343+
// expected-error@+1:21 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-29=any HasAssoc & HasAssoc}}
344+
let _: HasAssoc & HasAssoc
345+
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{11-31=any Empty & ((HasAssoc))}}
346+
let _: (C & ((HasAssoc)))
347+
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-48=(any (Empty & (HasAssoc & Empty)).Type.Type)}}
348+
let _: (C & (HasAssoc & C)).Type.Type?
349+
350+
338351
// expected-error@+1 {{optional 'any' type must be written '(any HasAssoc)?'}}{{10-23=(any HasAssoc)?}}
339352
let _: any HasAssoc? = nil
340353
// expected-error@+1 {{optional 'any' type must be written '(any HasAssoc.Type)?'}}{{10-28=(any HasAssoc.Type)?}}

test/type/explicit_existential_swift6.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,8 @@ func testAnyFixIt() {
334334
let _: (HasAssoc).Type = ConformingType.self
335335
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-27=any ((HasAssoc)).Type}}
336336
let _: ((HasAssoc)).Type = ConformingType.self
337+
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-32=any ((HasAssoc).Type).Type}}
338+
let _: ((HasAssoc).Type).Type
337339
// expected-error@+2 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-18=(any HasAssoc)}}
338340
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{30-38=(any HasAssoc)}}
339341
let _: HasAssoc.Protocol = HasAssoc.self
@@ -368,6 +370,16 @@ func testAnyFixIt() {
368370
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{37-45=any HasAssoc}}
369371
func f2(_: some ((HasAssocGeneric<HasAssoc>)) & (HasAssoc)) {}
370372

373+
// https://github.com/apple/swift/issues/65027
374+
375+
// expected-error@+2:10 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-29=any HasAssoc & HasAssoc}}
376+
// expected-error@+1:21 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-29=any HasAssoc & HasAssoc}}
377+
let _: HasAssoc & HasAssoc
378+
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{11-31=any Empty & ((HasAssoc))}}
379+
let _: (C & ((HasAssoc)))
380+
// expected-error@+1 {{use of protocol 'HasAssoc' as a type must be written 'any HasAssoc'}}{{10-48=(any (Empty & (HasAssoc & Empty)).Type.Type)}}
381+
let _: (C & (HasAssoc & C)).Type.Type?
382+
371383
// expected-error@+1 {{optional 'any' type must be written '(any HasAssoc)?'}}{{10-23=(any HasAssoc)?}}
372384
let _: any HasAssoc? = nil
373385
// expected-error@+1 {{optional 'any' type must be written '(any HasAssoc.Type)?'}}{{10-28=(any HasAssoc.Type)?}}

0 commit comments

Comments
 (0)