Skip to content

Commit ee7a4d7

Browse files
author
Nathan Hawes
committed
[IDE][SourceKit] Support escaped identifiers for the syntactic rename and related idents requests.
Resolves rdar://problem/46409010 Resolves rdar://problem/48256383
1 parent e8df655 commit ee7a4d7

16 files changed

+135
-10
lines changed

lib/IDE/Refactoring.cpp

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,7 @@ class Renamer {
9999
bool renameBase(CharSourceRange Range, RefactoringRangeKind RangeKind) {
100100
assert(Range.isValid());
101101

102-
StringRef Existing = Range.str();
103-
if (Existing != Old.base())
102+
if (stripBackticks(Range).str() != Old.base())
104103
return true;
105104
doRenameBase(Range, RangeKind);
106105
return false;
@@ -131,6 +130,27 @@ class Renamer {
131130
bool isOperator() const { return Lexer::isOperator(Old.base()); }
132131

133132
private:
133+
134+
/// Returns the range of the (possibly escaped) identifier at the start of
135+
/// \p Range and updates \p IsEscaped to indicate whether it's escaped or not.
136+
CharSourceRange getLeadingIdentifierRange(CharSourceRange Range, bool &IsEscaped) {
137+
assert(Range.isValid() && Range.getByteLength());
138+
IsEscaped = Range.str().front() == '`';
139+
SourceLoc Start = Range.getStart();
140+
if (IsEscaped)
141+
Start = Start.getAdvancedLoc(1);
142+
return Lexer::getCharSourceRangeFromSourceRange(SM, Start);
143+
}
144+
145+
CharSourceRange stripBackticks(CharSourceRange Range) {
146+
StringRef Content = Range.str();
147+
if (Content.size() < 3 || Content.front() != '`' || Content.back() != '`') {
148+
return Range;
149+
}
150+
return CharSourceRange(Range.getStart().getAdvancedLoc(1),
151+
Range.getByteLength() - 2);
152+
}
153+
134154
void splitAndRenameLabel(CharSourceRange Range, LabelRangeType RangeType,
135155
size_t NameIndex) {
136156
switch (RangeType) {
@@ -218,13 +238,15 @@ class Renamer {
218238

219239
bool labelRangeMatches(CharSourceRange Range, LabelRangeType RangeType, StringRef Expected) {
220240
if (Range.getByteLength()) {
221-
CharSourceRange ExistingLabelRange =
222-
Lexer::getCharSourceRangeFromSourceRange(SM, Range.getStart());
241+
bool IsEscaped = false;
242+
CharSourceRange ExistingLabelRange = getLeadingIdentifierRange(Range, IsEscaped);
223243
StringRef ExistingLabel = ExistingLabelRange.str();
244+
bool IsSingleName = Range == ExistingLabelRange ||
245+
(IsEscaped && Range.getByteLength() == ExistingLabel.size() + 2);
224246

225247
switch (RangeType) {
226248
case LabelRangeType::NoncollapsibleParam:
227-
if (ExistingLabelRange == Range && Expected.empty()) // subscript([x]: Int)
249+
if (IsSingleName && Expected.empty()) // subscript([x]: Int)
228250
return true;
229251
LLVM_FALLTHROUGH;
230252
case LabelRangeType::CallArg:

lib/IDE/SourceEntityWalker.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,8 +119,12 @@ bool SemaAnnotator::walkToDeclPre(Decl *D) {
119119
bool IsExtension = false;
120120

121121
if (auto *VD = dyn_cast<ValueDecl>(D)) {
122-
if (VD->hasName() && !VD->isImplicit())
122+
if (VD->hasName() && !VD->isImplicit()) {
123+
SourceManager &SM = VD->getASTContext().SourceMgr;
123124
NameLen = VD->getBaseName().userFacingName().size();
125+
if (Loc.isValid() && SM.extractText({Loc, 1}) == "`")
126+
NameLen += 2;
127+
}
124128

125129
auto ReportParamList = [&](ParameterList *PL) {
126130
for (auto *PD : *PL) {
@@ -670,7 +674,11 @@ bool SemaAnnotator::passCallAsFunctionReference(ValueDecl *D, SourceLoc Loc,
670674

671675
bool SemaAnnotator::
672676
passReference(ValueDecl *D, Type Ty, DeclNameLoc Loc, ReferenceMetaData Data) {
673-
return passReference(D, Ty, Loc.getBaseNameLoc(), Loc.getSourceRange(), Data);
677+
SourceManager &SM = D->getASTContext().SourceMgr;
678+
SourceLoc BaseStart = Loc.getBaseNameLoc(), BaseEnd = BaseStart;
679+
if (SM.extractText({BaseStart, 1}) == "`")
680+
BaseEnd = Lexer::getLocForEndOfToken(SM, BaseStart.getAdvancedLoc(1));
681+
return passReference(D, Ty, BaseStart, {BaseStart, BaseEnd}, Data);
674682
}
675683

676684
bool SemaAnnotator::

lib/IDE/SwiftSourceDocInfo.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@ static std::vector<CharSourceRange> getLabelRanges(const ParameterList* List,
131131
} else {
132132
NameLoc = ParamLoc;
133133
NameLength = Param->getNameStr().size();
134+
if (SM.extractText({NameLoc, 1}) == "`")
135+
NameLength += 2;
134136
LabelRanges.push_back(CharSourceRange(NameLoc, NameLength));
135137
}
136138
}

test/Index/roles.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,3 +531,10 @@ func test<M>(_: M, value: Int?) {
531531
// CHECK: [[@LINE-1]]:34 | constructor/Swift | init(_:) | s:14swift_ide_test7FromIntPyxSicfc | Ref,RelCont | rel: 1
532532
}
533533
}
534+
535+
func `escapedName`(`x`: Int) {}
536+
// CHECK: [[@LINE-1]]:6 | function/Swift | escapedName(x:) | {{.*}} | Def | rel: 0
537+
`escapedName`(`x`: 2)
538+
// CHECK: [[@LINE-1]]:1 | function/Swift | escapedName(x:) | {{.*}} | Ref,Call | rel: 0
539+
`escapedName`(`x`:)
540+
// CHECK: [[@LINE-1]]:1 | function/Swift | escapedName(x:) | {{.*}} | Ref | rel: 0

test/SourceKit/NameTranslation/swiftnames.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class C1 : NSObject {
88
}
99
func takeC1(a : Int, b: Int, c :Int) -> C1 {
1010
let C1Ins = C1(a: a, b: b)
11-
C1Ins.foo(a: a, b: b, c: c)
11+
C1Ins.`foo`(`a`: a, `b`: b, c: c)
1212
C1Ins.foo1(a: a, b, c: c)
1313
C1Ins.foo2(a, b, c: c)
1414
C1Ins.foo3()
@@ -57,11 +57,16 @@ class C3: NSObject {
5757

5858
// REQUIRES: objc_interop
5959
// RUN: %sourcekitd-test -req=translate -swift-name "foo(a:b:c:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK1 %s
60+
// RUN: %sourcekitd-test -req=translate -swift-name '`foo`(`a`:b:c:)' -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK1 %s
61+
// RUN: %sourcekitd-test -req=translate -swift-name '`foo(`a:b:c:)' -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK1 %s
6062
// RUN: %sourcekitd-test -req=translate -swift-name "foo(a:b:c:)" -pos=11:11 %s -print-raw-response -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK_RAW1 %s
6163
// RUN: %sourcekitd-test -req=translate -swift-name "bar(x:y:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKFEWER1 %s
6264
// RUN: %sourcekitd-test -req=translate -swift-name "bar(::)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKMISSING1 %s
65+
// RUN: %sourcekitd-test -req=translate -swift-name 'bar(`:`:)' -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKMISSING1 %s
6366
// RUN: %sourcekitd-test -req=translate -swift-name "(x:y:z:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKMISSING2 %s
67+
// RUN: %sourcekitd-test -req=translate -swift-name '`(x:y:z:)' -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECKMISSING2 %s
6468
// RUN: %sourcekitd-test -req=translate -swift-name "foo(a1:b1:c1:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK2 %s
69+
// RUN: %sourcekitd-test -req=translate -swift-name '`foo`(a1:`b1`:c1:)' -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK2 %s
6570
// RUN: %sourcekitd-test -req=translate -swift-name "foo(_:b1:c1:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK3 %s
6671
// RUN: %sourcekitd-test -req=translate -swift-name "foo1(_:_:c2:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK4 %s
6772
// RUN: %sourcekitd-test -req=translate -swift-name "foo1(_:_:_:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %s | %FileCheck -check-prefix=CHECK5 %s

test/SourceKit/Refactoring/basic.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ struct HasInitWithDefaultArgs {
113113
HasInitWithDefaultArgs(z: 45)
114114
HasInitWithDefaultArgs(y: 45, z: 89)
115115

116+
func `hasBackticks`(`x`: Int) {}
117+
`hasBackticks`(`x`:2)
118+
116119
// RUN: %sourcekitd-test -req=cursor -pos=3:1 -end-pos=5:13 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK1
117120

118121
// CHECK1: ACTIONS BEGIN
@@ -151,6 +154,13 @@ HasInitWithDefaultArgs(y: 45, z: 89)
151154
// RUN: %sourcekitd-test -req=cursor -pos=114:31 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL
152155
// RUN: %sourcekitd-test -req=cursor -pos=114:31 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL
153156

157+
// RUN: %sourcekitd-test -req=cursor -pos=116:6 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL
158+
// RUN: %sourcekitd-test -req=cursor -pos=116:7 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL
159+
// RUN: %sourcekitd-test -req=cursor -pos=117:1 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL
160+
// RUN: %sourcekitd-test -req=cursor -pos=117:2 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL
161+
// RUN: %sourcekitd-test -req=cursor -pos=117:16 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL
162+
// RUN: %sourcekitd-test -req=cursor -pos=117:17 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL
163+
154164
// RUN: %sourcekitd-test -req=cursor -pos=35:10 -end-pos=35:16 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-RENAME-EXTRACT
155165
// RUN: %sourcekitd-test -req=cursor -pos=35:10 -end-pos=35:16 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-RENAME-EXTRACT
156166

test/SourceKit/RelatedIdents/related_idents.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ struct Projection<T> {
6969
var item: T
7070
}
7171

72+
func `escapedName`(`x`: Int) {}
73+
`escapedName`(`x`: 2)
74+
`escapedName`(`x`:)
75+
escapedName(`x`: 2)
76+
escapedName(`x`:)
77+
7278
// RUN: %sourcekitd-test -req=related-idents -pos=6:17 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK1 %s
7379
// CHECK1: START RANGES
7480
// CHECK1-NEXT: 1:7 - 2
@@ -146,3 +152,16 @@ struct Projection<T> {
146152
// CHECK10-NEXT: 63:10 - 3
147153
// CHECK10-NEXT: 64:10 - 3
148154
// CHECK10-NEXT: END RANGES
155+
156+
// RUN: %sourcekitd-test -req=related-idents -pos=72:6 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK11 %s
157+
// RUN: %sourcekitd-test -req=related-idents -pos=73:1 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK11 %s
158+
// RUN: %sourcekitd-test -req=related-idents -pos=73:2 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK11 %s
159+
// RUN: %sourcekitd-test -req=related-idents -pos=74:1 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK11 %s
160+
// RUN: %sourcekitd-test -req=related-idents -pos=74:2 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK11 %s
161+
// CHECK11: START RANGES
162+
// CHECK11-NEXT: 72:6 - 13
163+
// CHECK11-NEXT: 73:1 - 13
164+
// CHECK11-NEXT: 74:1 - 13
165+
// CHECK11-NEXT: 75:1 - 11
166+
// CHECK11-NEXT: 76:1 - 11
167+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
struct Foo {
2+
/*test:def*/<keywordBase>subscript</keywordBase>(<arglabel index=0>`x`</arglabel> <noncollapsibleparam index=0>y</noncollapsibleparam>: Int) -> Int { fatalError() }
3+
}
4+
Foo()/*test:ref*/[<callarg index=0>`x`</callarg><callcolon index=0>: </callcolon>10]
5+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
struct Foo {
2+
/*test:def*/<keywordBase>subscript</keywordBase>(<arglabel index=0></arglabel><noncollapsibleparam index=0>`x`</noncollapsibleparam>: Int) -> Int { fatalError() }
3+
}
4+
Foo()/*test:ref*/[<callcombo index=0></callcombo>10]
5+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
func /*test:def*/<base>`foo`</base>(<arglabel index=0>`x`</arglabel><param index=0> `y`</param>: Int) {}
2+
/*test:call*/<base>`foo`</base>(<callarg index=0>`x`</callarg><callcolon index=0>: </callcolon>2)
3+
_ = /*test:ref*/<base>`foo`</base>(<sel index=0>`x`</sel>:)
4+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
func /*test:def*/<base>`foo`</base>(<arglabel index=0>`x`</arglabel><param index=0></param>: Int) {}
2+
/*test:call*/<base>`foo`</base>(<callarg index=0>`x`</callarg><callcolon index=0>: </callcolon>2)
3+
_ = /*test:ref*/<base>`foo`</base>(<sel index=0>`x`</sel>:)
4+
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
struct Foo {
2+
/*test:def*/subscript(`x` y: Int) -> Int { fatalError() }
3+
}
4+
Foo()/*test:ref*/[`x`: 10]
5+
6+
// RUN: %empty-directory(%t.ranges)
7+
// RUN: %refactor -find-rename-ranges -source-filename %s -pos="test" -is-function-like -old-name "subscript(x:)" >> %t.ranges/backticks-noncollapsible-separate-arglabel.swift.expected
8+
// RUN: diff -u %S/FindRangeOutputs/backticks-noncollapsible-separate-arglabel.swift.expected %t.ranges/backticks-noncollapsible-separate-arglabel.swift.expected
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
struct Foo {
2+
/*test:def*/subscript(`x`: Int) -> Int { fatalError() }
3+
}
4+
Foo()/*test:ref*/[10]
5+
6+
// RUN: %empty-directory(%t.ranges)
7+
// RUN: %refactor -find-rename-ranges -source-filename %s -pos="test" -is-function-like -old-name "subscript(_:)" >> %t.ranges/backticks-noncollapsible.swift.expected
8+
// RUN: diff -u %S/FindRangeOutputs/backticks-noncollapsible.swift.expected %t.ranges/backticks-noncollapsible.swift.expected
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
func /*test:def*/`foo`(`x` `y`: Int) {}
2+
/*test:call*/`foo`(`x`: 2)
3+
_ = /*test:ref*/`foo`(`x`:)
4+
5+
// RUN: %empty-directory(%t.ranges)
6+
// RUN: %refactor -find-rename-ranges -source-filename %s -pos="test" -is-function-like -old-name "foo(x:)" >> %t.ranges/backticks-separate-arglabel.swift.expected
7+
// RUN: diff -u %S/FindRangeOutputs/backticks-separate-arglabel.swift.expected %t.ranges/backticks-separate-arglabel.swift.expected
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
func /*test:def*/`foo`(`x`: Int) {}
2+
/*test:call*/`foo`(`x`: 2)
3+
_ = /*test:ref*/`foo`(`x`:)
4+
5+
// RUN: %empty-directory(%t.ranges)
6+
// RUN: %refactor -find-rename-ranges -source-filename %s -pos="test" -is-function-like -old-name "foo(x:)" >> %t.ranges/backticks.swift.expected
7+
// RUN: diff -u %S/FindRangeOutputs/backticks.swift.expected %t.ranges/backticks.swift.expected

tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,7 +1164,11 @@ static void handleSemanticRequest(
11641164
else
11651165
return Rec(createErrorRequestInvalid("'key.namekind' is unrecognizable"));
11661166
if (auto Base = Req.getString(KeyBaseName)) {
1167-
Input.BaseName = Base.getValue();
1167+
if (Input.NameKind == UIDKindNameSwift) {
1168+
Input.BaseName = Base.getValue().trim('`');
1169+
} else {
1170+
Input.BaseName = Base.getValue();
1171+
}
11681172
}
11691173
llvm::SmallVector<const char*, 4> ArgParts;
11701174
llvm::SmallVector<const char*, 4> Selectors;
@@ -1176,7 +1180,7 @@ static void handleSemanticRequest(
11761180
}
11771181
std::transform(ArgParts.begin(), ArgParts.end(),
11781182
std::back_inserter(Input.ArgNames),
1179-
[](const char *C) { return StringRef(C); });
1183+
[](const char *C) { return StringRef(C).trim('`'); });
11801184
std::transform(Selectors.begin(), Selectors.end(),
11811185
std::back_inserter(Input.ArgNames),
11821186
[](const char *C) { return StringRef(C); });

0 commit comments

Comments
 (0)