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