-
Notifications
You must be signed in to change notification settings - Fork 10.5k
[WIP] Teach SourceKit name translation request to translate Swift names to ObjC ones (by using PrintAsObjC). #7449
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
Changes from all commits
7ffe5f6
4a0f0fe
b1b8c7f
02802e9
a044b65
3924d9b
19204da
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2633,3 +2633,34 @@ bool swift::printAsObjC(llvm::raw_ostream &os, ModuleDecl *M, | |
llvm::PrettyStackTraceString trace("While generating Objective-C header"); | ||
return ModuleWriter(*M, bridgingHeader, minRequiredAccess).writeToStream(os); | ||
} | ||
|
||
std::pair<Identifier, ObjCSelector> swift:: | ||
getObjCNameForSwiftDecl(const ValueDecl *VD, DeclName PreferredName){ | ||
ASTContext &Ctx = VD->getASTContext(); | ||
LazyResolver *Resolver = Ctx.getLazyResolver(); | ||
if (auto *FD = dyn_cast<AbstractFunctionDecl>(VD)) { | ||
return {Identifier(), FD->getObjCSelector(Resolver, PreferredName)}; | ||
} else if (auto *VAD = dyn_cast<VarDecl>(VD)) { | ||
if (PreferredName) | ||
return {PreferredName.getBaseName(), ObjCSelector()}; | ||
return {VAD->getObjCPropertyName(), ObjCSelector()}; | ||
} else if (auto *SD = dyn_cast<SubscriptDecl>(VD)) { | ||
return getObjCNameForSwiftDecl(SD->getGetter(), PreferredName); | ||
} else if (auto *EL = dyn_cast<EnumElementDecl>(VD)) { | ||
EnumDecl* ED = EL->getDeclContext()->getAsEnumOrEnumExtensionContext(); | ||
SmallString<64> Buffer; | ||
{ | ||
llvm::raw_svector_ostream OS(Buffer); | ||
OS << getNameForObjC(ED).str(); | ||
SmallString<64> Scratch; | ||
OS << camel_case::toSentencecase(PreferredName.getBaseName().str(), | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you abstract this out so that the actual printing code can use this too? |
||
Scratch); | ||
} | ||
return {Ctx.getIdentifier(Buffer.str()), ObjCSelector()}; | ||
} else { | ||
auto Name = getNameForObjC(VD, CustomNamesOnly); | ||
if (!Name.empty()) | ||
return {Ctx.getIdentifier(Name), ObjCSelector()}; | ||
return {PreferredName.getBaseName(), ObjCSelector()}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hang on, what if PreferredName is also null? (Classes and protocols would currently fall into this case.) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We get an empty identifier then. I think it is fine. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Isn't it supposed to return the current name in that case? |
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
class C1 : NSObject { | ||
func foo(a : Int, b: Int, c : Int) {} | ||
func foo1(a : Int, _: Int, c : Int) {} | ||
func foo2(_ : Int, _: Int, c : Int) {} | ||
func foo3() {} | ||
init(a : Int, b :Int) {} | ||
init(_ : Int) {} | ||
} | ||
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.foo1(a: a, b, c: c) | ||
C1Ins.foo2(a, b, c: c) | ||
C1Ins.foo3() | ||
let C1Ins2 = C1(a) | ||
return C1Ins2 | ||
} | ||
|
||
@objc public enum CommonFix : Int { | ||
case A1 | ||
case B1 | ||
case C1 | ||
} | ||
|
||
func takeCommonFix(_ f: CommonFix) -> Int { | ||
switch(f) { | ||
case .A1: break | ||
case .B1: break | ||
case .C1: break | ||
return 1 | ||
} | ||
return 0 | ||
} | ||
|
||
@objc public enum CustomError: Int, Error { | ||
case a1, b1 | ||
} | ||
|
||
func takeError(_ e : CustomError) { | ||
switch(e) { | ||
case .a1: break | ||
case .b1: break | ||
return | ||
} | ||
} | ||
|
||
// 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 %mcp_opt %s | %FileCheck -check-prefix=CHECK1 %s | ||
// 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 | ||
// 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 | ||
// 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 | ||
// 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 | ||
// 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 | ||
// 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 | ||
// 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 | ||
// 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 | ||
// 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 | ||
|
||
// 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 | ||
// 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 | ||
// 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 | ||
|
||
// 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 | ||
// 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 | ||
// 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 | ||
// 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 | ||
|
||
// CHECK-NONE: <empty name translation info> | ||
// CHECK1: fooWithA:b:c: | ||
// CHECK2: fooWithA1:b1:c1: | ||
// CHECK3: foo:b1:c1: | ||
// CHECK4: foo1::c2: | ||
// CHECK5: foo1::: | ||
// CHECK6: foo2WithA:b:c: | ||
// CHECK7: foo2::: | ||
// CHECK8: foo1 | ||
// CHECK9: C11 | ||
|
||
// CHECK10: initWithA1:b2: | ||
// CHECK11: init:: | ||
// CHECK12: fooWithA1:: | ||
// CHECK13: CommonFixA2 | ||
// CHECK14: CustomErrorA2 |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -27,6 +27,7 @@ | |
#include "swift/IDE/ModuleInterfacePrinting.h" | ||
#include "swift/IDE/Utils.h" | ||
#include "swift/Markup/XMLUtils.h" | ||
#include "swift/PrintAsObjC/PrintAsObjC.h" | ||
#include "swift/Sema/IDETypeChecking.h" | ||
|
||
#include "clang/AST/ASTContext.h" | ||
|
@@ -877,14 +878,47 @@ getClangDeclarationName(clang::ASTContext &Ctx, NameTranslatingInfo &Info) { | |
} | ||
} | ||
|
||
static DeclName | ||
getSwiftDeclName(ASTContext &Ctx, NameTranslatingInfo &Info) { | ||
assert(SwiftLangSupport::getNameKindForUID(Info.NameKind) == NameKind::Swift); | ||
std::vector<Identifier> Args(Info.ArgNames.size(), Identifier()); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nitpick: Why not SmallVector? |
||
std::transform(Info.ArgNames.begin(), Info.ArgNames.end(), Args.begin(), | ||
[&](StringRef T) { return Ctx.getIdentifier(T); }); | ||
return DeclName(Ctx, Ctx.getIdentifier(Info.BaseName), | ||
llvm::makeArrayRef(Args)); | ||
} | ||
|
||
/// Returns true for failure to resolve. | ||
static bool passNameInfoForDecl(const ValueDecl *VD, NameTranslatingInfo &Info, | ||
std::function<void(const NameTranslatingInfo &)> Receiver) { | ||
switch (SwiftLangSupport::getNameKindForUID(Info.NameKind)) { | ||
case NameKind::Swift: { | ||
|
||
// FIXME: Implement the swift to objc name translation. | ||
return true; | ||
NameTranslatingInfo Result; | ||
auto &Ctx = VD->getDeclContext()->getASTContext(); | ||
auto ResultPair = getObjCNameForSwiftDecl(VD, getSwiftDeclName(Ctx, Info)); | ||
Identifier Name = ResultPair.first; | ||
if (!Name.empty()) { | ||
Result.NameKind = SwiftLangSupport::getUIDForNameKind(NameKind::ObjC); | ||
Result.BaseName = Name.str(); | ||
} else if (ObjCSelector Selector = ResultPair.second) { | ||
Result.NameKind = SwiftLangSupport::getUIDForNameKind(NameKind::ObjC); | ||
SmallString<64> Buffer; | ||
StringRef Total = Selector.getString(Buffer); | ||
SmallVector<StringRef, 4> Pieces; | ||
Total.split(Pieces, ":"); | ||
if (Selector.getNumArgs()) { | ||
assert(Pieces.back().empty()); | ||
Pieces.pop_back(); | ||
std::transform(Pieces.begin(), Pieces.end(), Pieces.begin(), | ||
[](StringRef P) { return StringRef(P.data(), P.size() + 1); }); | ||
} | ||
Result.ArgNames = llvm::makeArrayRef(Pieces); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks suspicious, even though I know ArgNames isn't an ArrayRef itself. Can you go through a different API just to make it not look like you're storing the ArrayRef? |
||
} else { | ||
Receiver(Result); | ||
return true; | ||
} | ||
Receiver(Result); | ||
return false; | ||
} | ||
case NameKind::ObjC: { | ||
ClangImporter *Importer = static_cast<ClangImporter *>(VD->getDeclContext()-> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -593,7 +593,7 @@ static int handleTestInvocation(ArrayRef<const char *> Args, | |
} else { | ||
BaseName = Text.substr(0, ArgStart); | ||
auto ArgEnd = Text.find_last_of(')'); | ||
if (ArgEnd != StringRef::npos) { | ||
if (ArgEnd == StringRef::npos) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Heh, oops. |
||
llvm::errs() << "Swift name is malformed.\n"; | ||
return 1; | ||
} | ||
|
@@ -1160,9 +1160,6 @@ static void printNameTranslationInfo(sourcekitd_variant_t Info, | |
} | ||
for (auto S : Selectors) { | ||
OS << S; | ||
if (S != Selectors.back()) { | ||
OS << ":"; | ||
} | ||
} | ||
OS << '\n'; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nitpick: trailing semicolon. :-)