diff --git a/lib/AST/Decl.cpp b/lib/AST/Decl.cpp index b319384c61679..c828a70e2c66b 100644 --- a/lib/AST/Decl.cpp +++ b/lib/AST/Decl.cpp @@ -7968,7 +7968,6 @@ AbstractStorageDecl::getObjCGetterSelector(Identifier preferredName) const { name = preferredName; return VarDecl::getDefaultObjCGetterSelector(ctx, name); } - ObjCSelector AbstractStorageDecl::getObjCSetterSelector(Identifier preferredName) const { auto abiRole = ABIRoleInfo(this); @@ -10573,7 +10572,8 @@ AbstractFunctionDecl::getObjCSelector(DeclName preferredName, StringRef baseNameStr; if (auto destructor = dyn_cast(this)) { return destructor->getObjCSelector(); - } else if (auto func = dyn_cast(this)) { + } + if (auto func = dyn_cast(this)) { // Otherwise cast this to be able to access getName() baseNameStr = func->getBaseIdentifier().str(); } else if (isa(this)) { diff --git a/test/SourceKit/ObjCSelector/basic.swift b/test/SourceKit/ObjCSelector/basic.swift new file mode 100644 index 0000000000000..d6cf8c3b2d7b5 --- /dev/null +++ b/test/SourceKit/ObjCSelector/basic.swift @@ -0,0 +1,26 @@ +// REQUIRES: objc_interop + +import Foundation + +class MyClass: NSObject { + @objc func simpleMethod() { + print("simple") + } + + @objc func methodWithParameters(param1: Int, param2: String) { + print("params") + } + + @objc(customSelector:with:) + func methodWithCustomSelector(foo: Int, bar: String) { + print("custom") + } +} + +// RUN: %sourcekitd-test -req=objc-selector -pos=6:16 %s -- -target %target-triple -sdk %sdk %s | %FileCheck %s -check-prefix=CHECK-SIMPLE +// RUN: %sourcekitd-test -req=objc-selector -pos=10:16 %s -- -target %target-triple -sdk %sdk %s | %FileCheck %s -check-prefix=CHECK-PARAMS +// RUN: %sourcekitd-test -req=objc-selector -pos=15:10 %s -- -target %target-triple -sdk %sdk %s | %FileCheck %s -check-prefix=CHECK-CUSTOM + +// CHECK-SIMPLE: simpleMethod +// CHECK-PARAMS: methodWithParametersWithParam1:param2: +// CHECK-CUSTOM: customSelector:with: diff --git a/tools/SourceKit/include/SourceKit/Core/LangSupport.h b/tools/SourceKit/include/SourceKit/Core/LangSupport.h index d60a7f6cd979d..1247d2a216330 100644 --- a/tools/SourceKit/include/SourceKit/Core/LangSupport.h +++ b/tools/SourceKit/include/SourceKit/Core/LangSupport.h @@ -1249,6 +1249,13 @@ class LangSupport { SourceKitCancellationToken CancellationToken, CategorizedEditsReceiver Receiver) = 0; + virtual void getObjCSelector(StringRef PrimaryFilePath, + StringRef InputBufferName, + unsigned Offset, + ArrayRef Args, + SourceKitCancellationToken CancellationToken, + std::function &)> Receiver) = 0; + virtual void collectExpressionTypes( StringRef PrimaryFilePath, StringRef InputBufferName, ArrayRef Args, ArrayRef ExpectedProtocols, diff --git a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h index 6ead209834927..22ca3db63c502 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h +++ b/tools/SourceKit/lib/SwiftLang/SwiftLangSupport.h @@ -729,6 +729,13 @@ class SwiftLangSupport : public LangSupport { SourceKitCancellationToken CancellationToken, CategorizedEditsReceiver Receiver) override; + void getObjCSelector(StringRef PrimaryFilePath, + StringRef InputBufferName, + unsigned Offset, + ArrayRef Args, + SourceKitCancellationToken CancellationToken, + std::function &)> Receiver) override; + void getDocInfo(llvm::MemoryBuffer *InputBuf, StringRef ModuleName, ArrayRef Args, diff --git a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp index 34caf4b5f44f0..4cfee8dd1fd71 100644 --- a/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp +++ b/tools/SourceKit/lib/SwiftLang/SwiftSourceDocInfo.cpp @@ -2888,6 +2888,87 @@ void SwiftLangSupport::semanticRefactoring( llvm::vfs::getRealFileSystem()); } +void SwiftLangSupport::getObjCSelector( + StringRef PrimaryFilePath, StringRef InputBufferName, + unsigned Offset, ArrayRef Args, + SourceKitCancellationToken CancellationToken, + std::function &)> Receiver) { + std::string Error; + SwiftInvocationRef Invok = + ASTMgr->getTypecheckInvocation(Args, PrimaryFilePath, Error); + if (!Invok) { + LOG_WARN_FUNC("failed to create an ASTInvocation: " << Error); + Receiver(RequestResult::fromError(Error)); + return; + } + + class ObjCSelectorConsumer : public SwiftASTConsumer { + std::string InputBufferName; + unsigned Offset; + std::function &)> Receiver; + + public: + ObjCSelectorConsumer(StringRef InputBufferName, unsigned Offset, + std::function &)> Receiver) + : InputBufferName(InputBufferName.str()), Offset(Offset), + Receiver(std::move(Receiver)) {} + + void handlePrimaryAST(ASTUnitRef AstUnit) override { + auto &CompIns = AstUnit->getCompilerInstance(); + auto &SM = CompIns.getSourceMgr(); + + SourceFile *SF = retrieveInputFile(InputBufferName, CompIns); + if (!SF) { + Receiver(RequestResult::fromError("Unable to find input file")); + return; + } + + unsigned BufferID = SF->getBufferID(); + SourceLoc Loc = SM.getLocForOffset(BufferID, Offset); + + auto CursorInfo = evaluateOrDefault( + SF->getASTContext().evaluator, + CursorInfoRequest{CursorInfoOwner(SF, Loc)}, + new ResolvedCursorInfo()); + + if (!CursorInfo || CursorInfo->isInvalid()) { + Receiver(RequestResult::fromError("Invalid cursor position")); + return; + } + + if (auto *ValueRefInfo = dyn_cast(CursorInfo.get())) { + if (auto *VD = ValueRefInfo->getValueD()) { + if (auto *AFD = dyn_cast(VD)) { + if (AFD->isObjC()) { + auto selector = AFD->getObjCSelector(); + SmallString<64> scratch; + std::string selectorString = selector.getString(scratch).str(); + Receiver(RequestResult::fromResult(selectorString)); + return; + } + } + } + } + + Receiver(RequestResult::fromError( + "Cursor is not on an Objective-C method")); + } + + void cancelled() override { + Receiver(RequestResult::cancelled()); + } + + void failed(StringRef Error) override { + Receiver(RequestResult::fromError(Error)); + } + }; + + auto Consumer = std::make_shared(InputBufferName, Offset, Receiver); + getASTManager()->processASTAsync(Invok, std::move(Consumer), nullptr, + CancellationToken, + llvm::vfs::getRealFileSystem()); +} + void SwiftLangSupport::collectExpressionTypes( StringRef PrimaryFilePath, StringRef InputBufferName, ArrayRef Args, ArrayRef ExpectedProtocols, diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp index f17962bfce1eb..2b3b3c2f7a287 100644 --- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.cpp @@ -155,6 +155,7 @@ bool TestOptions::parseArgs(llvm::ArrayRef Args) { .Case("compile.close", SourceKitRequest::CompileClose) .Case("syntactic-expandmacro", SourceKitRequest::SyntacticMacroExpansion) .Case("index-to-store", SourceKitRequest::IndexToStore) + .Case("objc-selector", SourceKitRequest::ObjCSelector) #define SEMANTIC_REFACTORING(KIND, NAME, ID) .Case("refactoring." #ID, SourceKitRequest::KIND) #include "swift/Refactoring/RefactoringKinds.def" .Default(SourceKitRequest::None); @@ -207,6 +208,7 @@ bool TestOptions::parseArgs(llvm::ArrayRef Args) { << "- dependency-updated\n" << "- syntactic-expandmacro\n" << "- index-to-store\n" + << "- objc-selector\n" #define SEMANTIC_REFACTORING(KIND, NAME, ID) << "- refactoring." #ID "\n" #include "swift/Refactoring/RefactoringKinds.def" "\n"; diff --git a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h index ae641fbe24f3b..3773f09bf1c8e 100644 --- a/tools/SourceKit/tools/sourcekitd-test/TestOptions.h +++ b/tools/SourceKit/tools/sourcekitd-test/TestOptions.h @@ -73,6 +73,7 @@ enum class SourceKitRequest { CompileClose, SyntacticMacroExpansion, IndexToStore, + ObjCSelector, #define SEMANTIC_REFACTORING(KIND, NAME, ID) KIND, #include "swift/Refactoring/RefactoringKinds.def" }; diff --git a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp index 05ca22c951aac..a4d224ad28dce 100644 --- a/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp +++ b/tools/SourceKit/tools/sourcekitd-test/sourcekitd-test.cpp @@ -1174,6 +1174,11 @@ static int handleTestInvocation(TestOptions Opts, TestOptions &InitOpts) { sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestIndexToStore); addRequestOptionsDirect(Req, Opts); break; + + case SourceKitRequest::ObjCSelector: + sourcekitd_request_dictionary_set_uid(Req, KeyRequest, RequestGetObjCSelector); + sourcekitd_request_dictionary_set_int64(Req, KeyOffset, ByteOffset); + break; } if (!Opts.SourceFile.empty()) { @@ -1596,6 +1601,13 @@ static bool handleResponse(sourcekitd_response_t Resp, const TestOptions &Opts, case SourceKitRequest::IndexToStore: printRawResponse(Resp); break; + case SourceKitRequest::ObjCSelector: { + const char *Text = sourcekitd_variant_dictionary_get_string(Info, KeyText); + if (Text) { + llvm::outs() << Text << "\n"; + } + break; + } } } diff --git a/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp b/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp index 3138558df83d9..f6d5cef3f7578 100644 --- a/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp +++ b/tools/SourceKit/tools/sourcekitd/lib/Service/Requests.cpp @@ -1767,6 +1767,51 @@ handleRequestSemanticRefactoring(const RequestDict &Req, }); } +static void +handleRequestGetObjCSelector(const RequestDict &Req, + SourceKitCancellationToken CancellationToken, + ResponseReceiver Rec) { + if (checkVFSNotSupported(Req, Rec)) + return; + + handleSemanticRequest(Req, Rec, [Req, CancellationToken, Rec]() { + std::optional PrimaryFilePath = + getPrimaryFilePathForRequestOrEmitError(Req, Rec); + if (!PrimaryFilePath) + return; + + StringRef InputBufferName = getInputBufferNameForRequest(Req, Rec); + + SmallVector Args; + if (getCompilerArgumentsForRequestOrEmitError(Req, Args, Rec)) + return; + + int64_t Offset = 0; + if (Req.getInt64(KeyOffset, Offset, /*isOptional=*/false)) { + return Rec(createErrorRequestInvalid("'key.offset' is required")); + } + + LangSupport &Lang = getGlobalContext().getSwiftLangSupport(); + + return Lang.getObjCSelector( + *PrimaryFilePath, InputBufferName, Offset, Args, + CancellationToken, + [Rec](const RequestResult &Result) { + if (Result.isCancelled()) { + return Rec(createErrorRequestCancelled()); + } + if (Result.isError()) { + return Rec(createErrorRequestFailed(Result.getError())); + } + + ResponseBuilder RespBuilder; + auto Dict = RespBuilder.getDictionary(); + Dict.set(KeyText, Result.value()); + Rec(RespBuilder.createResponse()); + }); + }); +} + static void handleRequestCollectExpressionType(const RequestDict &Req, SourceKitCancellationToken CancellationToken, @@ -2278,6 +2323,7 @@ void handleRequestImpl(sourcekitd_object_t ReqObj, HANDLE_REQUEST(RequestCursorInfo, handleRequestCursorInfo) HANDLE_REQUEST(RequestRangeInfo, handleRequestRangeInfo) HANDLE_REQUEST(RequestSemanticRefactoring, handleRequestSemanticRefactoring) + HANDLE_REQUEST(RequestGetObjCSelector, handleRequestGetObjCSelector) HANDLE_REQUEST(RequestCollectExpressionType, handleRequestCollectExpressionType) diff --git a/utils/gyb_sourcekit_support/UIDs.py b/utils/gyb_sourcekit_support/UIDs.py index d0fbecd686e3e..860ced91aeb38 100644 --- a/utils/gyb_sourcekit_support/UIDs.py +++ b/utils/gyb_sourcekit_support/UIDs.py @@ -292,6 +292,7 @@ def __init__(self, internal_name, external_name): REQUEST('FindLocalRenameRanges', 'source.request.find-local-rename-ranges'), REQUEST('SemanticRefactoring', 'source.request.semantic.refactoring'), + REQUEST('GetObjCSelector', 'source.request.objc.selector'), REQUEST('EnableCompileNotifications', 'source.request.enable-compile-notifications'), REQUEST('TestNotification', 'source.request.test_notification'),