Skip to content

Commit 055da1f

Browse files
authored
[SourceKit] Teach name translation request to translate Swift names to ObjC ones (by using PrintAsObjC). (#7449)
1 parent b1b9384 commit 055da1f

File tree

10 files changed

+209
-24
lines changed

10 files changed

+209
-24
lines changed

include/swift/AST/Decl.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4116,11 +4116,13 @@ class AbstractStorageDecl : public ValueDecl {
41164116

41174117
/// Given that this is an Objective-C property or subscript declaration,
41184118
/// produce its getter selector.
4119-
ObjCSelector getObjCGetterSelector(LazyResolver *resolver = nullptr) const;
4119+
ObjCSelector getObjCGetterSelector(LazyResolver *resolver = nullptr,
4120+
Identifier preferredName = Identifier()) const;
41204121

41214122
/// Given that this is an Objective-C property or subscript declaration,
41224123
/// produce its setter selector.
4123-
ObjCSelector getObjCSetterSelector(LazyResolver *resolver = nullptr) const;
4124+
ObjCSelector getObjCSetterSelector(LazyResolver *resolver = nullptr,
4125+
Identifier preferredName = Identifier()) const;
41244126

41254127
AbstractStorageDecl *getOverriddenDecl() const {
41264128
return OverriddenDecl;
@@ -4844,7 +4846,8 @@ class AbstractFunctionDecl : public ValueDecl, public DeclContext {
48444846
const CaptureInfo &getCaptureInfo() const { return Captures; }
48454847

48464848
/// Retrieve the Objective-C selector that names this method.
4847-
ObjCSelector getObjCSelector(LazyResolver *resolver = nullptr) const;
4849+
ObjCSelector getObjCSelector(LazyResolver *resolver = nullptr,
4850+
DeclName preferredName = DeclName()) const;
48484851

48494852
/// Determine whether the given method would produce an Objective-C
48504853
/// instance method.

include/swift/AST/Identifier.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,12 @@ class ObjCSelector {
433433
/// pieces.
434434
ObjCSelector(ASTContext &ctx, unsigned numArgs, ArrayRef<Identifier> pieces);
435435

436+
/// Construct an invalid ObjCSelector.
437+
ObjCSelector() : Storage() {};
438+
439+
/// Convert to true if the decl name is valid.
440+
explicit operator bool() const { return (bool)Storage; }
441+
436442
/// Determine the number of arguments in the selector.
437443
///
438444
/// When this is zero, the number of selector pieces will be one. Otherwise,

include/swift/PrintAsObjC/PrintAsObjC.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,26 @@
1515

1616
#include "swift/Basic/LLVM.h"
1717
#include "swift/AST/AttrKind.h"
18+
#include "swift/AST/Identifier.h"
1819

1920
namespace swift {
2021
class ModuleDecl;
22+
class ValueDecl;
2123

2224
/// Print the Objective-C-compatible declarations in a module as a Clang
2325
/// header.
2426
///
2527
/// Returns true on error.
2628
bool printAsObjC(raw_ostream &out, ModuleDecl *M, StringRef bridgingHeader,
2729
Accessibility minRequiredAccess);
30+
31+
/// Get the name for a value decl D if D is exported to ObjC, PreferredName is
32+
/// specified to perform what-if analysis, shadowing D's original name during
33+
/// computation.
34+
///
35+
/// Returns a pair of Identifier and ObjCSelector, only one of which is valid.
36+
std::pair<Identifier, ObjCSelector> getObjCNameForSwiftDecl(const ValueDecl *VD,
37+
DeclName PreferredName);
2838
}
2939

3040
#endif

lib/AST/Decl.cpp

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3406,7 +3406,7 @@ void AbstractStorageDecl::setInvalidBracesRange(SourceRange BracesRange) {
34063406
}
34073407

34083408
ObjCSelector AbstractStorageDecl::getObjCGetterSelector(
3409-
LazyResolver *resolver) const {
3409+
LazyResolver *resolver, Identifier preferredName) const {
34103410
// If the getter has an @objc attribute with a name, use that.
34113411
if (auto getter = getGetter()) {
34123412
if (auto objcAttr = getter->getAttrs().getAttribute<ObjCAttr>()) {
@@ -3430,11 +3430,16 @@ ObjCSelector AbstractStorageDecl::getObjCGetterSelector(
34303430

34313431
// The getter selector is the property name itself.
34323432
auto var = cast<VarDecl>(this);
3433-
return VarDecl::getDefaultObjCGetterSelector(ctx, var->getObjCPropertyName());
3433+
auto name = var->getObjCPropertyName();
3434+
3435+
// Use preferred name is specified.
3436+
if (!preferredName.empty())
3437+
name = preferredName;
3438+
return VarDecl::getDefaultObjCGetterSelector(ctx, name);
34343439
}
34353440

34363441
ObjCSelector AbstractStorageDecl::getObjCSetterSelector(
3437-
LazyResolver *resolver) const {
3442+
LazyResolver *resolver, Identifier preferredName) const {
34383443
// If the setter has an @objc attribute with a name, use that.
34393444
auto setter = getSetter();
34403445
auto objcAttr = setter ? setter->getAttrs().getAttribute<ObjCAttr>()
@@ -3464,9 +3469,10 @@ ObjCSelector AbstractStorageDecl::getObjCSetterSelector(
34643469
// The setter selector for, e.g., 'fooBar' is 'setFooBar:', with the
34653470
// property name capitalized and preceded by 'set'.
34663471
auto var = cast<VarDecl>(this);
3467-
auto result = VarDecl::getDefaultObjCSetterSelector(
3468-
ctx,
3469-
var->getObjCPropertyName());
3472+
Identifier Name = var->getObjCPropertyName();
3473+
if (!preferredName.empty())
3474+
Name = preferredName;
3475+
auto result = VarDecl::getDefaultObjCSetterSelector(ctx, Name);
34703476

34713477
// Cache the result, so we don't perform string manipulation again.
34723478
if (objcAttr)
@@ -4320,7 +4326,7 @@ SourceRange AbstractFunctionDecl::getSignatureSourceRange() const {
43204326
}
43214327

43224328
ObjCSelector AbstractFunctionDecl::getObjCSelector(
4323-
LazyResolver *resolver) const {
4329+
LazyResolver *resolver, DeclName preferredName) const {
43244330
// If there is an @objc attribute with a name, use that name.
43254331
auto objc = getAttrs().getAttribute<ObjCAttr>();
43264332
if (objc) {
@@ -4329,15 +4335,27 @@ ObjCSelector AbstractFunctionDecl::getObjCSelector(
43294335
}
43304336

43314337
auto &ctx = getASTContext();
4338+
auto baseName = getName();
43324339
auto argNames = getFullName().getArgumentNames();
43334340

4341+
// Use the preferred name if specified
4342+
if (preferredName) {
4343+
// Return invalid selector if argument count doesn't match.
4344+
if (argNames.size() != preferredName.getArgumentNames().size()) {
4345+
return ObjCSelector();
4346+
}
4347+
baseName = preferredName.getBaseName();
4348+
argNames = preferredName.getArgumentNames();
4349+
}
4350+
43344351
auto func = dyn_cast<FuncDecl>(this);
43354352
if (func) {
43364353
// For a getter or setter, go through the variable or subscript decl.
43374354
if (func->isGetterOrSetter()) {
43384355
auto asd = cast<AbstractStorageDecl>(func->getAccessorStorageDecl());
4339-
return func->isGetter() ? asd->getObjCGetterSelector(resolver)
4340-
: asd->getObjCSetterSelector(resolver);
4356+
return func->isGetter() ?
4357+
asd->getObjCGetterSelector(resolver, baseName) :
4358+
asd->getObjCSetterSelector(resolver, baseName);
43414359
}
43424360
}
43434361

@@ -4373,12 +4391,12 @@ ObjCSelector AbstractFunctionDecl::getObjCSelector(
43734391

43744392
// If we have no arguments, it's a nullary selector.
43754393
if (numSelectorPieces == 0) {
4376-
return ObjCSelector(ctx, 0, getName());
4394+
return ObjCSelector(ctx, 0, baseName);
43774395
}
43784396

43794397
// If it's a unary selector with no name for the first argument, we're done.
43804398
if (numSelectorPieces == 1 && argNames.size() == 1 && argNames[0].empty()) {
4381-
return ObjCSelector(ctx, 1, getName());
4399+
return ObjCSelector(ctx, 1, baseName);
43824400
}
43834401

43844402
/// Collect the selector pieces.
@@ -4403,7 +4421,7 @@ ObjCSelector AbstractFunctionDecl::getObjCSelector(
44034421

44044422
// For the first selector piece, attach either the first parameter
44054423
// or "AndReturnError" to the base name, if appropriate.
4406-
auto firstPiece = getName();
4424+
auto firstPiece = baseName;
44074425
llvm::SmallString<32> scratch;
44084426
scratch += firstPiece.str();
44094427
if (errorConvention && piece == errorConvention->getErrorParameterIndex()) {

lib/PrintAsObjC/PrintAsObjC.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2633,3 +2633,34 @@ bool swift::printAsObjC(llvm::raw_ostream &os, ModuleDecl *M,
26332633
llvm::PrettyStackTraceString trace("While generating Objective-C header");
26342634
return ModuleWriter(*M, bridgingHeader, minRequiredAccess).writeToStream(os);
26352635
}
2636+
2637+
std::pair<Identifier, ObjCSelector> swift::
2638+
getObjCNameForSwiftDecl(const ValueDecl *VD, DeclName PreferredName){
2639+
ASTContext &Ctx = VD->getASTContext();
2640+
LazyResolver *Resolver = Ctx.getLazyResolver();
2641+
if (auto *FD = dyn_cast<AbstractFunctionDecl>(VD)) {
2642+
return {Identifier(), FD->getObjCSelector(Resolver, PreferredName)};
2643+
} else if (auto *VAD = dyn_cast<VarDecl>(VD)) {
2644+
if (PreferredName)
2645+
return {PreferredName.getBaseName(), ObjCSelector()};
2646+
return {VAD->getObjCPropertyName(), ObjCSelector()};
2647+
} else if (auto *SD = dyn_cast<SubscriptDecl>(VD)) {
2648+
return getObjCNameForSwiftDecl(SD->getGetter(), PreferredName);
2649+
} else if (auto *EL = dyn_cast<EnumElementDecl>(VD)) {
2650+
EnumDecl* ED = EL->getDeclContext()->getAsEnumOrEnumExtensionContext();
2651+
SmallString<64> Buffer;
2652+
{
2653+
llvm::raw_svector_ostream OS(Buffer);
2654+
OS << getNameForObjC(ED).str();
2655+
SmallString<64> Scratch;
2656+
OS << camel_case::toSentencecase(PreferredName.getBaseName().str(),
2657+
Scratch);
2658+
}
2659+
return {Ctx.getIdentifier(Buffer.str()), ObjCSelector()};
2660+
} else {
2661+
auto Name = getNameForObjC(VD, CustomNamesOnly);
2662+
if (!Name.empty())
2663+
return {Ctx.getIdentifier(Name), ObjCSelector()};
2664+
return {PreferredName.getBaseName(), ObjCSelector()};
2665+
}
2666+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
class C1 : NSObject {
2+
func foo(a : Int, b: Int, c : Int) {}
3+
func foo1(a : Int, _: Int, c : Int) {}
4+
func foo2(_ : Int, _: Int, c : Int) {}
5+
func foo3() {}
6+
init(a : Int, b :Int) {}
7+
init(_ : Int) {}
8+
}
9+
func takeC1(a : Int, b: Int, c :Int) -> C1 {
10+
let C1Ins = C1(a: a, b: b)
11+
C1Ins.foo(a: a, b: b, c: c)
12+
C1Ins.foo1(a: a, b, c: c)
13+
C1Ins.foo2(a, b, c: c)
14+
C1Ins.foo3()
15+
let C1Ins2 = C1(a)
16+
return C1Ins2
17+
}
18+
19+
@objc public enum CommonFix : Int {
20+
case A1
21+
case B1
22+
case C1
23+
}
24+
25+
func takeCommonFix(_ f: CommonFix) -> Int {
26+
switch(f) {
27+
case .A1: break
28+
case .B1: break
29+
case .C1: break
30+
return 1
31+
}
32+
return 0
33+
}
34+
35+
@objc public enum CustomError: Int, Error {
36+
case a1, b1
37+
}
38+
39+
func takeError(_ e : CustomError) {
40+
switch(e) {
41+
case .a1: break
42+
case .b1: break
43+
return
44+
}
45+
}
46+
47+
// REQUIRES: objc_interop
48+
// RUN: %sourcekitd-test -req=translate -swift-name "foo(a:b:c:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK1 %s
49+
// RUN: %sourcekitd-test -req=translate -swift-name "foo(a1:b1:c1:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK2 %s
50+
// RUN: %sourcekitd-test -req=translate -swift-name "foo(_:b1:c1:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK3 %s
51+
// RUN: %sourcekitd-test -req=translate -swift-name "foo1(_:_:c2:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK4 %s
52+
// RUN: %sourcekitd-test -req=translate -swift-name "foo1(_:_:_:)" -pos=11:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK5 %s
53+
// RUN: %sourcekitd-test -req=translate -swift-name "foo2(a:b:c:)" -pos=12:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK6 %s
54+
// RUN: %sourcekitd-test -req=translate -swift-name "foo2(_:_:_:)" -pos=12:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK7 %s
55+
// RUN: %sourcekitd-test -req=translate -swift-name "foo1()" -pos=14:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK8 %s
56+
// RUN: %sourcekitd-test -req=translate -swift-name "foo1(a:b:c:)" -pos=14:11 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK-NONE %s
57+
// RUN: %sourcekitd-test -req=translate -swift-name "C11" -pos=1:8 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK9 %s
58+
59+
// RUN: %sourcekitd-test -req=translate -swift-name "init(a1:b2:)" -pos=10:16 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK10 %s
60+
// RUN: %sourcekitd-test -req=translate -swift-name "init(_:_:)" -pos=10:16 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK11 %s
61+
// RUN: %sourcekitd-test -req=translate -swift-name "foo(a1:_:)" -pos=10:16 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK12 %s
62+
63+
// RUN: %sourcekitd-test -req=translate -swift-name "A2" -pos=27:10 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK13 %s
64+
// RUN: %sourcekitd-test -req=translate -swift-name "a2" -pos=27:10 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK13 %s
65+
// RUN: %sourcekitd-test -req=translate -swift-name "a2" -pos=41:10 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK14 %s
66+
// RUN: %sourcekitd-test -req=translate -swift-name "A2" -pos=41:10 %s -- -F %S/Inputs/mock-sdk -I %t.tmp %mcp_opt %s | %FileCheck -check-prefix=CHECK14 %s
67+
68+
// CHECK-NONE: <empty name translation info>
69+
// CHECK1: fooWithA:b:c:
70+
// CHECK2: fooWithA1:b1:c1:
71+
// CHECK3: foo:b1:c1:
72+
// CHECK4: foo1::c2:
73+
// CHECK5: foo1:::
74+
// CHECK6: foo2WithA:b:c:
75+
// CHECK7: foo2:::
76+
// CHECK8: foo1
77+
// CHECK9: C11
78+
79+
// CHECK10: initWithA1:b2:
80+
// CHECK11: init::
81+
// CHECK12: fooWithA1::
82+
// CHECK13: CommonFixA2
83+
// CHECK14: CustomErrorA2

tools/SourceKit/lib/SwiftLang/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ add_sourcekit_library(SourceKitSwiftLang
1010
SwiftSourceDocInfo.cpp
1111
DEPENDS SourceKitCore swiftDriver swiftFrontend swiftClangImporter swiftIndex swiftIDE
1212
swiftAST swiftMarkup swiftParse swiftSIL swiftSILGen swiftSILOptimizer
13-
swiftIRGen swiftSema swiftBasic swiftSerialization
13+
swiftIRGen swiftSema swiftBasic swiftSerialization swiftPrintAsObjC
1414
swiftOption libcmark_static
1515
# Clang dependencies.
1616
clangIndex

tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include "swift/IDE/ModuleInterfacePrinting.h"
2828
#include "swift/IDE/Utils.h"
2929
#include "swift/Markup/XMLUtils.h"
30+
#include "swift/PrintAsObjC/PrintAsObjC.h"
3031
#include "swift/Sema/IDETypeChecking.h"
3132

3233
#include "clang/AST/ASTContext.h"
@@ -877,14 +878,47 @@ getClangDeclarationName(clang::ASTContext &Ctx, NameTranslatingInfo &Info) {
877878
}
878879
}
879880

881+
static DeclName
882+
getSwiftDeclName(ASTContext &Ctx, NameTranslatingInfo &Info) {
883+
assert(SwiftLangSupport::getNameKindForUID(Info.NameKind) == NameKind::Swift);
884+
std::vector<Identifier> Args(Info.ArgNames.size(), Identifier());
885+
std::transform(Info.ArgNames.begin(), Info.ArgNames.end(), Args.begin(),
886+
[&](StringRef T) { return Ctx.getIdentifier(T); });
887+
return DeclName(Ctx, Ctx.getIdentifier(Info.BaseName),
888+
llvm::makeArrayRef(Args));
889+
}
890+
880891
/// Returns true for failure to resolve.
881892
static bool passNameInfoForDecl(const ValueDecl *VD, NameTranslatingInfo &Info,
882893
std::function<void(const NameTranslatingInfo &)> Receiver) {
883894
switch (SwiftLangSupport::getNameKindForUID(Info.NameKind)) {
884895
case NameKind::Swift: {
885-
886-
// FIXME: Implement the swift to objc name translation.
887-
return true;
896+
NameTranslatingInfo Result;
897+
auto &Ctx = VD->getDeclContext()->getASTContext();
898+
auto ResultPair = getObjCNameForSwiftDecl(VD, getSwiftDeclName(Ctx, Info));
899+
Identifier Name = ResultPair.first;
900+
if (!Name.empty()) {
901+
Result.NameKind = SwiftLangSupport::getUIDForNameKind(NameKind::ObjC);
902+
Result.BaseName = Name.str();
903+
} else if (ObjCSelector Selector = ResultPair.second) {
904+
Result.NameKind = SwiftLangSupport::getUIDForNameKind(NameKind::ObjC);
905+
SmallString<64> Buffer;
906+
StringRef Total = Selector.getString(Buffer);
907+
SmallVector<StringRef, 4> Pieces;
908+
Total.split(Pieces, ":");
909+
if (Selector.getNumArgs()) {
910+
assert(Pieces.back().empty());
911+
Pieces.pop_back();
912+
std::transform(Pieces.begin(), Pieces.end(), Pieces.begin(),
913+
[](StringRef P) { return StringRef(P.data(), P.size() + 1); });
914+
}
915+
Result.ArgNames = llvm::makeArrayRef(Pieces);
916+
} else {
917+
Receiver(Result);
918+
return true;
919+
}
920+
Receiver(Result);
921+
return false;
888922
}
889923
case NameKind::ObjC: {
890924
ClangImporter *Importer = static_cast<ClangImporter *>(VD->getDeclContext()->

tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -593,7 +593,7 @@ static int handleTestInvocation(ArrayRef<const char *> Args,
593593
} else {
594594
BaseName = Text.substr(0, ArgStart);
595595
auto ArgEnd = Text.find_last_of(')');
596-
if (ArgEnd != StringRef::npos) {
596+
if (ArgEnd == StringRef::npos) {
597597
llvm::errs() << "Swift name is malformed.\n";
598598
return 1;
599599
}
@@ -1160,9 +1160,6 @@ static void printNameTranslationInfo(sourcekitd_variant_t Info,
11601160
}
11611161
for (auto S : Selectors) {
11621162
OS << S;
1163-
if (S != Selectors.back()) {
1164-
OS << ":";
1165-
}
11661163
}
11671164
OS << '\n';
11681165
}

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -801,7 +801,10 @@ handleSemanticRequest(RequestDict Req,
801801
}
802802
Input.ArgNames.resize(ArgParts.size() + Selectors.size());
803803
std::transform(ArgParts.begin(), ArgParts.end(), Input.ArgNames.begin(),
804-
[](const char *C) { return StringRef(C); });
804+
[](const char *C) {
805+
StringRef Original(C);
806+
return Original == "_" ? StringRef() : Original;
807+
});
805808
std::transform(Selectors.begin(), Selectors.end(), Input.ArgNames.begin(),
806809
[](const char *C) { return StringRef(C); });
807810
return Lang.getNameInfo(*SourceFile, Offset, Input, Args,

0 commit comments

Comments
 (0)