[Refactoring][Migrator] Fix rename of callsites with a trailing closure argument

A label range of 0 length was being reported as the label of trailing closure
arguments, just before the opening '{'.

For the rename refactoring, this meant that if the corresponding parameter had
an external label (e.g. 'a') the occurrence would be treated as not matching the
expected symbol name, and so not be updated at all.

For the migrator, when renaming a function with an unlabelled closure for its
last parameter to have a label, it would incorrectly insert the new label in
front of the opening '{' on all of that function's callsites with trailing
closures.

Resolves rdar://problem/42162571
This commit is contained in:
Nathan Hawes
2018-12-19 14:55:55 -08:00
parent f85bb3ee47
commit 43c2f27783
16 changed files with 212 additions and 4 deletions

View File

@@ -598,6 +598,7 @@ enum class LabelRangeEndAt: int8_t {
struct CallArgInfo {
Expr *ArgExp;
CharSourceRange LabelRange;
bool IsTrailingClosure;
CharSourceRange getEntireCharRange(const SourceManager &SM) const;
};
@@ -605,6 +606,8 @@ std::vector<CallArgInfo>
getCallArgInfo(SourceManager &SM, Expr *Arg, LabelRangeEndAt EndKind);
// Get the ranges of argument labels from an Arg, either tuple or paren.
// This includes empty ranges for any unlabelled arguments, and excludes
// trailing closures.
std::vector<CharSourceRange>
getCallArgLabelRanges(SourceManager &SM, Expr *Arg, LabelRangeEndAt EndKind);

View File

@@ -1640,15 +1640,18 @@ getCallArgInfo(SourceManager &SM, Expr *Arg, LabelRangeEndAt EndKind) {
if (EndKind == LabelRangeEndAt::LabelNameOnly)
LabelEnd = LabelStart.getAdvancedLoc(NameIdentifier.getLength());
}
bool IsTrailingClosure = TE->hasTrailingClosure() &&
ElemIndex == TE->getNumElements() - 1;
InfoVec.push_back({getSingleNonImplicitChild(Elem),
CharSourceRange(SM, LabelStart, LabelEnd)});
CharSourceRange(SM, LabelStart, LabelEnd), IsTrailingClosure});
++ElemIndex;
}
} else if (auto *PE = dyn_cast<ParenExpr>(Arg)) {
if (auto Sub = PE->getSubExpr())
InfoVec.push_back({getSingleNonImplicitChild(Sub),
CharSourceRange(Sub->getStartLoc(), 0)});
CharSourceRange(Sub->getStartLoc(), 0),
PE->hasTrailingClosure()
});
}
return InfoVec;
}
@@ -1657,7 +1660,13 @@ std::vector<CharSourceRange> swift::ide::
getCallArgLabelRanges(SourceManager &SM, Expr *Arg, LabelRangeEndAt EndKind) {
std::vector<CharSourceRange> Ranges;
auto InfoVec = getCallArgInfo(SM, Arg, EndKind);
std::transform(InfoVec.begin(), InfoVec.end(), std::back_inserter(Ranges),
auto EndWithoutTrailing = std::remove_if(InfoVec.begin(), InfoVec.end(),
[](CallArgInfo &Info) {
return Info.IsTrailingClosure;
});
std::transform(InfoVec.begin(), EndWithoutTrailing,
std::back_inserter(Ranges),
[](CallArgInfo &Info) { return Info.LabelRange; });
return Ranges;
}

View File

@@ -529,6 +529,17 @@
"RightComment": "Int",
"ModuleName": "Cities"
},
{
"DiffItemKind": "CommonDiffItem",
"NodeKind": "Function",
"NodeAnnotation": "Rename",
"ChildIndex": "0",
"LeftUsr": "s:6Cities12ToplevelTypeC8trailingyyySayypGSgcF",
"LeftComment": "",
"RightUsr": "",
"RightComment": "trailing2(a:)",
"ModuleName": "Cities"
},
{
"DiffItemKind": "CommonDiffItem",
"NodeKind": "TypeDecl",

View File

@@ -62,6 +62,7 @@ open class ToplevelType {
public init() {}
public init(recordName: String) {}
open func member(_ x: @escaping ([Any]?) -> Void) {}
open func trailing(_ x: @escaping ([Any]?) -> Void) {}
}
public var GlobalAttribute: String = ""

View File

@@ -22,3 +22,7 @@ class MySubTopLevelType2: ToplevelType {
class SubCities: Cities {
override var yogurt: Int { return 2 }
}
func boo(_ a: ToplevelType) {
a.trailing { print($0!) }
}

View File

@@ -22,3 +22,7 @@ class MySubTopLevelType2: ToplevelType {
class SubCities: Cities {
override var cheese: Int { return 2 }
}
func boo(_ a: ToplevelType) {
a.trailing2 { print($0!) }
}

View File

@@ -28,6 +28,10 @@ func /*trailing:def*/withTrailingClosure(x: Int, y: () -> Int) {}
/*trailing:call*/withTrailingClosure(x: 2)
{ return 1}
func /*trailing-only:def*/trailingOnly(a: () -> ()) {}
/*trailing-only:call*/trailingOnly(a: {})
/*trailing-only:call*/trailingOnly {}
func /*varargs:def*/withVarargs(x: Int..., y: Int, _: Int) {}

View File

@@ -28,6 +28,10 @@ func /*trailing:def*/<base>withTrailingClosure</base>(<arglabel index=0>x</argla
/*trailing:call*/<base>withTrailingClosure</base>(<callarg index=0>x</callarg><callcolon index=0>: </callcolon>2)
{ return 1}
func /*trailing-only:def*/trailingOnly(a: () -> ()) {}
/*trailing-only:call*/trailingOnly(a: {})
/*trailing-only:call*/trailingOnly {}
func /*varargs:def*/withVarargs(x: Int..., y: Int, _: Int) {}

View File

@@ -0,0 +1,70 @@
func /*defaults:def*/withDefaults(_ xx: Int = 4, y: Int = 2, x: Int = 1) {}
// valid
/*defaults:call*/withDefaults()
/*defaults:call*/withDefaults(2)
/*defaults:call*/withDefaults(y: 2)
/*defaults:call*/withDefaults(2, x: 3)
/*defaults:call*/withDefaults(y: 2, x: 3)
/*defaults:call*/withDefaults(2, y: 1, x: 4)
// false positives
/*defaults:call*/withDefaults(y: 2, 3)
/*defaults:call*/withDefaults(y: 2, 4, x: 3)
// invalid
/*defaults:call*/withDefaults(x: 2, y: 3)
func /*trailing:def*/withTrailingClosure(x: Int, y: () -> Int) {}
// valid
/*trailing:call*/withTrailingClosure(x: 2, y: { return 1})
/*trailing:call*/withTrailingClosure(x: 2) { return 1}
// false positives
/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1}
/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1}
/*trailing:call*/withTrailingClosure(x: 2)
{ return 1}
func /*trailing-only:def*/<base>trailingOnly</base>(<arglabel index=0>a</arglabel><param index=0></param>: () -> ()) {}
/*trailing-only:call*/<base>trailingOnly</base>(<callarg index=0>a</callarg><callcolon index=0>: </callcolon>{})
/*trailing-only:call*/<base>trailingOnly</base> {}
func /*varargs:def*/withVarargs(x: Int..., y: Int, _: Int) {}
// valid
/*varargs:call*/withVarargs(x: 1, 2, 3, y: 2, 4)
/*varargs:call*/withVarargs(y: 2, 4)
// false positives
/*varargs:call*/withVarargs(x: 1, y: 2, 4, 5)
//invalid
/*varargs:call*/withVarargs(2, y: 2)
func /*varargs2:def*/withVarargs(x: Int, y: Int, _: Int...) {}
// valid
/*varargs2:call*/withVarargs(x: 1, y: 2, 4, 5)
/*varargs2:call*/withVarargs(x: 1, y: 2)
// false positive
/*varargs2:call*/withVarargs(x: 1, 2, y: 2, 4)
func /*mixed:def*/withAllOfTheAbove(x: Int = 2, _: Int..., z: Int = 2, c: () -> Int) {}
// valid
/*mixed:call*/withAllOfTheAbove(2){ return 1 }
/*mixed:call*/withAllOfTheAbove(x: 1, 2, c: {return 1})
/*mixed:call*/withAllOfTheAbove(x: 1, c: {return 1})
/*mixed:call*/withAllOfTheAbove(1, z: 1) { return 1 }
/*mixed:call*/withAllOfTheAbove(1, 2, c: {return 1})
// false positives
/*mixed:call*/withAllOfTheAbove(z: 1, 2, c: {return 1})

View File

@@ -28,6 +28,10 @@ func /*trailing:def*/withTrailingClosure(x: Int, y: () -> Int) {}
/*trailing:call*/withTrailingClosure(x: 2)
{ return 1}
func /*trailing-only:def*/trailingOnly(a: () -> ()) {}
/*trailing-only:call*/trailingOnly(a: {})
/*trailing-only:call*/trailingOnly {}
func /*varargs:def*/withVarargs(x: Int..., y: Int, _: Int) {}

View File

@@ -28,6 +28,10 @@ func /*trailing:def*/withTrailingClosure(x: Int, y: () -> Int) {}
/*trailing:call*/withTrailingClosure(x: 2)
{ return 1}
func /*trailing-only:def*/trailingOnly(a: () -> ()) {}
/*trailing-only:call*/trailingOnly(a: {})
/*trailing-only:call*/trailingOnly {}
func /*varargs:def*/withVarargs(x: Int..., y: Int, _: Int) {}

View File

@@ -28,6 +28,10 @@ func /*trailing:def*/betterName(a: Int, b: () -> Int) {}
/*trailing:call*/betterName(a: 2)
{ return 1}
func /*trailing-only:def*/trailingOnly(a: () -> ()) {}
/*trailing-only:call*/trailingOnly(a: {})
/*trailing-only:call*/trailingOnly {}
func /*varargs:def*/withVarargs(x: Int..., y: Int, _: Int) {}

View File

@@ -0,0 +1,70 @@
func /*defaults:def*/withDefaults(_ xx: Int = 4, y: Int = 2, x: Int = 1) {}
// valid
/*defaults:call*/withDefaults()
/*defaults:call*/withDefaults(2)
/*defaults:call*/withDefaults(y: 2)
/*defaults:call*/withDefaults(2, x: 3)
/*defaults:call*/withDefaults(y: 2, x: 3)
/*defaults:call*/withDefaults(2, y: 1, x: 4)
// false positives
/*defaults:call*/withDefaults(y: 2, 3)
/*defaults:call*/withDefaults(y: 2, 4, x: 3)
// invalid
/*defaults:call*/withDefaults(x: 2, y: 3)
func /*trailing:def*/withTrailingClosure(x: Int, y: () -> Int) {}
// valid
/*trailing:call*/withTrailingClosure(x: 2, y: { return 1})
/*trailing:call*/withTrailingClosure(x: 2) { return 1}
// false positives
/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1}
/*trailing:call*/withTrailingClosure(x: 1, y: 2) { return 1}
/*trailing:call*/withTrailingClosure(x: 2)
{ return 1}
func /*trailing-only:def*/betterName(b: () -> ()) {}
/*trailing-only:call*/betterName(b: {})
/*trailing-only:call*/betterName {}
func /*varargs:def*/withVarargs(x: Int..., y: Int, _: Int) {}
// valid
/*varargs:call*/withVarargs(x: 1, 2, 3, y: 2, 4)
/*varargs:call*/withVarargs(y: 2, 4)
// false positives
/*varargs:call*/withVarargs(x: 1, y: 2, 4, 5)
//invalid
/*varargs:call*/withVarargs(2, y: 2)
func /*varargs2:def*/withVarargs(x: Int, y: Int, _: Int...) {}
// valid
/*varargs2:call*/withVarargs(x: 1, y: 2, 4, 5)
/*varargs2:call*/withVarargs(x: 1, y: 2)
// false positive
/*varargs2:call*/withVarargs(x: 1, 2, y: 2, 4)
func /*mixed:def*/withAllOfTheAbove(x: Int = 2, _: Int..., z: Int = 2, c: () -> Int) {}
// valid
/*mixed:call*/withAllOfTheAbove(2){ return 1 }
/*mixed:call*/withAllOfTheAbove(x: 1, 2, c: {return 1})
/*mixed:call*/withAllOfTheAbove(x: 1, c: {return 1})
/*mixed:call*/withAllOfTheAbove(1, z: 1) { return 1 }
/*mixed:call*/withAllOfTheAbove(1, 2, c: {return 1})
// false positives
/*mixed:call*/withAllOfTheAbove(z: 1, 2, c: {return 1})

View File

@@ -28,6 +28,10 @@ func /*trailing:def*/withTrailingClosure(x: Int, y: () -> Int) {}
/*trailing:call*/withTrailingClosure(x: 2)
{ return 1}
func /*trailing-only:def*/trailingOnly(a: () -> ()) {}
/*trailing-only:call*/trailingOnly(a: {})
/*trailing-only:call*/trailingOnly {}
func /*varargs:def*/betterName(a: Int..., b: Int, c: Int) {}

View File

@@ -28,6 +28,10 @@ func /*trailing:def*/withTrailingClosure(x: Int, y: () -> Int) {}
/*trailing:call*/withTrailingClosure(x: 2)
{ return 1}
func /*trailing-only:def*/trailingOnly(a: () -> ()) {}
/*trailing-only:call*/trailingOnly(a: {})
/*trailing-only:call*/trailingOnly {}
func /*varargs:def*/withVarargs(x: Int..., y: Int, _: Int) {}

View File

@@ -28,6 +28,10 @@ func /*trailing:def*/withTrailingClosure(x: Int, y: () -> Int) {}
/*trailing:call*/withTrailingClosure(x: 2)
{ return 1}
func /*trailing-only:def*/trailingOnly(a: () -> ()) {}
/*trailing-only:call*/trailingOnly(a: {})
/*trailing-only:call*/trailingOnly {}
func /*varargs:def*/withVarargs(x: Int..., y: Int, _: Int) {}
@@ -69,6 +73,8 @@ func /*mixed:def*/withAllOfTheAbove(x: Int = 2, _: Int..., z: Int = 2, c: () ->
// RUN: diff -u %S/Outputs/callsites/defaults.swift.expected %t.result/callsites_defaults.swift
// RUN: %refactor -syntactic-rename -source-filename %s -pos="trailing" -is-function-like -old-name "withTrailingClosure(x:y:)" -new-name "betterName(a:b:)" >> %t.result/callsites_trailing.swift
// RUN: diff -u %S/Outputs/callsites/trailing.swift.expected %t.result/callsites_trailing.swift
// RUN: %refactor -syntactic-rename -source-filename %s -pos="trailing-only" -is-function-like -old-name "trailingOnly(a:)" -new-name "betterName(b:)" >> %t.result/callsites_trailing_only.swift
// RUN: diff -u %S/Outputs/callsites/trailing_only.swift.expected %t.result/callsites_trailing_only.swift
// RUN: %refactor -syntactic-rename -source-filename %s -pos="varargs" -is-function-like -old-name "withVarargs(x:y:_:)" -new-name "betterName(a:b:c:)" >> %t.result/callsites_varargs.swift
// RUN: diff -u %S/Outputs/callsites/varargs.swift.expected %t.result/callsites_varargs.swift
// RUN: %refactor -syntactic-rename -source-filename %s -pos="varargs2" -is-function-like -old-name "withVarargs(x:y:_:)" -new-name "betterName(a:b:c:)" >> %t.result/callsites_varargs2.swift
@@ -80,3 +86,5 @@ func /*mixed:def*/withAllOfTheAbove(x: Int = 2, _: Int..., z: Int = 2, c: () ->
// RUN: diff -u %S/FindRangeOutputs/callsites/defaults.swift.expected %t.ranges/callsites_defaults.swift
// RUN: %refactor -find-rename-ranges -source-filename %s -pos="trailing" -is-function-like -old-name "withTrailingClosure(x:y:)" >> %t.ranges/callsites_trailing.swift
// RUN: diff -u %S/FindRangeOutputs/callsites/trailing.swift.expected %t.ranges/callsites_trailing.swift
// RUN: %refactor -find-rename-ranges -source-filename %s -pos="trailing-only" -is-function-like -old-name "trailingOnly(a:)" >> %t.ranges/callsites_trailing_only.swift
// RUN: diff -u %S/FindRangeOutputs/callsites/trailing_only.swift.expected %t.ranges/callsites_trailing_only.swift