Skip to content

Commit 0410f17

Browse files
author
Nathan Hawes
committed
[Migrator] Fix optional types not being updated correctly in some cases
If the replacement type was a function type or protocol-constrained type (e.g. 'Class & Proto) and the existing type had a postfix ? or !, we weren't wrapping the replacement in parens. This would result in an incorrect/ambiguous new type. This patch wraps the replacement in parens if it contains an & or ->, the existing type has a trailing ? or !. Resolves rdar://problem/32082269.
1 parent adccb52 commit 0410f17

File tree

5 files changed

+55
-13
lines changed

5 files changed

+55
-13
lines changed

lib/Migrator/APIDiffMigratorPass.cpp

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,19 +37,22 @@ namespace {
3737

3838
struct FoundResult {
3939
SourceRange TokenRange;
40-
bool Optional; // Range has a trailing ? or ! included
40+
bool Optional; // Range includes a trailing ? or !, e.g. [SomeType!]
4141
bool Suffixable; // No need to wrap parens when adding optionality
42+
bool Suffixed; // Range is followed by a trailing ? or !, e.g. [SomeType]!
4243
bool isValid() const { return TokenRange.isValid(); }
4344
};
4445

4546
class ChildIndexFinder : public TypeReprVisitor<ChildIndexFinder, FoundResult> {
4647
ArrayRef<uint8_t> ChildIndices;
48+
bool ParentIsOptional;
4749

4850
public:
4951
ChildIndexFinder(ArrayRef<uint8_t> ChildIndices) :
5052
ChildIndices(ChildIndices) {}
5153

5254
FoundResult findChild(AbstractFunctionDecl *Parent) {
55+
ParentIsOptional = false;
5356
auto NextIndex = consumeNext();
5457
if (!NextIndex) {
5558
if (auto Func = dyn_cast<FuncDecl>(Parent))
@@ -60,9 +63,9 @@ class ChildIndexFinder : public TypeReprVisitor<ChildIndexFinder, FoundResult> {
6063
if (!Optional)
6164
End = Init->getNameLoc();
6265
return {SourceRange(Init->getNameLoc(), End), Optional,
63-
/*suffixable=*/true};
66+
/*suffixable=*/true, /*suffixed=*/false};
6467
}
65-
return {SourceRange(), false, false};
68+
return {SourceRange(), false, false, false};
6669
}
6770

6871
for (auto *Params: Parent->getParameterLists()) {
@@ -100,7 +103,7 @@ class ChildIndexFinder : public TypeReprVisitor<ChildIndexFinder, FoundResult> {
100103

101104
FoundResult findChild(TypeLoc Loc) {
102105
if (!Loc.hasLocation())
103-
return {SourceRange(), false, false};
106+
return {SourceRange(), false, false, false};
104107
return visit(Loc.getTypeRepr());
105108
}
106109

@@ -110,12 +113,16 @@ class ChildIndexFinder : public TypeReprVisitor<ChildIndexFinder, FoundResult> {
110113
FoundResult handleParent(TypeRepr *Parent, const ArrayRef<T> Children,
111114
bool Optional = false, bool Suffixable = true) {
112115
if (!hasNextIndex())
113-
return {Parent->getSourceRange(), Optional, Suffixable};
116+
return {
117+
Parent->getSourceRange(),
118+
Optional, Suffixable, /*Suffixed=*/ParentIsOptional
119+
};
114120
auto NextIndex = consumeNext();
115121
if (isUserTypeAlias(Parent))
116-
return {SourceRange(), false, false};
122+
return {SourceRange(), false, false, false};
117123
assert(NextIndex < Children.size());
118124
TypeRepr *Child = Children[NextIndex];
125+
ParentIsOptional = Optional;
119126
return visit(Child);
120127
}
121128

@@ -137,7 +144,7 @@ class ChildIndexFinder : public TypeReprVisitor<ChildIndexFinder, FoundResult> {
137144
}
138145

139146
FoundResult visitErrorTypeRepr(ErrorTypeRepr *T) {
140-
return {SourceRange(), false, false};
147+
return {SourceRange(), false, false, false};
141148
}
142149

143150
FoundResult visitAttributedTypeRepr(AttributedTypeRepr *T) {
@@ -159,8 +166,10 @@ class ChildIndexFinder : public TypeReprVisitor<ChildIndexFinder, FoundResult> {
159166
FoundResult visitTupleTypeRepr(TupleTypeRepr *T) {
160167
// Single element TupleTypeReprs may be arbitrarily nested so don't count
161168
// as their own index level
162-
if (T->getNumElements() == 1)
169+
if (T->getNumElements() == 1) {
170+
ParentIsOptional = false;
163171
return visit(T->getElement(0));
172+
}
164173
return handleParent(T, T->getElements());
165174
}
166175

@@ -425,6 +434,10 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker {
425434
}
426435
}
427436

437+
bool typeReplacementMayNeedParens(StringRef Replacement) const {
438+
return Replacement.contains('&') || Replacement.contains("->");
439+
}
440+
428441
bool walkToDeclPre(Decl *D, CharSourceRange Range) override {
429442
if (D->isImplicit())
430443
return true;
@@ -465,6 +478,10 @@ struct APIDiffMigratorPass : public ASTMigratorPass, public SourceEntityWalker {
465478
break;
466479
case ide::api::NodeAnnotation::TypeRewritten:
467480
Editor.replace(Result.TokenRange, DiffItem->RightComment);
481+
if (Result.Suffixed && typeReplacementMayNeedParens(DiffItem->RightComment)) {
482+
Editor.insertBefore(Result.TokenRange.Start, "(");
483+
Editor.insertAfterToken(Result.TokenRange.End, ")");
484+
}
468485
break;
469486
default:
470487
break;

test/Migrator/API.json

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -207,12 +207,12 @@
207207
{
208208
"DiffItemKind": "CommonDiffItem",
209209
"NodeKind": "Function",
210-
"NodeAnnotation": "UnwrapOptional",
211-
"ChildIndex": "0",
210+
"NodeAnnotation": "TypeRewritten",
211+
"ChildIndex": "0:0",
212212
"LeftUsr": "s:6CitiesAAC7buderimABSgyF",
213213
"LeftComment": "",
214214
"RightUsr": "",
215-
"RightComment": "",
215+
"RightComment": "Cities & ExtraCities",
216216
"ModuleName": "Cities"
217217
},
218218
{
@@ -390,5 +390,27 @@
390390
"RightUsr": "",
391391
"RightComment": "setZooLocationNew(newX:newY:newZ:)",
392392
"ModuleName": "Cities"
393+
},
394+
{
395+
"DiffItemKind": "CommonDiffItem",
396+
"NodeKind": "Function",
397+
"NodeAnnotation": "TypeRewritten",
398+
"ChildIndex": "1:0",
399+
"LeftUsr": "s:6CitiesAAC8maroochyySiSg1x_AD1ytF",
400+
"LeftComment": "Int",
401+
"RightUsr": "",
402+
"RightComment": "(Int) -> Int",
403+
"ModuleName": "Cities"
404+
},
405+
{
406+
"DiffItemKind": "CommonDiffItem",
407+
"NodeKind": "Function",
408+
"NodeAnnotation": "TypeRewritten",
409+
"ChildIndex": "2:0",
410+
"LeftUsr": "s:6CitiesAAC8maroochyySiSg1x_AD1ytF",
411+
"LeftComment": "Int",
412+
"RightUsr": "",
413+
"RightComment": "(Int) -> Int",
414+
"ModuleName": "Cities"
393415
}
394416
]

test/Migrator/Inputs/cities.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ open class Cities {
88
open func yandina(x: [[String : Cities]]!) {}
99
open func buderim() -> Cities? { return Cities(x: 1) }
1010
open func noosa() -> [[String : Cities]?] { return [] }
11+
open func maroochy(x: Int?, y: Int?) {}
1112
}
1213

1314
public protocol ExtraCities {

test/Migrator/wrap_optional.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ class MyCities : Cities {
1212
override func mooloolaba(x: Cities, y: Cities?) {}
1313
override func toowoomba(x: [Cities], y: [Cities]?) {}
1414
override func mareeba(x: [String : Cities?], y: [String : Cities]?) {}
15+
override func maroochy(x: (Int)?, y: Int?) {}
1516
}
1617

1718
class MySubCities : MyCities {}
1819

1920
class MySubSubCities : MySubCities {
2021
override func yandina(x: [[String : Cities]]!) {}
21-
override func buderim() -> Cities? { return Cities(x: 1) }
22+
override func buderim() -> Cities? { return nil }
2223
override func noosa() -> [[String : Cities]?] { return [] }
2324
}
2425

test/Migrator/wrap_optional.swift.expected

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,14 @@ class MyCities : Cities {
1212
override func newMooloolaba(newX x: Cities?, newY y: Cities) {}
1313
override func toowoomba(x: [Cities?], y: [Cities?]) {}
1414
override func mareeba(x: [String? : Cities], y: [Int : Cities]) {}
15+
override func maroochy(x: ((Int) -> Int)?, y: ((Int) -> Int)?) {}
1516
}
1617

1718
class MySubCities : MyCities {}
1819

1920
class MySubSubCities : MySubCities {
2021
override func yandina(x: [[Int : Cities]]?) {}
21-
override func buderim() -> Cities { return Cities(x: 1) }
22+
override func buderim() -> (Cities & ExtraCities)? { return nil }
2223
override func noosa() -> [[Int : Cities]] { return [] }
2324
}
2425

0 commit comments

Comments
 (0)