@@ -500,38 +500,45 @@ static bool isCallToStandardLibrarySwap(CallExpr *CE, ASTContext &Ctx) {
500
500
}
501
501
#endif
502
502
503
- // / Do a sytactic pattern match to try to safely suggest a Fix-It to rewrite
504
- // / calls like swap(&collection[index1], &collection[index2]) to
503
+ // / Do a syntactic pattern match to determine whether the call is a call
504
+ // / to swap(&base[index1], &base[index2]), which can
505
+ // / be replaced with a call to MutableCollection.swapAt(_:_:) on base.
506
+ // /
507
+ // / Returns true if the call can be replaced. Returns the call expression,
508
+ // / the base expression, and the two indices as out expressions.
505
509
// /
506
510
// / This method takes an array of all the ApplyInsts for calls to swap()
507
- // / in the function to avoid need to construct a parent map over the AST
511
+ // / in the function to avoid needing to construct a parent map over the AST
508
512
// / to find the CallExpr for the inout accesses.
509
- static void
510
- tryFixItWithCallToCollectionSwapAt (const BeginAccessInst *Access1,
511
- const BeginAccessInst *Access2,
512
- ArrayRef<ApplyInst *> CallsToSwap,
513
- ASTContext &Ctx,
514
- InFlightDiagnostic &Diag) {
513
+ static bool
514
+ canReplaceWithCallToCollectionSwapAt (const BeginAccessInst *Access1,
515
+ const BeginAccessInst *Access2,
516
+ ArrayRef<ApplyInst *> CallsToSwap,
517
+ ASTContext &Ctx,
518
+ CallExpr *&FoundCall,
519
+ Expr *&Base,
520
+ Expr *&Index1,
521
+ Expr *&Index2) {
515
522
if (CallsToSwap.empty ())
516
- return ;
523
+ return false ;
517
524
518
525
// Inout arguments must be modifications.
519
526
if (Access1->getAccessKind () != SILAccessKind::Modify ||
520
527
Access2->getAccessKind () != SILAccessKind::Modify) {
521
- return ;
528
+ return false ;
522
529
}
523
530
524
531
SILLocation Loc1 = Access1->getLoc ();
525
532
SILLocation Loc2 = Access2->getLoc ();
526
533
if (Loc1.isNull () || Loc2.isNull ())
527
- return ;
534
+ return false ;
528
535
529
536
auto *InOut1 = Loc1.getAsASTNode <InOutExpr>();
530
537
auto *InOut2 = Loc2.getAsASTNode <InOutExpr>();
531
538
if (!InOut1 || !InOut2)
532
- return ;
539
+ return false ;
533
540
534
- CallExpr * FoundCall = nullptr ;
541
+ FoundCall = nullptr ;
535
542
// Look through all the calls to swap() recorded in the function to find
536
543
// which one we're diagnosing.
537
544
for (ApplyInst *AI : CallsToSwap) {
@@ -554,22 +561,22 @@ tryFixItWithCallToCollectionSwapAt(const BeginAccessInst *Access1,
554
561
}
555
562
}
556
563
if (!FoundCall)
557
- return ;
564
+ return false ;
558
565
559
566
// We found a call to swap(&e1, &e2). Now check to see whether it
560
567
// matches the form swap(&someCollection[index1], &someCollection[index2]).
561
568
auto *SE1 = dyn_cast<SubscriptExpr>(InOut1->getSubExpr ());
562
569
if (!SE1)
563
- return ;
570
+ return false ;
564
571
auto *SE2 = dyn_cast<SubscriptExpr>(InOut2->getSubExpr ());
565
572
if (!SE2)
566
- return ;
573
+ return false ;
567
574
568
575
// Do the two subscripts refer to the same subscript declaration?
569
576
auto *Decl1 = cast<SubscriptDecl>(SE1->getDecl ().getDecl ());
570
577
auto *Decl2 = cast<SubscriptDecl>(SE2->getDecl ().getDecl ());
571
578
if (Decl1 != Decl2)
572
- return ;
579
+ return false ;
573
580
574
581
ProtocolDecl *MutableCollectionDecl = Ctx.getMutableCollectionDecl ();
575
582
@@ -594,7 +601,7 @@ tryFixItWithCallToCollectionSwapAt(const BeginAccessInst *Access1,
594
601
}
595
602
596
603
if (!IsSubscriptOnMutableCollection)
597
- return ;
604
+ return false ;
598
605
599
606
// We're swapping two subscripts on mutable collections -- but are they
600
607
// the same collection? Approximate this by checking for textual
@@ -605,24 +612,33 @@ tryFixItWithCallToCollectionSwapAt(const BeginAccessInst *Access1,
605
612
StringRef Base2Text = extractExprText (SE2->getBase (), SM);
606
613
607
614
if (Base1Text != Base2Text)
608
- return ;
615
+ return false ;
609
616
610
- auto *Index1 = dyn_cast<ParenExpr>(SE1->getIndex ());
611
- if (!Index1 )
612
- return ;
617
+ auto *Index1Paren = dyn_cast<ParenExpr>(SE1->getIndex ());
618
+ if (!Index1Paren )
619
+ return false ;
613
620
614
- auto *Index2 = dyn_cast<ParenExpr>(SE2->getIndex ());
615
- if (!Index2 )
616
- return ;
621
+ auto *Index2Paren = dyn_cast<ParenExpr>(SE2->getIndex ());
622
+ if (!Index2Paren )
623
+ return false ;
617
624
618
- StringRef Index1Text = extractExprText (Index1->getSubExpr (), SM);
619
- StringRef Index2Text = extractExprText (Index2->getSubExpr (), SM);
625
+ Base = SE1->getBase ();
626
+ Index1 = Index1Paren->getSubExpr ();
627
+ Index2 = Index2Paren->getSubExpr ();
628
+ return true ;
629
+ }
620
630
621
- // Suggest replacing with call with a call to swapAt().
631
+ // / Suggest replacing with call with a call to swapAt().
632
+ static void addSwapAtFixit (InFlightDiagnostic &Diag, CallExpr *&FoundCall,
633
+ Expr *Base, Expr *&Index1, Expr *&Index2,
634
+ SourceManager &SM) {
635
+ StringRef BaseText = extractExprText (Base, SM);
636
+ StringRef Index1Text = extractExprText (Index1, SM);
637
+ StringRef Index2Text = extractExprText (Index2, SM);
622
638
SmallString<64 > FixItText;
623
639
{
624
640
llvm::raw_svector_ostream Out (FixItText);
625
- Out << Base1Text << " .swapAt(" << Index1Text << " , " << Index2Text << " )" ;
641
+ Out << BaseText << " .swapAt(" << Index1Text << " , " << Index2Text << " )" ;
626
642
}
627
643
628
644
Diag.fixItReplace (FoundCall->getSourceRange (), FixItText);
@@ -686,15 +702,27 @@ static void diagnoseExclusivityViolation(const ConflictingAccess &Violation,
686
702
SILModule &M = FirstAccess.getInstruction ()->getModule ();
687
703
std::string PathDescription = getPathDescription (
688
704
VD->getBaseName (), BaseType, MainAccess.getSubPath (), M);
705
+
706
+ // Determine whether we can safely suggest replacing the violation with
707
+ // a call to MutableCollection.swapAt().
708
+ bool SuggestSwapAt = false ;
709
+ CallExpr *CallToReplace = nullptr ;
710
+ Expr *Base = nullptr ;
711
+ Expr *SwapIndex1 = nullptr ;
712
+ Expr *SwapIndex2 = nullptr ;
713
+ if (SecondAccess.getRecordKind () == RecordedAccessKind::BeginInstruction) {
714
+ SuggestSwapAt = canReplaceWithCallToCollectionSwapAt (
715
+ FirstAccess.getInstruction (), SecondAccess.getInstruction (),
716
+ CallsToSwap, Ctx, CallToReplace, Base, SwapIndex1, SwapIndex2);
717
+ }
718
+
689
719
auto D =
690
720
diagnose (Ctx, MainAccess.getAccessLoc ().getSourceLoc (), DiagnosticID,
691
- PathDescription, AccessKindForMain);
721
+ PathDescription, AccessKindForMain, SuggestSwapAt );
692
722
D.highlight (RangeForMain);
693
- if (SecondAccess.getRecordKind () == RecordedAccessKind::BeginInstruction) {
694
- tryFixItWithCallToCollectionSwapAt (FirstAccess.getInstruction (),
695
- SecondAccess.getInstruction (),
696
- CallsToSwap, Ctx, D);
697
- }
723
+ if (SuggestSwapAt)
724
+ addSwapAtFixit (D, CallToReplace, Base, SwapIndex1, SwapIndex2,
725
+ Ctx.SourceMgr );
698
726
} else {
699
727
auto DiagnosticID = (Ctx.LangOpts .isSwiftVersion3 () ?
700
728
diag::exclusivity_access_required_unknown_decl_swift3 :
0 commit comments