Skip to content

Commit 1b3c4ad

Browse files
authored
Merge pull request #23372 from rintaro/ide-completion-escapedident-16232627
[CodeCompletion] Escape declaration base name if needed
2 parents 25e0a9f + df473f6 commit 1b3c4ad

File tree

3 files changed

+134
-13
lines changed

3 files changed

+134
-13
lines changed

lib/IDE/CodeCompletion.cpp

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1824,6 +1824,32 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
18241824
llvm_unreachable("unhandled kind");
18251825
}
18261826

1827+
void addValueBaseName(CodeCompletionResultBuilder &Builder,
1828+
DeclBaseName Name) {
1829+
auto NameStr = Name.userFacingName();
1830+
bool shouldEscapeKeywords;
1831+
if (Name.isSpecial()) {
1832+
// Special names (i.e. 'init') are always displayed as its user facing
1833+
// name.
1834+
shouldEscapeKeywords = false;
1835+
} else if (ExprType) {
1836+
// After dot. User can write any keyword after '.' except for `init` and
1837+
// `self`. E.g. 'func `init`()' must be called by 'expr.`init`()'.
1838+
shouldEscapeKeywords = NameStr == "self" || NameStr == "init";
1839+
} else {
1840+
// As primary expresson. We have to escape almost every keywords except
1841+
// for 'self' and 'Self'.
1842+
shouldEscapeKeywords = NameStr != "self" && NameStr != "Self";
1843+
}
1844+
1845+
if (!shouldEscapeKeywords) {
1846+
Builder.addTextChunk(NameStr);
1847+
} else {
1848+
SmallString<16> buffer;
1849+
Builder.addTextChunk(Builder.escapeKeyword(NameStr, true, buffer));
1850+
}
1851+
}
1852+
18271853
void addLeadingDot(CodeCompletionResultBuilder &Builder) {
18281854
if (NeedOptionalUnwrap) {
18291855
Builder.setNumBytesToErase(NumBytesToEraseForOptionalUnwrap);
@@ -1996,7 +2022,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
19962022
VD->shouldHideFromEditor())
19972023
return;
19982024

1999-
StringRef Name = VD->getName().get();
2025+
Identifier Name = VD->getName();
20002026
assert(!Name.empty() && "name should not be empty");
20012027

20022028
CommandWordsPairs Pairs;
@@ -2006,15 +2032,15 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
20062032
getSemanticContext(VD, Reason), ExpectedTypes);
20072033
Builder.setAssociatedDecl(VD);
20082034
addLeadingDot(Builder);
2009-
Builder.addTextChunk(Name);
2035+
addValueBaseName(Builder, Name);
20102036
setClangDeclKeywords(VD, Pairs, Builder);
20112037

20122038
if (!VD->hasValidSignature())
20132039
return;
20142040

20152041
// Add a type annotation.
20162042
Type VarType = getTypeOfMember(VD);
2017-
if (VD->getName() != Ctx.Id_self && VD->isInOut()) {
2043+
if (Name != Ctx.Id_self && VD->isInOut()) {
20182044
// It is useful to show inout for function parameters.
20192045
// But for 'self' it is just noise.
20202046
VarType = InOutType::get(VarType);
@@ -2320,7 +2346,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
23202346
return;
23212347
foundFunction(FD);
23222348

2323-
StringRef Name = FD->getName().get();
2349+
Identifier Name = FD->getName();
23242350
assert(!Name.empty() && "name should not be empty");
23252351

23262352
Type FunctionType = getTypeOfMember(FD);
@@ -2351,7 +2377,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
23512377
setClangDeclKeywords(FD, Pairs, Builder);
23522378
Builder.setAssociatedDecl(FD);
23532379
addLeadingDot(Builder);
2354-
Builder.addTextChunk(Name);
2380+
addValueBaseName(Builder, Name);
23552381
if (IsDynamicLookup)
23562382
Builder.addDynamicLookupMethodCallTail();
23572383
else if (FD->getAttrs().hasAttribute<OptionalAttr>())
@@ -2681,8 +2707,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
26812707
Builder.setAssociatedDecl(EED);
26822708
setClangDeclKeywords(EED, Pairs, Builder);
26832709
addLeadingDot(Builder);
2684-
2685-
Builder.addTextChunk(EED->getName().str());
2710+
addValueBaseName(Builder, EED->getName());
26862711

26872712
// Enum element is of function type; (Self.type) -> Self or
26882713
// (Self.Type) -> (Args...) -> Self.
@@ -2760,7 +2785,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
27602785

27612786
// Base name
27622787
addLeadingDot(Builder);
2763-
Builder.addTextChunk(AFD->getBaseName().userFacingName());
2788+
addValueBaseName(Builder, AFD->getBaseName());
27642789

27652790
// Add the argument labels.
27662791
auto ArgLabels = AFD->getFullName().getArgumentNames();

lib/IDE/CodeCompletionResultBuilder.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -267,9 +267,8 @@ class CodeCompletionResultBuilder {
267267
addTypeAnnotation(Annotation);
268268
}
269269

270-
StringRef escapeArgumentLabel(StringRef Word,
271-
bool escapeAllKeywords,
272-
llvm::SmallString<16> &EscapedKeyword) {
270+
StringRef escapeKeyword(StringRef Word, bool escapeAllKeywords,
271+
llvm::SmallString<16> &EscapedKeyword) {
273272
bool shouldEscape = false;
274273
if (escapeAllKeywords) {
275274
#define KEYWORD(kw) .Case(#kw, true)
@@ -325,15 +324,15 @@ class CodeCompletionResultBuilder {
325324
llvm::SmallString<16> EscapedKeyword;
326325
addChunkWithText(
327326
CodeCompletionString::Chunk::ChunkKind::CallParameterName,
328-
escapeArgumentLabel(Name.str(), false, EscapedKeyword));
327+
escapeKeyword(Name.str(), false, EscapedKeyword));
329328
addChunkWithTextNoCopy(
330329
CodeCompletionString::Chunk::ChunkKind::CallParameterColon, ": ");
331330
} else if (!LocalName.empty()) {
332331
// Use local (non-API) parameter name if we have nothing else.
333332
llvm::SmallString<16> EscapedKeyword;
334333
addChunkWithText(
335334
CodeCompletionString::Chunk::ChunkKind::CallParameterInternalName,
336-
escapeArgumentLabel(LocalName.str(), false, EscapedKeyword));
335+
escapeKeyword(LocalName.str(), false, EscapedKeyword));
337336
addChunkWithTextNoCopy(
338337
CodeCompletionString::Chunk::ChunkKind::CallParameterColon, ": ");
339338
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STATIC_PRIMARY | %FileCheck %s -check-prefix=STATIC_PRIMARY
2+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STATIC_SELF_NODOT | %FileCheck %s -check-prefix=STATIC_SELF_NODOT
3+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=STATIC_SELF_DOT | %FileCheck %s -check-prefix=STATIC_SELF_DOT
4+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=META_NODOT | %FileCheck %s -check-prefix=STATIC_SELF_NODOT
5+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=META_DOT | %FileCheck %s -check-prefix=STATIC_SELF_DOT
6+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INSTANCE_PRIMARY | %FileCheck %s -check-prefix=INSTANCE_PRIMARY
7+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INSTANCE_SELF_NODOT | %FileCheck %s -check-prefix=INSTANCE_SELF_NODOT
8+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=INSTANCE_SELF_DOT | %FileCheck %s -check-prefix=INSTANCE_SELF_DOT
9+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=VALUE_NODOT | %FileCheck %s -check-prefix=INSTANCE_SELF_NODOT
10+
// RUN: %target-swift-ide-test -code-completion -source-filename %s -code-completion-token=VALUE_DOT | %FileCheck %s -check-prefix=INSTANCE_SELF_DOT
11+
12+
13+
enum MyEnum {
14+
case `class`(struct: String)
15+
case `let`(`var`: String)
16+
17+
init(`init`: String) {}
18+
static func `public`(private: String) -> Int {}
19+
20+
func `init`(deinit: String) -> Int {}
21+
func `if`(else: String) -> Int {}
22+
23+
var `self`: Int { return 0 }
24+
25+
static func testStatic(meta: MyEnum.Type) {
26+
let _ = #^STATIC_PRIMARY^#
27+
// STATIC_PRIMARY: Begin completion
28+
// STATIC_PRIMARY-DAG: Decl[LocalVar]/Local: self[#MyEnum.Type#]; name=self
29+
// STATIC_PRIMARY-DAG: Decl[EnumElement]/CurrNominal: `class`({#struct: String#})[#MyEnum#]; name=`class`(struct: String)
30+
// STATIC_PRIMARY-DAG: Decl[EnumElement]/CurrNominal: `let`({#`var`: String#})[#MyEnum#]; name=`let`(`var`: String)
31+
// STATIC_PRIMARY-DAG: Decl[StaticMethod]/CurrNominal: `public`({#private: String#})[#Int#]; name=`public`(private: String)
32+
// STATIC_PRIMARY-DAG: Decl[InstanceMethod]/CurrNominal: `init`({#(self): MyEnum#})[#(deinit: String) -> Int#]; name=`init`(self: MyEnum)
33+
// STATIC_PRIMARY-DAG: Decl[InstanceMethod]/CurrNominal: `if`({#(self): MyEnum#})[#(else: String) -> Int#]; name=`if`(self: MyEnum)
34+
// STATIC_PRIMARY: End completion
35+
36+
let _ = self#^STATIC_SELF_NODOT^#
37+
// STATIC_SELF_NODOT: Begin completions
38+
// STATIC_SELF_NODOT-DAG: Keyword[self]/CurrNominal: .self[#MyEnum.Type#]; name=self
39+
// STATIC_SELF_NODOT-DAG: Decl[EnumElement]/CurrNominal: .class({#struct: String#})[#MyEnum#]; name=class(struct: String)
40+
// STATIC_SELF_NODOT-DAG: Decl[EnumElement]/CurrNominal: .let({#`var`: String#})[#MyEnum#]; name=let(`var`: String)
41+
// STATIC_SELF_NODOT-DAG: Decl[Constructor]/CurrNominal: .init({#init: String#})[#MyEnum#]; name=init(init: String)
42+
// STATIC_SELF_NODOT-DAG: Decl[StaticMethod]/CurrNominal: .public({#private: String#})[#Int#]; name=public(private: String)
43+
// STATIC_SELF_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .`init`({#(self): MyEnum#})[#(deinit: String) -> Int#]; name=`init`(self: MyEnum)
44+
// STATIC_SELF_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .if({#(self): MyEnum#})[#(else: String) -> Int#]; name=if(self: MyEnum)
45+
// STATIC_SELF_NODOT: End completion
46+
47+
let _ = self.#^STATIC_SELF_DOT^#
48+
// STATIC_SELF_DOT: Begin completions
49+
// STATIC_SELF_DOT-DAG: Keyword[self]/CurrNominal: self[#MyEnum.Type#]; name=self
50+
// STATIC_SELF_DOT-DAG: Decl[EnumElement]/CurrNominal: class({#struct: String#})[#MyEnum#]; name=class(struct: String)
51+
// STATIC_SELF_DOT-DAG: Decl[EnumElement]/CurrNominal: let({#`var`: String#})[#MyEnum#]; name=let(`var`: String)
52+
// STATIC_SELF_DOT-DAG: Decl[Constructor]/CurrNominal: init({#init: String#})[#MyEnum#]; name=init(init: String)
53+
// STATIC_SELF_DOT-DAG: Decl[StaticMethod]/CurrNominal: public({#private: String#})[#Int#]; name=public(private: String)
54+
// STATIC_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: `init`({#(self): MyEnum#})[#(deinit: String) -> Int#]; name=`init`(self: MyEnum)
55+
// STATIC_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: if({#(self): MyEnum#})[#(else: String) -> Int#]; name=if(self: MyEnum)
56+
// STATIC_SELF_DOT: End completion
57+
58+
let _ = meta#^META_NODOT^#
59+
// SAME AS 'STATIC_SELF_NODOT'.
60+
61+
let _ = meta.#^META_DOT^#
62+
// SAME AS 'STATIC_SELF_DOT'.
63+
}
64+
65+
func testInstance(val: MyEnum) {
66+
let _ = #^INSTANCE_PRIMARY^#
67+
// INSTANCE_PRIMARY: Begin completion
68+
// INSTANCE_PRIMARY-DAG: Decl[LocalVar]/Local: self[#MyEnum#]; name=self
69+
// INSTANCE_PRIMARY-DAG: Decl[InstanceVar]/CurrNominal: self[#Int#]; name=self
70+
// FIXME: ^ This is shadowed. We should hide this.
71+
// INSTANCE_PRIMARY-DAG: Decl[InstanceMethod]/CurrNominal: `init`({#deinit: String#})[#Int#]; name=`init`(deinit: String)
72+
// INSTANCE_PRIMARY-DAG: Decl[InstanceMethod]/CurrNominal: `if`({#else: String#})[#Int#]; name=`if`(else: String)
73+
// INSTANCE_PRIMARY: End completion
74+
75+
let _ = self#^INSTANCE_SELF_NODOT^#
76+
// INSTANCE_SELF_NODOT: Begin completions
77+
// INSTANCE_SELF_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .`init`({#deinit: String#})[#Int#]; name=`init`(deinit: String)
78+
// INSTANCE_SELF_NODOT-DAG: Decl[InstanceMethod]/CurrNominal: .if({#else: String#})[#Int#]; name=if(else: String)
79+
// INSTANCE_SELF_NODOT-DAG: Decl[InstanceVar]/CurrNominal: .`self`[#Int#]; name=`self`
80+
// INSTANCE_SELF_NODOT-DAG: Keyword[self]/CurrNominal: .self[#MyEnum#]; name=self
81+
82+
// INSTANCE_SELF_NODOT: End completions
83+
84+
let _ = self.#^INSTANCE_SELF_DOT^#
85+
// INSTANCE_SELF_DOT: Begin completions
86+
// INSTANCE_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: `init`({#deinit: String#})[#Int#]; name=`init`(deinit: String)
87+
// INSTANCE_SELF_DOT-DAG: Decl[InstanceMethod]/CurrNominal: if({#else: String#})[#Int#]; name=if(else: String)
88+
// INSTANCE_SELF_DOT-DAG: Decl[InstanceVar]/CurrNominal: `self`[#Int#]; name=`self`
89+
// INSTANCE_SELF_DOT-DAG: Keyword[self]/CurrNominal: self[#MyEnum#]; name=self
90+
// INSTANCE_SELF_DOT: End completions
91+
92+
let _ = val#^VALUE_NODOT^#
93+
// SAME AS 'INSTANCE_SELF_NODOT'.
94+
let _ = val.#^VALUE_DOT^#
95+
// SAME AS 'INSTANCE_SELF_DOT'.
96+
}
97+
}

0 commit comments

Comments
 (0)