Skip to content

Commit c8f7da4

Browse files
committed
[CodeCompletion] Support a narrow case for producing trailing closures directly
This adds a narrow special case in code-completion for control-flow-like methods such as DispatchQueue().sync that are () -> (), to add a new completion where the trailing closure is immediately expanded rather than having to invoke placeholder expansion as a second step. rdar://problem/26628804
1 parent 20939dd commit c8f7da4

File tree

8 files changed

+268
-135
lines changed

8 files changed

+268
-135
lines changed

include/swift/AST/Types.h

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2385,7 +2385,42 @@ BEGIN_CAN_TYPE_WRAPPER(FunctionType, AnyFunctionType)
23852385
return CanFunctionType(cast<FunctionType>(getPointer()->withExtInfo(info)));
23862386
}
23872387
END_CAN_TYPE_WRAPPER(FunctionType, AnyFunctionType)
2388-
2388+
2389+
/// A call argument or parameter.
2390+
struct CallArgParam {
2391+
/// The type of the argument or parameter. For a variadic parameter,
2392+
/// this is the element type.
2393+
Type Ty;
2394+
2395+
// The label associated with the argument or parameter, if any.
2396+
Identifier Label;
2397+
2398+
/// Whether the parameter has a default argument. Not valid for arguments.
2399+
bool HasDefaultArgument = false;
2400+
2401+
/// Whether the parameter is variadic. Not valid for arguments.
2402+
bool Variadic = false;
2403+
2404+
/// Whether the argument or parameter has a label.
2405+
bool hasLabel() const { return !Label.empty(); }
2406+
};
2407+
2408+
/// Break an argument type into an array of \c CallArgParams.
2409+
///
2410+
/// \param type The type to decompose.
2411+
SmallVector<CallArgParam, 4> decomposeArgType(Type type);
2412+
2413+
/// Break a parameter type into an array of \c CallArgParams.
2414+
///
2415+
/// \param paramOwner The declaration that owns this parameter.
2416+
/// \param level The level of parameters that are being decomposed.
2417+
SmallVector<CallArgParam, 4>
2418+
decomposeParamType(Type type, const ValueDecl *paramOwner, unsigned level);
2419+
2420+
/// Turn a param list into a symbolic and printable representation that does not
2421+
/// include the types, something like (: , b:, c:)
2422+
std::string getParamListAsString(ArrayRef<CallArgParam> parameters);
2423+
23892424
/// PolymorphicFunctionType - A polymorphic function type.
23902425
class PolymorphicFunctionType : public AnyFunctionType {
23912426
// TODO: storing a GenericParamList* here is really the wrong solution;

lib/AST/Type.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -778,6 +778,93 @@ Type TypeBase::replaceCovariantResultType(Type newResultType,
778778
return FunctionType::get(inputType, resultType, fnType->getExtInfo());
779779
}
780780

781+
SmallVector<CallArgParam, 4> swift::decomposeArgType(Type type) {
782+
return decomposeParamType(type, nullptr, /*level=*/0);
783+
}
784+
785+
SmallVector<CallArgParam, 4>
786+
swift::decomposeParamType(Type type, const ValueDecl *paramOwner,
787+
unsigned level) {
788+
// Find the corresponding parameter list.
789+
const ParameterList *paramList = nullptr;
790+
if (paramOwner) {
791+
if (auto func = dyn_cast<AbstractFunctionDecl>(paramOwner)) {
792+
if (level < func->getNumParameterLists())
793+
paramList = func->getParameterList(level);
794+
} else if (auto subscript = dyn_cast<SubscriptDecl>(paramOwner)) {
795+
if (level == 1)
796+
paramList = subscript->getIndices();
797+
}
798+
}
799+
800+
SmallVector<CallArgParam, 4> result;
801+
switch (type->getKind()) {
802+
case TypeKind::Tuple: {
803+
auto tupleTy = cast<TupleType>(type.getPointer());
804+
805+
// FIXME: In the weird case where we have a tuple type that should
806+
// be wrapped in a ParenType but isn't, just... forget it happened.
807+
if (paramList && tupleTy->getNumElements() != paramList->size() &&
808+
paramList->size() == 1)
809+
paramList = nullptr;
810+
811+
for (auto i : range(0, tupleTy->getNumElements())) {
812+
const auto &elt = tupleTy->getElement(i);
813+
814+
CallArgParam argParam;
815+
argParam.Ty = elt.isVararg() ? elt.getVarargBaseTy() : elt.getType();
816+
argParam.Label = elt.getName();
817+
argParam.HasDefaultArgument =
818+
paramList && paramList->get(i)->isDefaultArgument();
819+
argParam.Variadic = elt.isVararg();
820+
result.push_back(argParam);
821+
}
822+
break;
823+
}
824+
825+
case TypeKind::Paren: {
826+
CallArgParam argParam;
827+
argParam.Ty = cast<ParenType>(type.getPointer())->getUnderlyingType();
828+
argParam.HasDefaultArgument =
829+
paramList && paramList->get(0)->isDefaultArgument();
830+
result.push_back(argParam);
831+
break;
832+
}
833+
834+
default: {
835+
CallArgParam argParam;
836+
argParam.Ty = type;
837+
result.push_back(argParam);
838+
break;
839+
}
840+
}
841+
842+
return result;
843+
}
844+
845+
/// Turn a param list into a symbolic and printable representation that does not
846+
/// include the types, something like (_:, b:, c:)
847+
std::string swift::getParamListAsString(ArrayRef<CallArgParam> params) {
848+
std::string result = "(";
849+
850+
bool isFirst = true;
851+
for (auto &param : params) {
852+
if (isFirst)
853+
isFirst = false;
854+
else
855+
result += ", ";
856+
857+
if (param.hasLabel())
858+
result += param.Label.str();
859+
else
860+
result += "_";
861+
result += ":";
862+
}
863+
864+
result += ')';
865+
return result;
866+
}
867+
781868
/// Rebuilds the given 'self' type using the given object type as the
782869
/// replacement for the object type of self.
783870
static Type rebuildSelfTypeWithObjectType(Type selfTy, Type objectTy) {

lib/IDE/CodeCompletion.cpp

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1175,8 +1175,6 @@ void CodeCompletionString::getName(raw_ostream &OS) const {
11751175
if (FirstTextChunk.hasValue()) {
11761176
for (auto C : getChunks().slice(*FirstTextChunk)) {
11771177
using ChunkKind = Chunk::ChunkKind;
1178-
if (C.getKind() == ChunkKind::BraceStmtWithCursor)
1179-
break;
11801178

11811179
bool shouldPrint = !C.isAnnotation();
11821180
switch (C.getKind()) {
@@ -1565,6 +1563,29 @@ protocolForLiteralKind(CodeCompletionLiteralKind kind) {
15651563
}
15661564
}
15671565

1566+
/// Whether funcType has a single argument (not including defaulted arguments)
1567+
/// that is of type () -> ().
1568+
static bool hasTrivialTrailingClosure(const FuncDecl *FD,
1569+
AnyFunctionType *funcType) {
1570+
unsigned level = FD->isInstanceMember() ? 1 : 0;
1571+
auto Args = decomposeParamType(funcType->getInput(), FD, level);
1572+
bool OneArg = Args.size() == 1;
1573+
if (Args.size() > 1) {
1574+
unsigned NonDefault =
1575+
std::count_if(Args.begin(), Args.end() - 1, [](const CallArgParam &P) {
1576+
return !P.HasDefaultArgument;
1577+
});
1578+
1579+
OneArg = NonDefault == 0;
1580+
}
1581+
1582+
if (OneArg)
1583+
if (auto Fn = Args.back().Ty->getAs<AnyFunctionType>())
1584+
return Fn->getInput()->isVoid() && Fn->getResult()->isVoid();
1585+
1586+
return false;
1587+
}
1588+
15681589
/// Build completions by doing visible decl lookup from a context.
15691590
class CompletionLookup final : public swift::VisibleDeclConsumer {
15701591
CodeCompletionResultSink &Sink;
@@ -2361,8 +2382,15 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
23612382
if (FirstIndex != 0 && !FunctionType->is<ErrorType>())
23622383
FunctionType = FunctionType->castTo<AnyFunctionType>()->getResult();
23632384

2385+
bool trivialTrailingClosure = false;
2386+
if (!IsImplicitlyCurriedInstanceMethod && !FunctionType->is<ErrorType>()) {
2387+
trivialTrailingClosure = hasTrivialTrailingClosure(
2388+
FD, FunctionType->castTo<AnyFunctionType>());
2389+
}
2390+
23642391
// Add the method, possibly including any default arguments.
2365-
auto addMethodImpl = [&](bool includeDefaultArgs = true) {
2392+
auto addMethodImpl = [&](bool includeDefaultArgs = true,
2393+
bool trivialTrailingClosure = false) {
23662394
CommandWordsPairs Pairs;
23672395
CodeCompletionResultBuilder Builder(
23682396
Sink, CodeCompletionResult::ResultKind::Declaration,
@@ -2395,6 +2423,8 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
23952423
Builder.addCallParameter(Ctx.Id_self, FirstInputType,
23962424
/*IsVarArg*/ false, true);
23972425
Builder.addRightParen();
2426+
} else if (trivialTrailingClosure) {
2427+
Builder.addBraceStmtWithCursor(" { code }");
23982428
} else {
23992429
Builder.addLeftParen();
24002430
auto AFT = FunctionType->castTo<AnyFunctionType>();
@@ -2427,6 +2457,10 @@ class CompletionLookup final : public swift::VisibleDeclConsumer {
24272457
hasInterestingDefaultValues(FD)) {
24282458
addMethodImpl(/*includeDefaultArgs*/ false);
24292459
}
2460+
if (trivialTrailingClosure) {
2461+
addMethodImpl(/*includeDefaultArgs=*/false,
2462+
/*trivialTrailingClosure=*/true);
2463+
}
24302464
addMethodImpl();
24312465
}
24322466

lib/IDE/CodeCompletionResultBuilder.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -417,9 +417,10 @@ class CodeCompletionResultBuilder {
417417
getLastChunk().setIsAnnotation();
418418
}
419419

420-
void addBraceStmtWithCursor() {
421-
addChunkWithTextNoCopy(
422-
CodeCompletionString::Chunk::ChunkKind::BraceStmtWithCursor, " {}");
420+
void addBraceStmtWithCursor(StringRef Description = "") {
421+
addChunkWithText(
422+
CodeCompletionString::Chunk::ChunkKind::BraceStmtWithCursor,
423+
Description);
423424
}
424425

425426
void addWhitespace(StringRef space) {

lib/Sema/CSSimplify.cpp

Lines changed: 0 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -72,94 +72,6 @@ static Optional<unsigned> scoreParamAndArgNameTypo(StringRef paramName,
7272
return dist;
7373
}
7474

75-
SmallVector<CallArgParam, 4> constraints::decomposeArgType(Type type) {
76-
return decomposeParamType(type, nullptr, /*level=*/0);
77-
}
78-
79-
SmallVector<CallArgParam, 4>
80-
constraints::decomposeParamType(Type type, ValueDecl *paramOwner,
81-
unsigned level) {
82-
// Find the corresponding parameter list.
83-
ParameterList *paramList = nullptr;
84-
if (paramOwner) {
85-
if (auto func = dyn_cast<AbstractFunctionDecl>(paramOwner)) {
86-
if (level < func->getNumParameterLists())
87-
paramList = func->getParameterList(level);
88-
} else if (auto subscript = dyn_cast<SubscriptDecl>(paramOwner)) {
89-
if (level == 1)
90-
paramList = subscript->getIndices();
91-
}
92-
}
93-
94-
SmallVector<CallArgParam, 4> result;
95-
switch (type->getKind()) {
96-
case TypeKind::Tuple: {
97-
auto tupleTy = cast<TupleType>(type.getPointer());
98-
99-
// FIXME: In the weird case where we have a tuple type that should
100-
// be wrapped in a ParenType but isn't, just... forget it happened.
101-
if (paramList && tupleTy->getNumElements() != paramList->size() &&
102-
paramList->size() == 1)
103-
paramList = nullptr;
104-
105-
for (auto i : range(0, tupleTy->getNumElements())) {
106-
const auto &elt = tupleTy->getElement(i);
107-
108-
CallArgParam argParam;
109-
argParam.Ty = elt.isVararg() ? elt.getVarargBaseTy() : elt.getType();
110-
argParam.Label = elt.getName();
111-
argParam.HasDefaultArgument =
112-
paramList && paramList->get(i)->isDefaultArgument();
113-
argParam.Variadic = elt.isVararg();
114-
result.push_back(argParam);
115-
}
116-
break;
117-
}
118-
119-
case TypeKind::Paren: {
120-
CallArgParam argParam;
121-
argParam.Ty = cast<ParenType>(type.getPointer())->getUnderlyingType();
122-
argParam.HasDefaultArgument =
123-
paramList && paramList->get(0)->isDefaultArgument();
124-
result.push_back(argParam);
125-
break;
126-
}
127-
128-
default: {
129-
CallArgParam argParam;
130-
argParam.Ty = type;
131-
result.push_back(argParam);
132-
break;
133-
}
134-
}
135-
136-
return result;
137-
}
138-
139-
/// Turn a param list into a symbolic and printable representation that does not
140-
/// include the types, something like (_:, b:, c:)
141-
std::string constraints::getParamListAsString(ArrayRef<CallArgParam> params){
142-
std::string result = "(";
143-
144-
bool isFirst = true;
145-
for (auto &param : params) {
146-
if (isFirst)
147-
isFirst = false;
148-
else
149-
result += ", ";
150-
151-
if (param.hasLabel())
152-
result += param.Label.str();
153-
else
154-
result += "_";
155-
result += ":";
156-
}
157-
158-
159-
result += ')';
160-
return result;
161-
}
162-
16375
bool constraints::
16476
areConservativelyCompatibleArgumentLabels(ValueDecl *decl,
16577
unsigned parameterDepth,

lib/Sema/ConstraintSystem.h

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2131,43 +2131,6 @@ static inline bool computeTupleShuffle(TupleType *fromTuple,
21312131
/// trailing closure.
21322132
bool hasTrailingClosure(const ConstraintLocatorBuilder &locator);
21332133

2134-
/// A call argument or parameter.
2135-
struct LLVM_LIBRARY_VISIBILITY CallArgParam {
2136-
/// The type of the argument or parameter. For a variadic parameter,
2137-
/// this is the element type.
2138-
Type Ty;
2139-
2140-
// The label associated with the argument or parameter, if any.
2141-
Identifier Label;
2142-
2143-
/// Whether the parameter has a default argument. Not valid for arguments.
2144-
bool HasDefaultArgument = false;
2145-
2146-
/// Whether the parameter is variadic. Not valid for arguments.
2147-
bool Variadic = false;
2148-
2149-
/// Whether the argument or parameter has a label.
2150-
bool hasLabel() const { return !Label.empty(); }
2151-
};
2152-
2153-
/// Break an argument type into an array of \c CallArgParams.
2154-
///
2155-
/// \param type The type to decompose.
2156-
SmallVector<CallArgParam, 4> decomposeArgType(Type type);
2157-
2158-
/// Break a parameter type into an array of \c CallArgParams.
2159-
///
2160-
/// \param paramOwner The declaration that owns this parameter.
2161-
/// \param level The level of parameters that are being decomposed.
2162-
SmallVector<CallArgParam, 4> decomposeParamType(
2163-
Type type,
2164-
ValueDecl *paramOwner,
2165-
unsigned level);
2166-
2167-
/// Turn a param list into a symbolic and printable representation that does not
2168-
/// include the types, something like (: , b:, c:)
2169-
std::string getParamListAsString(ArrayRef<CallArgParam> parameters);
2170-
21712134
/// Describes the arguments to which a parameter binds.
21722135
/// FIXME: This is an awful data structure. We want the equivalent of a
21732136
/// TinyPtrVector for unsigned values.

0 commit comments

Comments
 (0)