Skip to content

Commit 62575ed

Browse files
authored
Merge pull request #41706 from hborla/insert-any-fixit
[Diagnostics] Add a fix-it for the missing `any` warning.
2 parents 4971819 + 4c76f7c commit 62575ed

File tree

2 files changed

+121
-5
lines changed

2 files changed

+121
-5
lines changed

lib/Sema/TypeCheckType.cpp

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4115,12 +4115,17 @@ class ExistentialTypeVisitor
41154115
ASTContext &Ctx;
41164116
bool checkStatements;
41174117
bool hitTopStmt;
4118+
4119+
unsigned exprCount = 0;
4120+
llvm::SmallVector<TypeRepr *, 4> reprStack;
41184121

41194122
public:
41204123
ExistentialTypeVisitor(ASTContext &ctx, bool checkStatements)
41214124
: Ctx(ctx), checkStatements(checkStatements), hitTopStmt(false) { }
41224125

41234126
bool walkToTypeReprPre(TypeRepr *T) override {
4127+
reprStack.push_back(T);
4128+
41244129
if (T->isInvalid())
41254130
return false;
41264131
if (auto compound = dyn_cast<CompoundIdentTypeRepr>(T)) {
@@ -4141,6 +4146,11 @@ class ExistentialTypeVisitor
41414146
return true;
41424147
}
41434148

4149+
bool walkToTypeReprPost(TypeRepr *T) override {
4150+
reprStack.pop_back();
4151+
return true;
4152+
}
4153+
41444154
std::pair<bool, Stmt*> walkToStmtPre(Stmt *S) override {
41454155
if (checkStatements && !hitTopStmt) {
41464156
hitTopStmt = true;
@@ -4154,22 +4164,96 @@ class ExistentialTypeVisitor
41544164
return !checkStatements;
41554165
}
41564166

4167+
std::pair<bool, Expr *> walkToExprPre(Expr *E) override {
4168+
++exprCount;
4169+
return {true, E};
4170+
}
4171+
4172+
Expr * walkToExprPost(Expr *E) override {
4173+
--exprCount;
4174+
return E;
4175+
}
4176+
41574177
void visitTypeRepr(TypeRepr *T) {
41584178
// Do nothing for all TypeReprs except the ones listed below.
41594179
}
41604180

4181+
bool existentialNeedsParens(TypeRepr *parent) {
4182+
switch (parent->getKind()) {
4183+
case TypeReprKind::Optional:
4184+
case TypeReprKind::Protocol:
4185+
return true;
4186+
case TypeReprKind::Metatype:
4187+
case TypeReprKind::Attributed:
4188+
case TypeReprKind::Error:
4189+
case TypeReprKind::Function:
4190+
case TypeReprKind::InOut:
4191+
case TypeReprKind::Composition:
4192+
case TypeReprKind::OpaqueReturn:
4193+
case TypeReprKind::NamedOpaqueReturn:
4194+
case TypeReprKind::Existential:
4195+
case TypeReprKind::SimpleIdent:
4196+
case TypeReprKind::GenericIdent:
4197+
case TypeReprKind::CompoundIdent:
4198+
case TypeReprKind::Dictionary:
4199+
case TypeReprKind::ImplicitlyUnwrappedOptional:
4200+
case TypeReprKind::Tuple:
4201+
case TypeReprKind::Fixed:
4202+
case TypeReprKind::Array:
4203+
case TypeReprKind::SILBox:
4204+
case TypeReprKind::Shared:
4205+
case TypeReprKind::Owned:
4206+
case TypeReprKind::Isolated:
4207+
case TypeReprKind::Placeholder:
4208+
case TypeReprKind::CompileTimeConst:
4209+
return false;
4210+
}
4211+
}
4212+
41614213
void visitIdentTypeRepr(IdentTypeRepr *T) {
41624214
if (T->isInvalid())
41634215
return;
41644216

4217+
// Compute the type repr to attach 'any' to.
4218+
TypeRepr *replaceRepr = T;
4219+
// Insert parens in expression context for '(any P).self'
4220+
bool needsParens = (exprCount != 0);
4221+
if (reprStack.size() > 1) {
4222+
auto parentIt = reprStack.end() - 2;
4223+
needsParens = existentialNeedsParens(*parentIt);
4224+
4225+
// Expand to include parenthesis before checking if the parent needs
4226+
// to be replaced.
4227+
while (parentIt != reprStack.begin() &&
4228+
(*parentIt)->getWithoutParens() != *parentIt)
4229+
parentIt -= 1;
4230+
4231+
// For existential metatypes, 'any' is applied to the metatype.
4232+
if ((*parentIt)->getKind() == TypeReprKind::Metatype) {
4233+
replaceRepr = *parentIt;
4234+
if (parentIt != reprStack.begin())
4235+
needsParens = existentialNeedsParens(*(parentIt - 1));
4236+
}
4237+
}
4238+
4239+
std::string fix;
4240+
llvm::raw_string_ostream OS(fix);
4241+
if (needsParens)
4242+
OS << "(";
4243+
ExistentialTypeRepr existential(SourceLoc(), replaceRepr);
4244+
existential.print(OS);
4245+
if (needsParens)
4246+
OS << ")";
4247+
41654248
auto comp = T->getComponentRange().back();
41664249
if (auto *proto = dyn_cast_or_null<ProtocolDecl>(comp->getBoundDecl())) {
41674250
if (proto->existentialRequiresAny()) {
41684251
Ctx.Diags.diagnose(comp->getNameLoc(),
41694252
diag::existential_requires_any,
41704253
proto->getDeclaredInterfaceType(),
41714254
/*isAlias=*/false)
4172-
.limitBehavior(DiagnosticBehavior::Warning);
4255+
.limitBehavior(DiagnosticBehavior::Warning)
4256+
.fixItReplace(replaceRepr->getSourceRange(), fix);
41734257
}
41744258
} else if (auto *alias = dyn_cast_or_null<TypeAliasDecl>(comp->getBoundDecl())) {
41754259
auto type = Type(alias->getDeclaredInterfaceType()->getDesugaredType());
@@ -4187,7 +4271,8 @@ class ExistentialTypeVisitor
41874271
diag::existential_requires_any,
41884272
alias->getDeclaredInterfaceType(),
41894273
/*isAlias=*/true)
4190-
.limitBehavior(DiagnosticBehavior::Warning);
4274+
.limitBehavior(DiagnosticBehavior::Warning)
4275+
.fixItReplace(replaceRepr->getSourceRange(), fix);
41914276
}
41924277
}
41934278
}

test/type/explicit_existential.swift

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,16 +244,16 @@ protocol Output {
244244
associatedtype A
245245
}
246246

247-
// expected-warning@+2{{protocol 'Input' as a type must be explicitly marked as 'any'}}
248-
// expected-warning@+1{{protocol 'Output' as a type must be explicitly marked as 'any'}}
247+
// expected-warning@+2{{protocol 'Input' as a type must be explicitly marked as 'any'}}{{30-35=any Input}}
248+
// expected-warning@+1{{protocol 'Output' as a type must be explicitly marked as 'any'}}{{40-46=any Output}}
249249
typealias InvalidFunction = (Input) -> Output
250250
func testInvalidFunctionAlias(fn: InvalidFunction) {}
251251

252252
typealias ExistentialFunction = (any Input) -> any Output
253253
func testFunctionAlias(fn: ExistentialFunction) {}
254254

255255
typealias Constraint = Input
256-
func testConstraintAlias(x: Constraint) {} // expected-warning{{'Constraint' (aka 'Input') as a type must be explicitly marked as 'any'}}
256+
func testConstraintAlias(x: Constraint) {} // expected-warning{{'Constraint' (aka 'Input') as a type must be explicitly marked as 'any'}}{{29-39=any Constraint}}
257257

258258
typealias Existential = any Input
259259
func testExistentialAlias(x: Existential, y: any Constraint) {}
@@ -278,3 +278,34 @@ enum EE : Equatable, any Empty { // expected-error {{raw type 'any Empty' is not
278278
// expected-error@-3 {{raw type 'any Empty' must appear first in the enum inheritance clause}}
279279
case hack
280280
}
281+
282+
func testAnyFixIt() {
283+
struct ConformingType : HasAssoc {
284+
typealias Assoc = Int
285+
func foo() {}
286+
287+
func method() -> any HasAssoc {}
288+
}
289+
290+
// expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{10-18=any HasAssoc}}
291+
let _: HasAssoc = ConformingType()
292+
// expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{19-27=any HasAssoc}}
293+
let _: Optional<HasAssoc> = nil
294+
// expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{10-23=any HasAssoc.Type}}
295+
let _: HasAssoc.Type = ConformingType.self
296+
// expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{10-25=any (HasAssoc).Type}}
297+
let _: (HasAssoc).Type = ConformingType.self
298+
// expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{10-27=any ((HasAssoc)).Type}}
299+
let _: ((HasAssoc)).Type = ConformingType.self
300+
// expected-warning@+2 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{10-18=(any HasAssoc)}}
301+
// expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{30-38=(any HasAssoc)}}
302+
let _: HasAssoc.Protocol = HasAssoc.self
303+
// expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{11-19=any HasAssoc}}
304+
let _: (HasAssoc).Protocol = (any HasAssoc).self
305+
// expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{10-18=(any HasAssoc)}}
306+
let _: HasAssoc? = ConformingType()
307+
// expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{10-23=(any HasAssoc.Type)}}
308+
let _: HasAssoc.Type? = ConformingType.self
309+
// expected-warning@+1 {{'HasAssoc' as a type must be explicitly marked as 'any'}}{{10-18=(any HasAssoc)}}
310+
let _: HasAssoc.Protocol? = (any HasAssoc).self
311+
}

0 commit comments

Comments
 (0)