Skip to content

Commit 20ab640

Browse files
committed
[Function builders] Add code completion for build* functions.
When performing code completion inside the declaration of a type with the function builder attribute, also include completions for all of the possible "build" functions.
1 parent 19cfe21 commit 20ab640

File tree

5 files changed

+231
-101
lines changed

5 files changed

+231
-101
lines changed

include/swift/Sema/IDETypeChecking.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,29 @@ namespace swift {
254254
bool IncludeProtocolRequirements = true,
255255
bool Transitive = false);
256256

257+
/// Enumerates the various kinds of "build" functions within a function
258+
/// builder.
259+
enum class FunctionBuilderBuildFunction {
260+
BuildBlock,
261+
BuildExpression,
262+
BuildOptional,
263+
BuildEitherFirst,
264+
BuildEitherSecond,
265+
BuildArray,
266+
BuildLimitedAvailability,
267+
BuildFinalResult,
268+
};
269+
270+
/// Try to infer the component type of a function builder from the type
271+
/// of buildBlock or buildExpression, if it was there.
272+
Type inferFunctionBuilderComponentType(NominalTypeDecl *builder);
273+
274+
/// Print the declaration for a function builder "build" function, for use
275+
/// in Fix-Its, code completion, and so on.
276+
void printFunctionBuilderBuildFunction(
277+
NominalTypeDecl *builder, Type componentType,
278+
FunctionBuilderBuildFunction function,
279+
Optional<std::string> stubIndent, llvm::raw_ostream &out);
257280
}
258281

259282
#endif

lib/IDE/CodeCompletion.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5136,6 +5136,60 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
51365136
}
51375137
}
51385138

5139+
void addFunctionBuilderBuildCompletion(
5140+
NominalTypeDecl *builder, Type componentType,
5141+
FunctionBuilderBuildFunction function) {
5142+
CodeCompletionResultBuilder Builder(
5143+
Sink,
5144+
CodeCompletionResult::ResultKind::Pattern,
5145+
SemanticContextKind::CurrentNominal, {});
5146+
Builder.setExpectedTypeRelation(
5147+
CodeCompletionResult::ExpectedTypeRelation::NotApplicable);
5148+
5149+
if (!hasFuncIntroducer) {
5150+
if (!hasAccessModifier &&
5151+
builder->getFormalAccess() >= AccessLevel::Public)
5152+
Builder.addAccessControlKeyword(AccessLevel::Public);
5153+
5154+
if (!hasStaticOrClass)
5155+
Builder.addKeyword("static");
5156+
5157+
Builder.addKeyword("func");
5158+
}
5159+
5160+
std::string declStringWithoutFunc;
5161+
{
5162+
llvm::raw_string_ostream out(declStringWithoutFunc);
5163+
printFunctionBuilderBuildFunction(
5164+
builder, componentType, function, None, out);
5165+
}
5166+
Builder.addTextChunk(declStringWithoutFunc);
5167+
Builder.addBraceStmtWithCursor();
5168+
}
5169+
5170+
/// Add completions for the various "build" functions in a function builder.
5171+
void addFunctionBuilderBuildCompletions(NominalTypeDecl *builder) {
5172+
Type componentType = inferFunctionBuilderComponentType(builder);
5173+
5174+
addFunctionBuilderBuildCompletion(
5175+
builder, componentType, FunctionBuilderBuildFunction::BuildBlock);
5176+
addFunctionBuilderBuildCompletion(
5177+
builder, componentType, FunctionBuilderBuildFunction::BuildExpression);
5178+
addFunctionBuilderBuildCompletion(
5179+
builder, componentType, FunctionBuilderBuildFunction::BuildOptional);
5180+
addFunctionBuilderBuildCompletion(
5181+
builder, componentType, FunctionBuilderBuildFunction::BuildEitherFirst);
5182+
addFunctionBuilderBuildCompletion(
5183+
builder, componentType, FunctionBuilderBuildFunction::BuildEitherSecond);
5184+
addFunctionBuilderBuildCompletion(
5185+
builder, componentType, FunctionBuilderBuildFunction::BuildArray);
5186+
addFunctionBuilderBuildCompletion(
5187+
builder, componentType,
5188+
FunctionBuilderBuildFunction::BuildLimitedAvailability);
5189+
addFunctionBuilderBuildCompletion(
5190+
builder, componentType, FunctionBuilderBuildFunction::BuildFinalResult);
5191+
}
5192+
51395193
void getOverrideCompletions(SourceLoc Loc) {
51405194
if (!CurrDeclContext->isTypeContext())
51415195
return;
@@ -5154,6 +5208,10 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
51545208
addDesignatedInitializers(NTD);
51555209
addAssociatedTypes(NTD);
51565210
}
5211+
5212+
if (NTD && NTD->getAttrs().hasAttribute<FunctionBuilderAttr>()) {
5213+
addFunctionBuilderBuildCompletions(NTD);
5214+
}
51575215
}
51585216
};
51595217
} // end anonymous namespace

lib/Sema/BuilderTransform.cpp

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
#include "SolutionResult.h"
2121
#include "TypeChecker.h"
2222
#include "TypeCheckAvailability.h"
23+
#include "swift/Sema/IDETypeChecking.h"
24+
#include "swift/AST/ASTPrinter.h"
2325
#include "swift/AST/ASTVisitor.h"
2426
#include "swift/AST/ASTWalker.h"
2527
#include "swift/AST/NameLookup.h"
@@ -1884,3 +1886,107 @@ bool TypeChecker::typeSupportsBuilderOp(
18841886
return foundMatch;
18851887
}
18861888

1889+
Type swift::inferFunctionBuilderComponentType(NominalTypeDecl *builder) {
1890+
Type componentType;
1891+
1892+
SmallVector<ValueDecl *, 4> potentialMatches;
1893+
ASTContext &ctx = builder->getASTContext();
1894+
bool supportsBuildBlock = TypeChecker::typeSupportsBuilderOp(
1895+
builder->getDeclaredInterfaceType(), builder, ctx.Id_buildBlock,
1896+
/*argLabels=*/{}, &potentialMatches);
1897+
if (supportsBuildBlock) {
1898+
for (auto decl : potentialMatches) {
1899+
auto func = dyn_cast<FuncDecl>(decl);
1900+
if (!func || !func->isStatic())
1901+
continue;
1902+
1903+
// If we haven't seen a component type before, gather it.
1904+
if (!componentType) {
1905+
componentType = func->getResultInterfaceType();
1906+
continue;
1907+
}
1908+
1909+
// If there are inconsistent component types, bail out.
1910+
if (!componentType->isEqual(func->getResultInterfaceType())) {
1911+
componentType = Type();
1912+
break;
1913+
}
1914+
}
1915+
}
1916+
1917+
return componentType;
1918+
}
1919+
1920+
void swift::printFunctionBuilderBuildFunction(
1921+
NominalTypeDecl *builder, Type componentType,
1922+
FunctionBuilderBuildFunction function,
1923+
Optional<std::string> stubIndent, llvm::raw_ostream &out) {
1924+
// Render the component type into a string.
1925+
std::string componentTypeString;
1926+
if (componentType)
1927+
componentTypeString = componentType.getString();
1928+
else
1929+
componentTypeString = "<#Component#>";
1930+
1931+
// Render the code.
1932+
ExtraIndentStreamPrinter printer(out, stubIndent.getValueOr(std::string()));
1933+
1934+
// If we're supposed to provide a full stub, add a newline and the introducer
1935+
// keywords.
1936+
if (stubIndent) {
1937+
printer.printNewline();
1938+
1939+
if (builder->getFormalAccess() >= AccessLevel::Public)
1940+
printer << "public ";
1941+
1942+
printer << "static func ";
1943+
}
1944+
1945+
bool printedResult = false;
1946+
switch (function) {
1947+
case FunctionBuilderBuildFunction::BuildBlock:
1948+
printer << "buildBlock(_ components: " << componentTypeString << "...)";
1949+
break;
1950+
1951+
case FunctionBuilderBuildFunction::BuildExpression:
1952+
printer << "buildExpression(_ expression: <#Expression#>)";
1953+
break;
1954+
1955+
case FunctionBuilderBuildFunction::BuildOptional:
1956+
printer << "buildOptional(_ component: " << componentTypeString << "?)";
1957+
break;
1958+
1959+
case FunctionBuilderBuildFunction::BuildEitherFirst:
1960+
printer << "buildEither(first component: " << componentTypeString << ")";
1961+
break;
1962+
1963+
case FunctionBuilderBuildFunction::BuildEitherSecond:
1964+
printer << "buildEither(second component: " << componentTypeString << ")";
1965+
break;
1966+
1967+
case FunctionBuilderBuildFunction::BuildArray:
1968+
printer << "buildArray(_ components: [" << componentTypeString << "])";
1969+
break;
1970+
1971+
case FunctionBuilderBuildFunction::BuildLimitedAvailability:
1972+
printer << "buildLimitedAvailability(_ component: " << componentTypeString
1973+
<< ")";
1974+
break;
1975+
1976+
case FunctionBuilderBuildFunction::BuildFinalResult:
1977+
printer << "buildFinalResult(_ component: " << componentTypeString
1978+
<< ") -> <#Result#>";
1979+
printedResult = true;
1980+
break;
1981+
}
1982+
1983+
if (!printedResult)
1984+
printer << " -> " << componentTypeString << " {";
1985+
1986+
if (stubIndent) {
1987+
printer.printNewline();
1988+
printer << " <#code#>";
1989+
printer.printNewline();
1990+
printer << "}";
1991+
}
1992+
}

lib/Sema/CSDiagnostics.cpp

Lines changed: 25 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "MiscDiagnostics.h"
2020
#include "TypeCheckProtocol.h"
2121
#include "TypoCorrection.h"
22+
#include "swift/Sema/IDETypeChecking.h"
2223
#include "swift/AST/ASTContext.h"
2324
#include "swift/AST/ASTPrinter.h"
2425
#include "swift/AST/Decl.h"
@@ -5501,96 +5502,6 @@ static bool hasMissingElseInChain(IfStmt *ifStmt) {
55015502
return false;
55025503
}
55035504

5504-
namespace {
5505-
enum class MissingBuildFunction {
5506-
BuildOptional,
5507-
BuildEitherFirst,
5508-
BuildEitherSecond,
5509-
BuildArray,
5510-
};
5511-
}
5512-
5513-
/// Add code for a missing 'build' function to a function builder.
5514-
static void printMissingBuildFunctionCode(
5515-
SourceLoc buildInsertionLoc, NominalTypeDecl *builder,
5516-
MissingBuildFunction missing, llvm::raw_ostream &out) {
5517-
// Determine the component type from the builder itself, if we can.
5518-
ASTContext &ctx = builder->getASTContext();
5519-
Type componentType;
5520-
bool isPublic = false;
5521-
{
5522-
SmallVector<ValueDecl *, 4> potentialMatches;
5523-
bool supportsBuildBlock = TypeChecker::typeSupportsBuilderOp(
5524-
builder->getDeclaredInterfaceType(), builder, ctx.Id_buildBlock,
5525-
/*argLabels=*/{}, &potentialMatches);
5526-
if (supportsBuildBlock) {
5527-
for (auto decl : potentialMatches) {
5528-
auto func = dyn_cast<FuncDecl>(decl);
5529-
if (!func || !func->isStatic())
5530-
continue;
5531-
5532-
// If we haven't seen a component type before, gather it.
5533-
if (!componentType) {
5534-
componentType = func->getResultInterfaceType();
5535-
isPublic = func->getFormalAccess() == AccessLevel::Public;
5536-
continue;
5537-
}
5538-
5539-
// If there are inconsistent component types, bail out.
5540-
if (!componentType->isEqual(func->getResultInterfaceType())) {
5541-
componentType = Type();
5542-
isPublic = false;
5543-
break;
5544-
}
5545-
}
5546-
}
5547-
}
5548-
5549-
// Render the component type into a string.
5550-
std::string componentTypeString;
5551-
if (componentType)
5552-
componentTypeString = componentType.getString();
5553-
else
5554-
componentTypeString = "<#Component#>";
5555-
5556-
// Render the code.
5557-
StringRef extraIndent;
5558-
StringRef currentIndent = Lexer::getIndentationForLine(
5559-
ctx.SourceMgr, buildInsertionLoc, &extraIndent);
5560-
std::string stubIndent = (currentIndent + extraIndent).str();
5561-
5562-
ExtraIndentStreamPrinter printer(out, stubIndent);
5563-
printer.printNewline();
5564-
5565-
if (isPublic)
5566-
printer << "public ";
5567-
5568-
printer << "static func ";
5569-
switch (missing) {
5570-
case MissingBuildFunction::BuildOptional:
5571-
printer << "buildOptional(_ component: " << componentTypeString << "?)";
5572-
break;
5573-
5574-
case MissingBuildFunction::BuildEitherFirst:
5575-
printer << "buildEither(first component: " << componentTypeString << ")";
5576-
break;
5577-
5578-
case MissingBuildFunction::BuildEitherSecond:
5579-
printer << "buildEither(second component: " << componentTypeString << ")";
5580-
break;
5581-
5582-
case MissingBuildFunction::BuildArray:
5583-
printer << "buildArray(_ components: [" << componentTypeString << "])";
5584-
break;
5585-
}
5586-
5587-
printer << " -> " << componentTypeString << " {";
5588-
printer.printNewline();
5589-
printer << " <#code#>";
5590-
printer.printNewline();
5591-
printer << "}";
5592-
}
5593-
55945505
void SkipUnhandledConstructInFunctionBuilderFailure::diagnosePrimary(
55955506
bool asNote) {
55965507
if (auto stmt = unhandled.dyn_cast<Stmt *>()) {
@@ -5601,9 +5512,19 @@ void SkipUnhandledConstructInFunctionBuilderFailure::diagnosePrimary(
56015512
// Emit custom notes to help the user introduce the appropriate 'build'
56025513
// functions.
56035514
SourceLoc buildInsertionLoc = builder->getBraces().Start;
5515+
std::string stubIndent;
5516+
Type componentType;
56045517
if (buildInsertionLoc.isValid()) {
56055518
buildInsertionLoc = Lexer::getLocForEndOfToken(
56065519
getASTContext().SourceMgr, buildInsertionLoc);
5520+
5521+
ASTContext &ctx = getASTContext();
5522+
StringRef extraIndent;
5523+
StringRef currentIndent = Lexer::getIndentationForLine(
5524+
ctx.SourceMgr, buildInsertionLoc, &extraIndent);
5525+
stubIndent = (currentIndent + extraIndent).str();
5526+
5527+
componentType = inferFunctionBuilderComponentType(builder);
56075528
}
56085529

56095530
if (buildInsertionLoc.isInvalid()) {
@@ -5616,9 +5537,9 @@ void SkipUnhandledConstructInFunctionBuilderFailure::diagnosePrimary(
56165537
std::string fixItString;
56175538
{
56185539
llvm::raw_string_ostream out(fixItString);
5619-
printMissingBuildFunctionCode(
5620-
buildInsertionLoc, builder, MissingBuildFunction::BuildOptional,
5621-
out);
5540+
printFunctionBuilderBuildFunction(
5541+
builder, componentType, FunctionBuilderBuildFunction::BuildOptional,
5542+
stubIndent, out);
56225543
}
56235544

56245545
diag.fixItInsert(buildInsertionLoc, fixItString);
@@ -5630,13 +5551,15 @@ void SkipUnhandledConstructInFunctionBuilderFailure::diagnosePrimary(
56305551
std::string fixItString;
56315552
{
56325553
llvm::raw_string_ostream out(fixItString);
5633-
printMissingBuildFunctionCode(
5634-
buildInsertionLoc, builder, MissingBuildFunction::BuildEitherFirst,
5635-
out);
5554+
printFunctionBuilderBuildFunction(
5555+
builder, componentType,
5556+
FunctionBuilderBuildFunction::BuildEitherFirst,
5557+
stubIndent, out);
56365558
out << '\n';
5637-
printMissingBuildFunctionCode(
5638-
buildInsertionLoc, builder, MissingBuildFunction::BuildEitherSecond,
5639-
out);
5559+
printFunctionBuilderBuildFunction(
5560+
builder, componentType,
5561+
FunctionBuilderBuildFunction::BuildEitherSecond,
5562+
stubIndent, out);
56405563
}
56415564

56425565
diag.fixItInsert(buildInsertionLoc, fixItString);
@@ -5648,8 +5571,9 @@ void SkipUnhandledConstructInFunctionBuilderFailure::diagnosePrimary(
56485571
std::string fixItString;
56495572
{
56505573
llvm::raw_string_ostream out(fixItString);
5651-
printMissingBuildFunctionCode(
5652-
buildInsertionLoc, builder, MissingBuildFunction::BuildArray, out);
5574+
printFunctionBuilderBuildFunction(
5575+
builder, componentType, FunctionBuilderBuildFunction::BuildArray,
5576+
stubIndent, out);
56535577
}
56545578

56555579
diag.fixItInsert(buildInsertionLoc, fixItString);

0 commit comments

Comments
 (0)