|
20 | 20 | #include "TypoCorrection.h"
|
21 | 21 | #include "swift/AST/ASTContext.h"
|
22 | 22 | #include "swift/AST/Decl.h"
|
| 23 | +#include "swift/AST/ExistentialLayout.h" |
23 | 24 | #include "swift/AST/Expr.h"
|
24 | 25 | #include "swift/AST/GenericSignature.h"
|
25 | 26 | #include "swift/AST/Initializer.h"
|
@@ -2006,6 +2007,9 @@ void ContextualFailure::tryFixIts(InFlightDiagnostic &diagnostic) const {
|
2006 | 2007 | if (tryIntegerCastFixIts(diagnostic))
|
2007 | 2008 | return;
|
2008 | 2009 |
|
| 2010 | + if (tryProtocolConformanceFixIt(diagnostic)) |
| 2011 | + return; |
| 2012 | + |
2009 | 2013 | if (tryTypeCoercionFixIt(diagnostic))
|
2010 | 2014 | return;
|
2011 | 2015 | }
|
@@ -2430,6 +2434,90 @@ bool ContextualFailure::tryTypeCoercionFixIt(
|
2430 | 2434 | return false;
|
2431 | 2435 | }
|
2432 | 2436 |
|
| 2437 | +bool ContextualFailure::tryProtocolConformanceFixIt( |
| 2438 | + InFlightDiagnostic &diagnostic) const { |
| 2439 | + auto innermostTyCtx = getDC()->getInnermostTypeContext(); |
| 2440 | + if (!innermostTyCtx) |
| 2441 | + return false; |
| 2442 | + |
| 2443 | + auto nominal = innermostTyCtx->getSelfNominalTypeDecl(); |
| 2444 | + if (!nominal) |
| 2445 | + return false; |
| 2446 | + |
| 2447 | + // We need to get rid of optionals and parens as it's not relevant when |
| 2448 | + // printing the diagnostic and the fix-it. |
| 2449 | + auto unwrappedToType = |
| 2450 | + ToType->lookThroughAllOptionalTypes()->getWithoutParens(); |
| 2451 | + |
| 2452 | + // If the protocol requires a class & we don't have one (maybe the context |
| 2453 | + // is a struct), then bail out instead of offering a broken fix-it later on. |
| 2454 | + auto requiresClass = false; |
| 2455 | + ExistentialLayout layout; |
| 2456 | + if (unwrappedToType->isExistentialType()) { |
| 2457 | + layout = unwrappedToType->getExistentialLayout(); |
| 2458 | + requiresClass = layout.requiresClass(); |
| 2459 | + } |
| 2460 | + |
| 2461 | + if (requiresClass && !FromType->is<ClassType>()) { |
| 2462 | + return false; |
| 2463 | + } |
| 2464 | + |
| 2465 | + // We can only offer a fix-it if we're assigning to a protocol type and |
| 2466 | + // the type we're assigning is the same as the innermost type context. |
| 2467 | + bool shouldOfferFixIt = nominal->getSelfTypeInContext()->isEqual(FromType) && |
| 2468 | + unwrappedToType->isExistentialType(); |
| 2469 | + if (!shouldOfferFixIt) |
| 2470 | + return false; |
| 2471 | + |
| 2472 | + diagnostic.flush(); |
| 2473 | + |
| 2474 | + // Let's build a list of protocols that the context does not conform to. |
| 2475 | + SmallVector<std::string, 8> missingProtoTypeStrings; |
| 2476 | + for (auto protocol : layout.getProtocols()) { |
| 2477 | + if (!getTypeChecker().conformsToProtocol( |
| 2478 | + FromType, protocol->getDecl(), getDC(), |
| 2479 | + ConformanceCheckFlags::InExpression)) { |
| 2480 | + missingProtoTypeStrings.push_back(protocol->getString()); |
| 2481 | + } |
| 2482 | + } |
| 2483 | + |
| 2484 | + // If we have a protocol composition type and we don't conform to all |
| 2485 | + // the protocols of the composition, then store the composition directly. |
| 2486 | + // This is because we need to append 'Foo & Bar' instead of 'Foo, Bar' in |
| 2487 | + // order to match the written type. |
| 2488 | + if (auto compositionTy = unwrappedToType->getAs<ProtocolCompositionType>()) { |
| 2489 | + if (compositionTy->getMembers().size() == missingProtoTypeStrings.size()) { |
| 2490 | + missingProtoTypeStrings = {compositionTy->getString()}; |
| 2491 | + } |
| 2492 | + } |
| 2493 | + |
| 2494 | + assert(!missingProtoTypeStrings.empty() && |
| 2495 | + "type already conforms to all the protocols?"); |
| 2496 | + |
| 2497 | + // Combine all protocol names together, separated by commas. |
| 2498 | + std::string protoString = llvm::join(missingProtoTypeStrings, ", "); |
| 2499 | + |
| 2500 | + // Emit a diagnostic to inform the user that they need to conform to the |
| 2501 | + // missing protocols. |
| 2502 | + // |
| 2503 | + // TODO: Maybe also insert the requirement stubs? |
| 2504 | + auto conformanceDiag = emitDiagnostic( |
| 2505 | + getAnchor()->getLoc(), diag::assign_protocol_conformance_fix_it, |
| 2506 | + unwrappedToType, nominal->getDescriptiveKind(), FromType); |
| 2507 | + if (nominal->getInherited().size() > 0) { |
| 2508 | + auto lastInherited = nominal->getInherited().back().getLoc(); |
| 2509 | + auto lastInheritedEndLoc = |
| 2510 | + Lexer::getLocForEndOfToken(getASTContext().SourceMgr, lastInherited); |
| 2511 | + conformanceDiag.fixItInsert(lastInheritedEndLoc, ", " + protoString); |
| 2512 | + } else { |
| 2513 | + auto nameEndLoc = Lexer::getLocForEndOfToken(getASTContext().SourceMgr, |
| 2514 | + nominal->getNameLoc()); |
| 2515 | + conformanceDiag.fixItInsert(nameEndLoc, ": " + protoString); |
| 2516 | + } |
| 2517 | + |
| 2518 | + return true; |
| 2519 | +} |
| 2520 | + |
2433 | 2521 | void ContextualFailure::tryComputedPropertyFixIts(Expr *expr) const {
|
2434 | 2522 | if (!isa<ClosureExpr>(expr))
|
2435 | 2523 | return;
|
|
0 commit comments