@@ -5980,31 +5980,6 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
5980
5980
if (T->isInvalid ())
5981
5981
return Action::SkipNode ();
5982
5982
5983
- // Arbitrary protocol constraints are OK on opaque types.
5984
- if (isa<OpaqueReturnTypeRepr>(T))
5985
- return Action::SkipNode ();
5986
-
5987
- // Arbitrary protocol constraints are okay for 'any' types.
5988
- if (isa<ExistentialTypeRepr>(T))
5989
- return Action::SkipNode ();
5990
-
5991
- // Suppressed conformance needs to be within any/some.
5992
- if (auto inverse = dyn_cast<InverseTypeRepr>(T)) {
5993
- // Find an enclosing protocol composition, if there is one, so we
5994
- // can insert 'any' before that.
5995
- SourceLoc anyLoc = inverse->getTildeLoc ();
5996
- if (!reprStack.empty ()) {
5997
- if (isa<CompositionTypeRepr>(reprStack.back ())) {
5998
- anyLoc = reprStack.back ()->getStartLoc ();
5999
- }
6000
- }
6001
-
6002
- Ctx.Diags .diagnose (inverse->getTildeLoc (), diag::inverse_requires_any)
6003
- .highlight (inverse->getConstraint ()->getSourceRange ())
6004
- .fixItInsert (anyLoc, " any " );
6005
- return Action::SkipNode ();
6006
- }
6007
-
6008
5983
reprStack.push_back (T);
6009
5984
6010
5985
auto *declRefTR = dyn_cast<DeclRefTypeRepr>(T);
@@ -6057,9 +6032,12 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
6057
6032
}
6058
6033
6059
6034
private:
6060
- bool existentialNeedsParens (TypeRepr *parent) const {
6035
+ // / Returns a Boolean value indicating whether the insertion of `any` before
6036
+ // / a type representation with the given parent requires paretheses.
6037
+ static bool anySyntaxNeedsParens (TypeRepr *parent) {
6061
6038
switch (parent->getKind ()) {
6062
6039
case TypeReprKind::Optional:
6040
+ case TypeReprKind::ImplicitlyUnwrappedOptional:
6063
6041
case TypeReprKind::Protocol:
6064
6042
return true ;
6065
6043
case TypeReprKind::Metatype:
@@ -6074,7 +6052,6 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
6074
6052
case TypeReprKind::UnqualifiedIdent:
6075
6053
case TypeReprKind::QualifiedIdent:
6076
6054
case TypeReprKind::Dictionary:
6077
- case TypeReprKind::ImplicitlyUnwrappedOptional:
6078
6055
case TypeReprKind::Inverse:
6079
6056
case TypeReprKind::Tuple:
6080
6057
case TypeReprKind::Fixed:
@@ -6097,40 +6074,94 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
6097
6074
}
6098
6075
6099
6076
void emitInsertAnyFixit (InFlightDiagnostic &diag, DeclRefTypeRepr *T) const {
6100
- TypeRepr *replaceRepr = T;
6077
+ TypeRepr *replacementT = T;
6101
6078
6102
6079
// Insert parens in expression context for '(any P).self'
6103
6080
bool needsParens = (exprCount != 0 );
6081
+
6082
+ // Compute the replacement node (the node to which to apply `any`).
6104
6083
if (reprStack.size () > 1 ) {
6105
- auto parentIt = reprStack.end () - 2 ;
6106
- needsParens = existentialNeedsParens (*parentIt);
6107
-
6108
- // Expand to include parenthesis before checking if the parent needs
6109
- // to be replaced.
6110
- while (parentIt != reprStack.begin () &&
6111
- (*parentIt)->getWithoutParens () != *parentIt)
6112
- parentIt -= 1 ;
6113
-
6114
- // For existential metatypes, 'any' is applied to the metatype.
6115
- if ((*parentIt)->getKind () == TypeReprKind::Metatype) {
6116
- replaceRepr = *parentIt;
6117
- if (parentIt != reprStack.begin ())
6118
- needsParens = existentialNeedsParens (*(parentIt - 1 ));
6084
+ auto it = reprStack.end () - 1 ;
6085
+ auto replacementIt = it;
6086
+
6087
+ // Backtrack the stack and expand the replacement range to any parent
6088
+ // inverses, compositions or `.Type` metatypes, skipping only parentheses.
6089
+ //
6090
+ // E.g. `(X & ~P).Type` → `any (X & ~P).Type`.
6091
+ // ↑
6092
+ // We're here
6093
+ do {
6094
+ --it;
6095
+ if ((*it)->isParenType ()) {
6096
+ continue ;
6097
+ }
6098
+
6099
+ if (isa<InverseTypeRepr>(*it) || isa<CompositionTypeRepr>(*it) ||
6100
+ isa<MetatypeTypeRepr>(*it)) {
6101
+ replacementIt = it;
6102
+ continue ;
6103
+ }
6104
+
6105
+ break ;
6106
+ } while (it != reprStack.begin ());
6107
+
6108
+ // Whether parentheses are necessary is determined by the immediate parent
6109
+ // of the replacement node.
6110
+ if (replacementIt != reprStack.begin ()) {
6111
+ needsParens = anySyntaxNeedsParens (*(replacementIt - 1 ));
6119
6112
}
6113
+
6114
+ replacementT = *replacementIt;
6120
6115
}
6121
6116
6122
6117
llvm::SmallString<64 > fix;
6123
6118
{
6124
6119
llvm::raw_svector_ostream OS (fix);
6125
6120
if (needsParens)
6126
6121
OS << " (" ;
6127
- ExistentialTypeRepr existential (SourceLoc (), replaceRepr );
6122
+ ExistentialTypeRepr existential (SourceLoc (), replacementT );
6128
6123
existential.print (OS);
6129
6124
if (needsParens)
6130
6125
OS << " )" ;
6131
6126
}
6132
6127
6133
- diag.fixItReplace (replaceRepr->getSourceRange (), fix);
6128
+ diag.fixItReplace (replacementT->getSourceRange (), fix);
6129
+ }
6130
+
6131
+ // / Returns a Boolean value indicating whether the type representation being
6132
+ // / visited, assuming it is a constraint type demanding `any` or `some`, is
6133
+ // / missing either keyword.
6134
+ bool isAnyOrSomeMissing () const {
6135
+ assert (isa<DeclRefTypeRepr>(reprStack.back ()));
6136
+
6137
+ if (reprStack.size () < 2 ) {
6138
+ return true ;
6139
+ }
6140
+
6141
+ auto it = reprStack.end () - 1 ;
6142
+ while (true ) {
6143
+ --it;
6144
+ if (it == reprStack.begin ()) {
6145
+ break ;
6146
+ }
6147
+
6148
+ // Look through parens, inverses, metatypes, and compositions.
6149
+ if ((*it)->isParenType () || isa<InverseTypeRepr>(*it) ||
6150
+ isa<CompositionTypeRepr>(*it) || isa<MetatypeTypeRepr>(*it)) {
6151
+ continue ;
6152
+ }
6153
+
6154
+ // Look through '?' and '!' too; `any P?` et al. is diagnosed in the
6155
+ // type resolver.
6156
+ if (isa<OptionalTypeRepr>(*it) ||
6157
+ isa<ImplicitlyUnwrappedOptionalTypeRepr>(*it)) {
6158
+ continue ;
6159
+ }
6160
+
6161
+ break ;
6162
+ }
6163
+
6164
+ return !(isa<OpaqueReturnTypeRepr>(*it) || isa<ExistentialTypeRepr>(*it));
6134
6165
}
6135
6166
6136
6167
void checkDeclRefTypeRepr (DeclRefTypeRepr *T) const {
@@ -6140,13 +6171,32 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
6140
6171
return ;
6141
6172
}
6142
6173
6174
+ // Backtrack the stack, looking just through parentheses and metatypes. If
6175
+ // we find an inverse (which always requires `any`), diagnose it specially.
6176
+ if (reprStack.size () > 1 ) {
6177
+ auto it = reprStack.end () - 2 ;
6178
+ while (it != reprStack.begin () &&
6179
+ ((*it)->isParenType () || isa<MetatypeTypeRepr>(*it))) {
6180
+ --it;
6181
+ continue ;
6182
+ }
6183
+
6184
+ if (auto *inverse = dyn_cast<InverseTypeRepr>(*it);
6185
+ inverse && isAnyOrSomeMissing ()) {
6186
+ auto diag = Ctx.Diags .diagnose (inverse->getTildeLoc (),
6187
+ diag::inverse_requires_any);
6188
+ emitInsertAnyFixit (diag, T);
6189
+ return ;
6190
+ }
6191
+ }
6192
+
6143
6193
auto *decl = T->getBoundDecl ();
6144
6194
if (!decl) {
6145
6195
return ;
6146
6196
}
6147
6197
6148
6198
if (auto *proto = dyn_cast<ProtocolDecl>(decl)) {
6149
- if (proto->existentialRequiresAny ()) {
6199
+ if (proto->existentialRequiresAny () && isAnyOrSomeMissing () ) {
6150
6200
auto diag =
6151
6201
Ctx.Diags .diagnose (T->getNameLoc (), diag::existential_requires_any,
6152
6202
proto->getDeclaredInterfaceType (),
@@ -6176,7 +6226,7 @@ class ExistentialTypeSyntaxChecker : public ASTWalker {
6176
6226
if (auto *PCT = type->getAs <ProtocolCompositionType>())
6177
6227
diagnose |= !PCT->getInverses ().empty ();
6178
6228
6179
- if (diagnose) {
6229
+ if (diagnose && isAnyOrSomeMissing () ) {
6180
6230
auto diag = Ctx.Diags .diagnose (
6181
6231
T->getNameLoc (), diag::existential_requires_any,
6182
6232
alias->getDeclaredInterfaceType (),
0 commit comments