@@ -22,9 +22,115 @@ using namespace swift;
22
22
using namespace swift ::migrator;
23
23
24
24
namespace {
25
+
26
+ class ShorthandFinder : public ASTWalker {
27
+ private:
28
+ llvm::DenseMap<ParamDecl*, std::vector<Expr*>> References;
29
+
30
+ std::pair<bool , Expr *> walkToExprPre (Expr *E) override {
31
+ Expr *ParentElementExpr = nullptr ;
32
+ Expr *OrigE = E;
33
+
34
+ if (auto *TupleElem = dyn_cast<TupleElementExpr>(E)) {
35
+ ParentElementExpr = TupleElem;
36
+ E = TupleElem->getBase ()->getSemanticsProvidingExpr ();
37
+ }
38
+
39
+ if (auto *DeclRef = dyn_cast<DeclRefExpr>(E)) {
40
+ ParamDecl *Decl = dyn_cast<ParamDecl>(DeclRef->getDecl ());
41
+ Expr *Reference = ParentElementExpr? ParentElementExpr : DeclRef;
42
+ if (References.count (Decl) && !Reference->isImplicit ()) {
43
+ References[Decl].push_back (Reference);
44
+ return { false , OrigE };
45
+ }
46
+ }
47
+ return { true , OrigE };
48
+ }
49
+
50
+ public:
51
+ ShorthandFinder (ClosureExpr *Expr) {
52
+ if (!Expr->hasAnonymousClosureVars ())
53
+ return ;
54
+ References.clear ();
55
+ for (auto *Param: *Expr->getParameters ()) {
56
+ References[Param] = {};
57
+ }
58
+ Expr->walk (*this );
59
+ }
60
+
61
+ void forEachReference (llvm::function_ref<void (Expr*, ParamDecl*)> Callback) {
62
+ for (auto Entry: References) {
63
+ for (auto *Expr : Entry.getSecond ()) {
64
+ Callback (Expr, Entry.getFirst ());
65
+ }
66
+ }
67
+ }
68
+ };
25
69
26
70
struct TupleSplatMigratorPass : public ASTMigratorPass ,
27
71
public SourceEntityWalker {
72
+
73
+ bool handleClosureShorthandMismatch (FunctionConversionExpr *FC) {
74
+ if (!SF->getASTContext ().LangOpts .isSwiftVersion3 () || !FC->isImplicit () ||
75
+ !isa<ClosureExpr>(FC->getSubExpr ())) {
76
+ return false ;
77
+ }
78
+
79
+ auto *Closure = cast<ClosureExpr>(FC->getSubExpr ());
80
+ if (Closure->getInLoc ().isValid ())
81
+ return false ;
82
+
83
+ FunctionType *FuncTy = FC->getType ()->getAs <FunctionType>();
84
+
85
+ unsigned NativeArity = 0 ;
86
+ if (isa<ParenType>(FuncTy->getInput ().getPointer ())) {
87
+ NativeArity = 1 ;
88
+ } else if (auto TT = FuncTy->getInput ()->getAs <TupleType>()) {
89
+ NativeArity = TT->getNumElements ();
90
+ }
91
+
92
+ unsigned ClosureArity = Closure->getParameters ()->size ();
93
+ if (NativeArity == ClosureArity)
94
+ return false ;
95
+
96
+ ShorthandFinder Finder (Closure);
97
+ if (NativeArity == 1 && ClosureArity > 1 ) {
98
+ // Prepend $0. to existing references
99
+ Finder.forEachReference ([this ](Expr *Ref, ParamDecl* Def) {
100
+ if (auto *TE = dyn_cast<TupleElementExpr>(Ref))
101
+ Ref = TE->getBase ();
102
+ SourceLoc AfterDollar = Ref->getStartLoc ().getAdvancedLoc (1 );
103
+ Editor.insert (AfterDollar, " 0." );
104
+ });
105
+ return true ;
106
+ }
107
+
108
+ if (ClosureArity == 1 && NativeArity > 1 ) {
109
+ // Remove $0. from existing references or if it's only $0, replace it
110
+ // with a tuple of the native arity, e.g. ($0, $1, $2)
111
+ Finder.forEachReference ([this , NativeArity](Expr *Ref, ParamDecl *Def) {
112
+ if (auto *TE = dyn_cast<TupleElementExpr>(Ref)) {
113
+ SourceLoc Start = TE->getStartLoc ();
114
+ SourceLoc End = TE->getLoc ();
115
+ Editor.replace (CharSourceRange (SM, Start, End), " $" );
116
+ } else {
117
+ std::string TupleText;
118
+ {
119
+ llvm::raw_string_ostream OS (TupleText);
120
+ for (size_t i = 1 ; i != NativeArity; ++i) {
121
+ OS << " , " ;
122
+ OS << " $" << i;
123
+ }
124
+ OS << " )" ;
125
+ }
126
+ Editor.insert (Ref->getStartLoc (), " (" );
127
+ Editor.insertAfterToken (Ref->getEndLoc (), TupleText);
128
+ }
129
+ });
130
+ return true ;
131
+ }
132
+ return false ;
133
+ }
28
134
29
135
// / Migrates code that compiles fine in Swift 3 but breaks in Swift 4 due to
30
136
// / changes in how the typechecker handles tuple arguments.
@@ -86,7 +192,7 @@ struct TupleSplatMigratorPass : public ASTMigratorPass,
86
192
auto parenT = dyn_cast<ParenType>(fnTy2->getInput ().getPointer ());
87
193
if (!parenT)
88
194
return false ;
89
- auto tupleInFn = dyn_cast <TupleType>(parenT-> getUnderlyingType (). getPointer () );
195
+ auto tupleInFn = parenT-> getAs <TupleType>();
90
196
if (!tupleInFn)
91
197
return false ;
92
198
if (!E->getArg ())
@@ -197,7 +303,9 @@ struct TupleSplatMigratorPass : public ASTMigratorPass,
197
303
}
198
304
199
305
bool walkToExprPre (Expr *E) override {
200
- if (auto *CE = dyn_cast<CallExpr>(E)) {
306
+ if (auto *FCE = dyn_cast<FunctionConversionExpr>(E)) {
307
+ handleClosureShorthandMismatch (FCE);
308
+ } else if (auto *CE = dyn_cast<CallExpr>(E)) {
201
309
handleTupleArgumentMismatches (CE);
202
310
}
203
311
return true ;
0 commit comments