mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
[Code completion] Code complete compound function names within #selector.
When we're code completing a postfix or dot expression inside the subexpression of an #selector expression, prefer compound function names. This helps us write, e.g., #selector(UIView. and get completions such as "insertSubview(_:aboveSubview:)". Fixes rdar://problem/24470075.
This commit is contained in:
@@ -37,6 +37,9 @@ protected:
|
|||||||
/// case.
|
/// case.
|
||||||
bool InEnumElementRawValue = false;
|
bool InEnumElementRawValue = false;
|
||||||
|
|
||||||
|
/// True if code completion is done inside a #selector expression.
|
||||||
|
bool InObjCSelectorExpr = false;
|
||||||
|
|
||||||
std::vector<Expr *> leadingSequenceExprs;
|
std::vector<Expr *> leadingSequenceExprs;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -95,6 +98,24 @@ public:
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// RAII type that temporarily sets the "in Objective-C #selector expression"
|
||||||
|
/// flag on the code completion callbacks object.
|
||||||
|
class InObjCSelectorExprRAII {
|
||||||
|
CodeCompletionCallbacks *Callbacks;
|
||||||
|
|
||||||
|
public:
|
||||||
|
InObjCSelectorExprRAII(CodeCompletionCallbacks *Callbacks)
|
||||||
|
: Callbacks(Callbacks) {
|
||||||
|
if (Callbacks)
|
||||||
|
Callbacks->InObjCSelectorExpr = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
InObjCSelectorExprRAII() {
|
||||||
|
if (Callbacks)
|
||||||
|
Callbacks->InObjCSelectorExpr = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/// \brief Complete the whole expression. This is a fallback that should
|
/// \brief Complete the whole expression. This is a fallback that should
|
||||||
/// produce results when more specific completion methods failed.
|
/// produce results when more specific completion methods failed.
|
||||||
virtual void completeExpr() = 0;
|
virtual void completeExpr() = 0;
|
||||||
|
|||||||
@@ -1086,6 +1086,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks {
|
|||||||
bool HasSpace = false;
|
bool HasSpace = false;
|
||||||
bool HasRParen = false;
|
bool HasRParen = false;
|
||||||
bool ShouldCompleteCallPatternAfterParen = true;
|
bool ShouldCompleteCallPatternAfterParen = true;
|
||||||
|
bool PreferFunctionReferencesToCalls = false;
|
||||||
Optional<DeclKind> AttTargetDK;
|
Optional<DeclKind> AttTargetDK;
|
||||||
|
|
||||||
SmallVector<StringRef, 3> ParsedKeywords;
|
SmallVector<StringRef, 3> ParsedKeywords;
|
||||||
@@ -1420,6 +1421,7 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
|
|||||||
bool HaveRParen = false;
|
bool HaveRParen = false;
|
||||||
bool IsSuperRefExpr = false;
|
bool IsSuperRefExpr = false;
|
||||||
bool IsDynamicLookup = false;
|
bool IsDynamicLookup = false;
|
||||||
|
bool PreferFunctionReferencesToCalls = false;
|
||||||
bool HaveLeadingSpace = false;
|
bool HaveLeadingSpace = false;
|
||||||
|
|
||||||
/// \brief True if we are code completing inside a static method.
|
/// \brief True if we are code completing inside a static method.
|
||||||
@@ -1567,6 +1569,10 @@ public:
|
|||||||
IsDynamicLookup = true;
|
IsDynamicLookup = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setPreferFunctionReferencesToCalls() {
|
||||||
|
PreferFunctionReferencesToCalls = true;
|
||||||
|
}
|
||||||
|
|
||||||
void setHaveLeadingSpace(bool value) { HaveLeadingSpace = value; }
|
void setHaveLeadingSpace(bool value) { HaveLeadingSpace = value; }
|
||||||
|
|
||||||
void addExpressionSpecificDecl(const Decl *D) {
|
void addExpressionSpecificDecl(const Decl *D) {
|
||||||
@@ -2404,6 +2410,43 @@ public:
|
|||||||
Builder.addDeclAttrKeyword(Name, Annotation);
|
Builder.addDeclAttrKeyword(Name, Annotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add the compound function name for the given function.
|
||||||
|
void addCompoundFunctionName(AbstractFunctionDecl *AFD,
|
||||||
|
DeclVisibilityKind Reason) {
|
||||||
|
CommandWordsPairs Pairs;
|
||||||
|
CodeCompletionResultBuilder Builder(
|
||||||
|
Sink, CodeCompletionResult::ResultKind::Declaration,
|
||||||
|
getSemanticContext(AFD, Reason), ExpectedTypes);
|
||||||
|
setClangDeclKeywords(AFD, Pairs, Builder);
|
||||||
|
Builder.setAssociatedDecl(AFD);
|
||||||
|
|
||||||
|
// Base name
|
||||||
|
addLeadingDot(Builder);
|
||||||
|
Builder.addTextChunk(AFD->getFullName().getBaseName().str());
|
||||||
|
|
||||||
|
// Add the argument labels.
|
||||||
|
auto ArgLabels = AFD->getFullName().getArgumentNames();
|
||||||
|
if (ArgLabels.size() > 0) {
|
||||||
|
if (!HaveLParen)
|
||||||
|
Builder.addLeftParen();
|
||||||
|
else
|
||||||
|
Builder.addAnnotatedLeftParen();
|
||||||
|
|
||||||
|
for (auto ArgLabel : ArgLabels) {
|
||||||
|
if (ArgLabel.empty())
|
||||||
|
Builder.addTextChunk("_");
|
||||||
|
else
|
||||||
|
Builder.addTextChunk(ArgLabel.str());
|
||||||
|
Builder.addTextChunk(":");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!HaveRParen)
|
||||||
|
Builder.addRightParen();
|
||||||
|
else
|
||||||
|
Builder.addAnnotatedRightParen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Implement swift::VisibleDeclConsumer.
|
// Implement swift::VisibleDeclConsumer.
|
||||||
void foundDecl(ValueDecl *D, DeclVisibilityKind Reason) override {
|
void foundDecl(ValueDecl *D, DeclVisibilityKind Reason) override {
|
||||||
// Hide private stdlib declarations.
|
// Hide private stdlib declarations.
|
||||||
@@ -2426,6 +2469,12 @@ public:
|
|||||||
switch (Kind) {
|
switch (Kind) {
|
||||||
case LookupKind::ValueExpr:
|
case LookupKind::ValueExpr:
|
||||||
if (auto *CD = dyn_cast<ConstructorDecl>(D)) {
|
if (auto *CD = dyn_cast<ConstructorDecl>(D)) {
|
||||||
|
// Do we want compound function names here?
|
||||||
|
if (PreferFunctionReferencesToCalls) {
|
||||||
|
addCompoundFunctionName(CD, Reason);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (auto MT = ExprType->getRValueType()->getAs<AnyMetatypeType>()) {
|
if (auto MT = ExprType->getRValueType()->getAs<AnyMetatypeType>()) {
|
||||||
if (HaveDot) {
|
if (HaveDot) {
|
||||||
Type Ty;
|
Type Ty;
|
||||||
@@ -2483,6 +2532,12 @@ public:
|
|||||||
if (FD->isAccessor())
|
if (FD->isAccessor())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// Do we want compound function names here?
|
||||||
|
if (PreferFunctionReferencesToCalls) {
|
||||||
|
addCompoundFunctionName(FD, Reason);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
addMethodCall(FD, Reason);
|
addMethodCall(FD, Reason);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -3765,6 +3820,9 @@ void CodeCompletionCallbacksImpl::completeDotExpr(Expr *E, SourceLoc DotLoc) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Kind = CompletionKind::DotExpr;
|
Kind = CompletionKind::DotExpr;
|
||||||
|
if (InObjCSelectorExpr)
|
||||||
|
PreferFunctionReferencesToCalls = true;
|
||||||
|
|
||||||
ParsedExpr = E;
|
ParsedExpr = E;
|
||||||
this->DotLoc = DotLoc;
|
this->DotLoc = DotLoc;
|
||||||
CurDeclContext = P.CurDeclContext;
|
CurDeclContext = P.CurDeclContext;
|
||||||
@@ -3786,6 +3844,9 @@ void CodeCompletionCallbacksImpl::completePostfixExprBeginning(CodeCompletionExp
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Kind = CompletionKind::PostfixExprBeginning;
|
Kind = CompletionKind::PostfixExprBeginning;
|
||||||
|
if (InObjCSelectorExpr)
|
||||||
|
PreferFunctionReferencesToCalls = true;
|
||||||
|
|
||||||
CurDeclContext = P.CurDeclContext;
|
CurDeclContext = P.CurDeclContext;
|
||||||
CStyleForLoopIterationVariable =
|
CStyleForLoopIterationVariable =
|
||||||
CodeCompletionCallbacks::CStyleForLoopIterationVariable;
|
CodeCompletionCallbacks::CStyleForLoopIterationVariable;
|
||||||
@@ -3801,6 +3862,9 @@ void CodeCompletionCallbacksImpl::completePostfixExpr(Expr *E, bool hasSpace) {
|
|||||||
|
|
||||||
HasSpace = hasSpace;
|
HasSpace = hasSpace;
|
||||||
Kind = CompletionKind::PostfixExpr;
|
Kind = CompletionKind::PostfixExpr;
|
||||||
|
if (InObjCSelectorExpr)
|
||||||
|
PreferFunctionReferencesToCalls = true;
|
||||||
|
|
||||||
ParsedExpr = E;
|
ParsedExpr = E;
|
||||||
CurDeclContext = P.CurDeclContext;
|
CurDeclContext = P.CurDeclContext;
|
||||||
}
|
}
|
||||||
@@ -3839,6 +3903,9 @@ void CodeCompletionCallbacksImpl::completeExprSuper(SuperRefExpr *SRE) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Kind = CompletionKind::SuperExpr;
|
Kind = CompletionKind::SuperExpr;
|
||||||
|
if (InObjCSelectorExpr)
|
||||||
|
PreferFunctionReferencesToCalls = true;
|
||||||
|
|
||||||
ParsedExpr = SRE;
|
ParsedExpr = SRE;
|
||||||
CurDeclContext = P.CurDeclContext;
|
CurDeclContext = P.CurDeclContext;
|
||||||
}
|
}
|
||||||
@@ -3849,6 +3916,9 @@ void CodeCompletionCallbacksImpl::completeExprSuperDot(SuperRefExpr *SRE) {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Kind = CompletionKind::SuperExprDot;
|
Kind = CompletionKind::SuperExprDot;
|
||||||
|
if (InObjCSelectorExpr)
|
||||||
|
PreferFunctionReferencesToCalls = true;
|
||||||
|
|
||||||
ParsedExpr = SRE;
|
ParsedExpr = SRE;
|
||||||
CurDeclContext = P.CurDeclContext;
|
CurDeclContext = P.CurDeclContext;
|
||||||
}
|
}
|
||||||
@@ -4389,6 +4459,8 @@ void CodeCompletionCallbacksImpl::doneParsing() {
|
|||||||
if (ExprType) {
|
if (ExprType) {
|
||||||
Lookup.setIsStaticMetatype(ParsedExpr->isStaticallyDerivedMetatype());
|
Lookup.setIsStaticMetatype(ParsedExpr->isStaticallyDerivedMetatype());
|
||||||
}
|
}
|
||||||
|
if (PreferFunctionReferencesToCalls)
|
||||||
|
Lookup.setPreferFunctionReferencesToCalls();
|
||||||
|
|
||||||
auto DoPostfixExprBeginning = [&] (){
|
auto DoPostfixExprBeginning = [&] (){
|
||||||
if (CStyleForLoopIterationVariable)
|
if (CStyleForLoopIterationVariable)
|
||||||
|
|||||||
@@ -522,6 +522,8 @@ ParserResult<Expr> Parser::parseExprSelector() {
|
|||||||
SourceLoc lParenLoc = consumeToken(tok::l_paren);
|
SourceLoc lParenLoc = consumeToken(tok::l_paren);
|
||||||
|
|
||||||
// Parse the subexpression.
|
// Parse the subexpression.
|
||||||
|
CodeCompletionCallbacks::InObjCSelectorExprRAII
|
||||||
|
InObjCSelectorExpr(CodeCompletion);
|
||||||
ParserResult<Expr> subExpr = parseExpr(diag::expr_selector_expected_expr);
|
ParserResult<Expr> subExpr = parseExpr(diag::expr_selector_expected_expr);
|
||||||
if (subExpr.hasCodeCompletion())
|
if (subExpr.hasCodeCompletion())
|
||||||
return subExpr;
|
return subExpr;
|
||||||
|
|||||||
@@ -4,6 +4,14 @@
|
|||||||
|
|
||||||
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=SELECTOR_ARG2 | FileCheck -check-prefix=CHECK-SELECTOR_ARG %s
|
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=SELECTOR_ARG2 | FileCheck -check-prefix=CHECK-SELECTOR_ARG %s
|
||||||
|
|
||||||
|
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=IN_SELECTOR1 | FileCheck -check-prefix=CHECK-IN_SELECTOR %s
|
||||||
|
|
||||||
|
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=IN_SELECTOR2 | FileCheck -check-prefix=CHECK-IN_SELECTOR %s
|
||||||
|
|
||||||
|
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=IN_SELECTOR3 | FileCheck -check-prefix=CHECK-IN_SUPER_SELECTOR %s
|
||||||
|
|
||||||
|
// RUN: %target-swift-ide-test(mock-sdk: %clang-importer-sdk) -code-completion -source-filename %s -code-completion-token=IN_SELECTOR4 | FileCheck -check-prefix=CHECK-IN_SUPER_SELECTOR %s
|
||||||
|
|
||||||
// REQUIRES: objc_interop
|
// REQUIRES: objc_interop
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
@@ -20,7 +28,38 @@ func selectorArg2(obj: NSObject) {
|
|||||||
obj.messageSomeObject(obj, selector:#^SELECTOR_ARG2^#
|
obj.messageSomeObject(obj, selector:#^SELECTOR_ARG2^#
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func inSelector1() {
|
||||||
|
_ = #selector(NSObject.#^IN_SELECTOR1^#)
|
||||||
|
}
|
||||||
|
|
||||||
|
func inSelector2() {
|
||||||
|
_ = #selector(NSObject#^IN_SELECTOR2^#)
|
||||||
|
}
|
||||||
|
|
||||||
|
class Subclass : NSObject {
|
||||||
|
func inSelector3() {
|
||||||
|
_ = #selector(super.#^IN_SELECTOR3^#)
|
||||||
|
}
|
||||||
|
|
||||||
|
func inSelector4() {
|
||||||
|
_ = #selector(super#^IN_SELECTOR4^#)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// CHECK-AFTER_POUND: Keyword/ExprSpecific: available({#Platform...#}, *); name=available(Platform..., *)
|
// CHECK-AFTER_POUND: Keyword/ExprSpecific: available({#Platform...#}, *); name=available(Platform..., *)
|
||||||
// CHECK-AFTER_POUND: Keyword/ExprSpecific: selector({#@objc method#}); name=selector(@objc method)
|
// CHECK-AFTER_POUND: Keyword/ExprSpecific: selector({#@objc method#}); name=selector(@objc method)
|
||||||
|
|
||||||
// CHECK-SELECTOR_ARG: Keyword/ExprSpecific: #selector({#@objc method#}); name=#selector(@objc method)
|
// CHECK-SELECTOR_ARG: Keyword/ExprSpecific: #selector({#@objc method#}); name=#selector(@objc method)
|
||||||
|
|
||||||
|
// CHECK-IN_SELECTOR: Decl[Constructor]/CurrNominal: {{.?}}init; name=init
|
||||||
|
// CHECK-IN_SELECTOR: Decl[StaticMethod]/CurrNominal: {{.?}}performSelector(_:withObject:); name=performSelector(_:withObject:)
|
||||||
|
// CHECK-IN_SELECTOR: Decl[InstanceMethod]/CurrNominal: {{.?}}performSelector(_:withObject:); name=performSelector(_:withObject:)
|
||||||
|
// CHECK-IN_SELECTOR: Decl[InstanceMethod]/CurrNominal: {{.?}}myClass; name=myClass
|
||||||
|
// CHECK-IN_SELECTOR: Decl[StaticMethod]/CurrNominal: {{.?}}description; name=description
|
||||||
|
// CHECK-IN_SELECTOR: Decl[StaticMethod]/CurrNominal: {{.?}}isEqual(_:); name=isEqual(_:)
|
||||||
|
// CHECK-IN_SELECTOR: Decl[InstanceMethod]/CurrNominal: {{.?}}isEqual(_:); name=isEqual(_:)
|
||||||
|
|
||||||
|
// CHECK-IN_SUPER_SELECTOR: Decl[InstanceMethod]/CurrNominal: {{.?}}performSelector(_:withObject:); name=performSelector(_:withObject:)
|
||||||
|
// CHECK-IN_SUPER_SELECTOR: Decl[InstanceMethod]/CurrNominal: {{.?}}myClass; name=myClass
|
||||||
|
// CHECK-IN_SUPER_SELECTOR: Decl[InstanceMethod]/CurrNominal: {{.?}}isEqual(_:); name=isEqual(_:)
|
||||||
|
|||||||
Reference in New Issue
Block a user