Skip to content

[5.3][IDE][SourceKit] Support escaped identifiers for the syntactic rename and related idents requests. #31598

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 27 additions & 5 deletions lib/IDE/Refactoring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,7 @@ class Renamer {
bool renameBase(CharSourceRange Range, RefactoringRangeKind RangeKind) {
assert(Range.isValid());

StringRef Existing = Range.str();
if (Existing != Old.base())
if (stripBackticks(Range).str() != Old.base())
return true;
doRenameBase(Range, RangeKind);
return false;
Expand Down Expand Up @@ -131,6 +130,27 @@ class Renamer {
bool isOperator() const { return Lexer::isOperator(Old.base()); }

private:

/// Returns the range of the (possibly escaped) identifier at the start of
/// \p Range and updates \p IsEscaped to indicate whether it's escaped or not.
CharSourceRange getLeadingIdentifierRange(CharSourceRange Range, bool &IsEscaped) {
assert(Range.isValid() && Range.getByteLength());
IsEscaped = Range.str().front() == '`';
SourceLoc Start = Range.getStart();
if (IsEscaped)
Start = Start.getAdvancedLoc(1);
return Lexer::getCharSourceRangeFromSourceRange(SM, Start);
}

CharSourceRange stripBackticks(CharSourceRange Range) {
StringRef Content = Range.str();
if (Content.size() < 3 || Content.front() != '`' || Content.back() != '`') {
return Range;
}
return CharSourceRange(Range.getStart().getAdvancedLoc(1),
Range.getByteLength() - 2);
}

void splitAndRenameLabel(CharSourceRange Range, LabelRangeType RangeType,
size_t NameIndex) {
switch (RangeType) {
Expand Down Expand Up @@ -218,13 +238,15 @@ class Renamer {

bool labelRangeMatches(CharSourceRange Range, LabelRangeType RangeType, StringRef Expected) {
if (Range.getByteLength()) {
CharSourceRange ExistingLabelRange =
Lexer::getCharSourceRangeFromSourceRange(SM, Range.getStart());
bool IsEscaped = false;
CharSourceRange ExistingLabelRange = getLeadingIdentifierRange(Range, IsEscaped);
StringRef ExistingLabel = ExistingLabelRange.str();
bool IsSingleName = Range == ExistingLabelRange ||
(IsEscaped && Range.getByteLength() == ExistingLabel.size() + 2);

switch (RangeType) {
case LabelRangeType::NoncollapsibleParam:
if (ExistingLabelRange == Range && Expected.empty()) // subscript([x]: Int)
if (IsSingleName && Expected.empty()) // subscript([x]: Int)
return true;
LLVM_FALLTHROUGH;
case LabelRangeType::CallArg:
Expand Down
12 changes: 10 additions & 2 deletions lib/IDE/SourceEntityWalker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,12 @@ bool SemaAnnotator::walkToDeclPre(Decl *D) {
bool IsExtension = false;

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

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

bool SemaAnnotator::
passReference(ValueDecl *D, Type Ty, DeclNameLoc Loc, ReferenceMetaData Data) {
return passReference(D, Ty, Loc.getBaseNameLoc(), Loc.getSourceRange(), Data);
SourceManager &SM = D->getASTContext().SourceMgr;
SourceLoc BaseStart = Loc.getBaseNameLoc(), BaseEnd = BaseStart;
if (SM.extractText({BaseStart, 1}) == "`")
BaseEnd = Lexer::getLocForEndOfToken(SM, BaseStart.getAdvancedLoc(1));
return passReference(D, Ty, BaseStart, {BaseStart, BaseEnd}, Data);
}

bool SemaAnnotator::
Expand Down
2 changes: 2 additions & 0 deletions lib/IDE/SwiftSourceDocInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,8 @@ static std::vector<CharSourceRange> getLabelRanges(const ParameterList* List,
} else {
NameLoc = ParamLoc;
NameLength = Param->getNameStr().size();
if (SM.extractText({NameLoc, 1}) == "`")
NameLength += 2;
LabelRanges.push_back(CharSourceRange(NameLoc, NameLength));
}
}
Expand Down
7 changes: 7 additions & 0 deletions test/Index/roles.swift
Original file line number Diff line number Diff line change
Expand Up @@ -531,3 +531,10 @@ func test<M>(_: M, value: Int?) {
// CHECK: [[@LINE-1]]:34 | constructor/Swift | init(_:) | s:14swift_ide_test7FromIntPyxSicfc | Ref,RelCont | rel: 1
}
}

func `escapedName`(`x`: Int) {}
// CHECK: [[@LINE-1]]:6 | function/Swift | escapedName(x:) | {{.*}} | Def | rel: 0
`escapedName`(`x`: 2)
// CHECK: [[@LINE-1]]:1 | function/Swift | escapedName(x:) | {{.*}} | Ref,Call | rel: 0
`escapedName`(`x`:)
// CHECK: [[@LINE-1]]:1 | function/Swift | escapedName(x:) | {{.*}} | Ref | rel: 0
7 changes: 6 additions & 1 deletion test/SourceKit/NameTranslation/swiftnames.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class C1 : NSObject {
}
func takeC1(a : Int, b: Int, c :Int) -> C1 {
let C1Ins = C1(a: a, b: b)
C1Ins.foo(a: a, b: b, c: c)
C1Ins.`foo`(`a`: a, `b`: b, c: c)
C1Ins.foo1(a: a, b, c: c)
C1Ins.foo2(a, b, c: c)
C1Ins.foo3()
Expand Down Expand Up @@ -57,11 +57,16 @@ class C3: NSObject {

// REQUIRES: objc_interop
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
// 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
Expand Down
10 changes: 10 additions & 0 deletions test/SourceKit/Refactoring/basic.swift
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,9 @@ struct HasInitWithDefaultArgs {
HasInitWithDefaultArgs(z: 45)
HasInitWithDefaultArgs(y: 45, z: 89)

func `hasBackticks`(`x`: Int) {}
`hasBackticks`(`x`:2)

// RUN: %sourcekitd-test -req=cursor -pos=3:1 -end-pos=5:13 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK1

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

// RUN: %sourcekitd-test -req=cursor -pos=116:6 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL
// RUN: %sourcekitd-test -req=cursor -pos=116:7 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL
// RUN: %sourcekitd-test -req=cursor -pos=117:1 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL
// RUN: %sourcekitd-test -req=cursor -pos=117:2 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL
// RUN: %sourcekitd-test -req=cursor -pos=117:16 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL
// RUN: %sourcekitd-test -req=cursor -pos=117:17 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-GLOBAL

// RUN: %sourcekitd-test -req=cursor -pos=35:10 -end-pos=35:16 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-RENAME-EXTRACT
// RUN: %sourcekitd-test -req=cursor -pos=35:10 -end-pos=35:16 -cursor-action %s -- %s | %FileCheck %s -check-prefix=CHECK-RENAME-EXTRACT

Expand Down
19 changes: 19 additions & 0 deletions test/SourceKit/RelatedIdents/related_idents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@ struct Projection<T> {
var item: T
}

func `escapedName`(`x`: Int) {}
`escapedName`(`x`: 2)
`escapedName`(`x`:)
escapedName(`x`: 2)
escapedName(`x`:)

// RUN: %sourcekitd-test -req=related-idents -pos=6:17 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK1 %s
// CHECK1: START RANGES
// CHECK1-NEXT: 1:7 - 2
Expand Down Expand Up @@ -146,3 +152,16 @@ struct Projection<T> {
// CHECK10-NEXT: 63:10 - 3
// CHECK10-NEXT: 64:10 - 3
// CHECK10-NEXT: END RANGES

// RUN: %sourcekitd-test -req=related-idents -pos=72:6 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK11 %s
// RUN: %sourcekitd-test -req=related-idents -pos=73:1 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK11 %s
// RUN: %sourcekitd-test -req=related-idents -pos=73:2 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK11 %s
// RUN: %sourcekitd-test -req=related-idents -pos=74:1 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK11 %s
// RUN: %sourcekitd-test -req=related-idents -pos=74:2 %s -- -module-name related_idents %s | %FileCheck -check-prefix=CHECK11 %s
// CHECK11: START RANGES
// CHECK11-NEXT: 72:6 - 13
// CHECK11-NEXT: 73:1 - 13
// CHECK11-NEXT: 74:1 - 13
// CHECK11-NEXT: 75:1 - 11
// CHECK11-NEXT: 76:1 - 11

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
struct Foo {
/*test:def*/<keywordBase>subscript</keywordBase>(<arglabel index=0>`x`</arglabel> <noncollapsibleparam index=0>y</noncollapsibleparam>: Int) -> Int { fatalError() }
}
Foo()/*test:ref*/[<callarg index=0>`x`</callarg><callcolon index=0>: </callcolon>10]

Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
struct Foo {
/*test:def*/<keywordBase>subscript</keywordBase>(<arglabel index=0></arglabel><noncollapsibleparam index=0>`x`</noncollapsibleparam>: Int) -> Int { fatalError() }
}
Foo()/*test:ref*/[<callcombo index=0></callcombo>10]

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
func /*test:def*/<base>`foo`</base>(<arglabel index=0>`x`</arglabel><param index=0> `y`</param>: Int) {}
/*test:call*/<base>`foo`</base>(<callarg index=0>`x`</callarg><callcolon index=0>: </callcolon>2)
_ = /*test:ref*/<base>`foo`</base>(<sel index=0>`x`</sel>:)

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
func /*test:def*/<base>`foo`</base>(<arglabel index=0>`x`</arglabel><param index=0></param>: Int) {}
/*test:call*/<base>`foo`</base>(<callarg index=0>`x`</callarg><callcolon index=0>: </callcolon>2)
_ = /*test:ref*/<base>`foo`</base>(<sel index=0>`x`</sel>:)

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
struct Foo {
/*test:def*/subscript(`x` y: Int) -> Int { fatalError() }
}
Foo()/*test:ref*/[`x`: 10]

// RUN: %empty-directory(%t.ranges)
// 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
// RUN: diff -u %S/FindRangeOutputs/backticks-noncollapsible-separate-arglabel.swift.expected %t.ranges/backticks-noncollapsible-separate-arglabel.swift.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
struct Foo {
/*test:def*/subscript(`x`: Int) -> Int { fatalError() }
}
Foo()/*test:ref*/[10]

// RUN: %empty-directory(%t.ranges)
// RUN: %refactor -find-rename-ranges -source-filename %s -pos="test" -is-function-like -old-name "subscript(_:)" >> %t.ranges/backticks-noncollapsible.swift.expected
// RUN: diff -u %S/FindRangeOutputs/backticks-noncollapsible.swift.expected %t.ranges/backticks-noncollapsible.swift.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
func /*test:def*/`foo`(`x` `y`: Int) {}
/*test:call*/`foo`(`x`: 2)
_ = /*test:ref*/`foo`(`x`:)

// RUN: %empty-directory(%t.ranges)
// RUN: %refactor -find-rename-ranges -source-filename %s -pos="test" -is-function-like -old-name "foo(x:)" >> %t.ranges/backticks-separate-arglabel.swift.expected
// RUN: diff -u %S/FindRangeOutputs/backticks-separate-arglabel.swift.expected %t.ranges/backticks-separate-arglabel.swift.expected
7 changes: 7 additions & 0 deletions test/refactoring/SyntacticRename/backticks.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
func /*test:def*/`foo`(`x`: Int) {}
/*test:call*/`foo`(`x`: 2)
_ = /*test:ref*/`foo`(`x`:)

// RUN: %empty-directory(%t.ranges)
// RUN: %refactor -find-rename-ranges -source-filename %s -pos="test" -is-function-like -old-name "foo(x:)" >> %t.ranges/backticks.swift.expected
// RUN: diff -u %S/FindRangeOutputs/backticks.swift.expected %t.ranges/backticks.swift.expected
8 changes: 6 additions & 2 deletions tools/SourceKit/tools/sourcekitd/lib/API/Requests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1164,7 +1164,11 @@ static void handleSemanticRequest(
else
return Rec(createErrorRequestInvalid("'key.namekind' is unrecognizable"));
if (auto Base = Req.getString(KeyBaseName)) {
Input.BaseName = Base.getValue();
if (Input.NameKind == UIDKindNameSwift) {
Input.BaseName = Base.getValue().trim('`');
} else {
Input.BaseName = Base.getValue();
}
}
llvm::SmallVector<const char*, 4> ArgParts;
llvm::SmallVector<const char*, 4> Selectors;
Expand All @@ -1176,7 +1180,7 @@ static void handleSemanticRequest(
}
std::transform(ArgParts.begin(), ArgParts.end(),
std::back_inserter(Input.ArgNames),
[](const char *C) { return StringRef(C); });
[](const char *C) { return StringRef(C).trim('`'); });
std::transform(Selectors.begin(), Selectors.end(),
std::back_inserter(Input.ArgNames),
[](const char *C) { return StringRef(C); });
Expand Down