[Completion] Suggest trivial trailing closures for macros

Follow the same logic as function decl completion and suggest a
trailing closure for trivial cases.

rdar://150550747
This commit is contained in:
Hamish Knight
2025-05-21 18:06:54 +01:00
parent 352caa4ddc
commit 52d8b36af4
3 changed files with 49 additions and 12 deletions

View File

@@ -458,7 +458,8 @@ public:
void addEnumElementRef(const EnumElementDecl *EED, DeclVisibilityKind Reason,
DynamicLookupInfo dynamicLookupInfo,
bool HasTypeContext);
void addMacroCallArguments(const MacroDecl *MD, DeclVisibilityKind Reason);
void addMacroCallArguments(const MacroDecl *MD, DeclVisibilityKind Reason,
bool forTrivialTrailingClosure = false);
void addMacroExpansion(const MacroDecl *MD, DeclVisibilityKind Reason);
void addKeyword(

View File

@@ -104,12 +104,18 @@ static Type defaultTypeLiteralKind(CodeCompletionLiteralKind kind,
llvm_unreachable("Unhandled CodeCompletionLiteralKind in switch.");
}
/// Whether funcType has a single argument (not including defaulted arguments)
/// that is of type () -> ().
static bool hasTrivialTrailingClosure(const FuncDecl *FD,
AnyFunctionType *funcType) {
ParameterListInfo paramInfo(funcType->getParams(), FD,
/*skipCurriedSelf*/ FD->hasCurriedSelf());
/// Whether the provided type has a single argument (not including defaulted
/// arguments) that is of type () -> ().
static bool hasTrivialTrailingClosure(const ValueDecl *VD, Type type) {
if (!VD->hasParameterList())
return false;
auto *funcType = type->getAs<AnyFunctionType>();
if (!funcType)
return false;
ParameterListInfo paramInfo(funcType->getParams(), VD,
/*skipCurriedSelf*/ VD->hasCurriedSelf());
if (paramInfo.size() - paramInfo.numNonDefaultedParameters() == 1) {
auto param = funcType->getParams().back();
@@ -1947,7 +1953,8 @@ static StringRef getTypeAnnotationString(const MacroDecl *MD,
}
void CompletionLookup::addMacroCallArguments(const MacroDecl *MD,
DeclVisibilityKind Reason) {
DeclVisibilityKind Reason,
bool forTrivialTrailingClosure) {
CodeCompletionResultBuilder Builder =
makeResultBuilder(CodeCompletionResultKind::Declaration,
getSemanticContext(MD, Reason, DynamicLookupInfo()));
@@ -1955,11 +1962,12 @@ void CompletionLookup::addMacroCallArguments(const MacroDecl *MD,
addValueBaseName(Builder, MD->getBaseIdentifier());
Type macroType = MD->getInterfaceType();
if (MD->parameterList && MD->parameterList->size() > 0) {
if (forTrivialTrailingClosure) {
Builder.addBraceStmtWithCursor(" { code }");
} else if (MD->parameterList && MD->parameterList->size() > 0) {
auto *macroTy = MD->getInterfaceType()->castTo<AnyFunctionType>();
Builder.addLeftParen();
addCallArgumentPatterns(Builder, macroType->castTo<AnyFunctionType>(),
MD->parameterList,
addCallArgumentPatterns(Builder, macroTy, MD->parameterList,
MD->getGenericSignature());
Builder.addRightParen();
}
@@ -1990,6 +1998,9 @@ void CompletionLookup::addMacroExpansion(const MacroDecl *MD,
return;
}
if (hasTrivialTrailingClosure(MD, MD->getInterfaceType()))
addMacroCallArguments(MD, Reason, /*forTrivialTrailingClosure*/ true);
addMacroCallArguments(MD, Reason);
}

View File

@@ -21,6 +21,15 @@ public macro freestandingExprStringMacro() -> String
@freestanding(expression)
public macro freestandingExprTMacro<T>(_ value: T) -> T
@freestanding(expression)
public macro freestandingExprVoidClosureMacro(fn: () -> Void)
@freestanding(expression)
public macro freestandingExprIntClosureMacro(fn: () -> Int)
@freestanding(declaration)
public macro freestandingDeclVoidClosureMacro(fn: () -> Void)
@freestanding(declaration)
public macro freestandingDeclMacro()
@@ -158,8 +167,21 @@ func nestedFreestanding() {
// ALL_FREESTANDING-DAG: Decl[Macro]/{{.*}}: freestandingExprTMacro({#(value): T#})[#T#]; name=freestandingExprTMacro(:)
// ALL_FREESTANDING-DAG: Decl[Macro]/{{.*}}: EverythingMacro[#Expression Macro, Declaration Macro, Accessor Macro, Member Attribute Macro, Member Macro, Peer Macro, Extension Macro#]; name=EverythingMacro
//
// We offer a trailing closure completion for the Void case, but not the
// Int case currently. Placeholder expansion will turn the latter into the
// former though.
//
// ALL_FREESTANDING-DAG: Decl[Macro]/{{.*}}: freestandingExprVoidClosureMacro {|}[#Void#]; name=freestandingExprVoidClosureMacro
// ALL_FREESTANDING-DAG: Decl[Macro]/{{.*}}: freestandingExprVoidClosureMacro({#fn: () -> Void##() -> Void#})[#Void#]; name=freestandingExprVoidClosureMacro(fn:)
//
// ALL_FREESTANDING-DAG: Decl[Macro]/{{.*}}: freestandingExprIntClosureMacro({#fn: () -> Int##() -> Int#})[#Void#]; name=freestandingExprIntClosureMacro(fn:)
//
// ALL_FREESTANDING-DAG: Decl[Macro]/{{.*}}: freestandingDeclVoidClosureMacro {|}[#Declaration Macro#]; name=freestandingDeclVoidClosureMacro
// ALL_FREESTANDING-DAG: Decl[Macro]/{{.*}}: freestandingDeclVoidClosureMacro({#fn: () -> Void##() -> Void#})[#Declaration Macro#]; name=freestandingDeclVoidClosureMacro(fn:)
//
// ALL_FREESTANDING_NOT-NOT: Attached
// ALL_FREESTANDING_NOT-NOT: BodyMacro
// ALL_FREESTANDING_NOT-NOT: freestandingExprIntClosureMacro {|}
func exprFreestanding(arg: Int) {
_ = arg + ##^EXPR_FREESTANDING?check=EXPR_FREESTANDING;check=EXPR_FREESTANDING_NOT^#
@@ -181,6 +203,9 @@ struct NestedFreestanding {
// ITEM_FREESTANDING-DAG: Decl[Macro]/{{.*}}: freestandingDeclMacro[#Declaration Macro#]; name=freestandingDeclMacro
// ITEM_FREESTANDING-DAG: Decl[Macro]/{{.*}}: EverythingMacro[#Expression Macro, Declaration Macro, Accessor Macro, Member Attribute Macro, Member Macro, Peer Macro, Extension Macro#]; name=EverythingMacro
//
// ITEM_FREESTANDING-DAG: Decl[Macro]/{{.*}}: freestandingDeclVoidClosureMacro {|}[#Declaration Macro#]; name=freestandingDeclVoidClosureMacro
// ITEM_FREESTANDING-DAG: Decl[Macro]/{{.*}}: freestandingDeclVoidClosureMacro({#fn: () -> Void##() -> Void#})[#Declaration Macro#]; name=freestandingDeclVoidClosureMacro(fn:)
//
// ITEM_FREESTANDING_NOT-NOT: Attached
// ITEM_FREESTANDING_NOT-NOT: freestandingExpr
// ITEM_FREESTANDING_NOT-NOT: freestandingCodeItemMacro