Skip to content

Commit f898747

Browse files
authored
Merge pull request #33972 from DougGregor/function-builders-qoi
[Function builders] Fix-Its and code completion improvements
2 parents 983ee29 + 075e7d4 commit f898747

14 files changed

+591
-10
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5312,6 +5312,19 @@ NOTE(function_builder_buildblock_enum_case, none,
53125312
NOTE(function_builder_buildblock_not_static_method, none,
53135313
"potential match 'buildBlock' is not a static method", ())
53145314

5315+
NOTE(function_builder_missing_build_optional, none,
5316+
"add 'buildOptional(_:)' to the function builder %0 to add support for "
5317+
"'if' statements without an 'else'", (Type))
5318+
NOTE(function_builder_missing_build_either, none,
5319+
"add 'buildEither(first:)' and 'buildEither(second:)' to the function "
5320+
"builder %0 to add support for 'if'-'else' and 'switch'", (Type))
5321+
NOTE(function_builder_missing_build_array, none,
5322+
"add 'buildArray(_:)' to the function builder %0 to add support for "
5323+
"'for'..'in' loops", (Type))
5324+
NOTE(function_builder_missing_build_limited_availability, none,
5325+
"add 'buildLimitedAvailability(_:)' to the function "
5326+
"builder %0 to erase type information for less-available types", (Type))
5327+
53155328
//------------------------------------------------------------------------------
53165329
// MARK: Tuple Shuffle Diagnostics
53175330
//------------------------------------------------------------------------------

include/swift/AST/EducationalNotes.def

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,15 @@ EDUCATIONAL_NOTES(type_cannot_conform, "protocol-type-non-conformance.md")
8181
EDUCATIONAL_NOTES(unlabeled_trailing_closure_deprecated,
8282
"trailing-closure-matching.md")
8383

84+
EDUCATIONAL_NOTES(function_builder_static_buildblock,
85+
"function-builder-methods.md")
86+
EDUCATIONAL_NOTES(function_builder_missing_limited_availability,
87+
"function-builder-methods.md")
88+
EDUCATIONAL_NOTES(function_builder_missing_build_optional,
89+
"function-builder-methods.md")
90+
EDUCATIONAL_NOTES(function_builder_missing_build_either,
91+
"function-builder-methods.md")
92+
EDUCATIONAL_NOTES(function_builder_missing_build_array,
93+
"function-builder-methods.md")
94+
8495
#undef EDUCATIONAL_NOTES

include/swift/IDE/CodeCompletion.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -640,11 +640,13 @@ class CodeCompletionResult {
640640
CodeCompletionString *CompletionString,
641641
ExpectedTypeRelation TypeDistance,
642642
CodeCompletionOperatorKind KnownOperatorKind =
643-
CodeCompletionOperatorKind::None)
643+
CodeCompletionOperatorKind::None,
644+
StringRef BriefDocComment = StringRef())
644645
: Kind(Kind), KnownOperatorKind(unsigned(KnownOperatorKind)),
645646
SemanticContext(unsigned(SemanticContext)), NotRecommended(false),
646647
NotRecReason(NotRecommendedReason::NoReason),
647648
NumBytesToErase(NumBytesToErase), CompletionString(CompletionString),
649+
BriefDocComment(BriefDocComment),
648650
TypeDistance(TypeDistance) {
649651
assert(Kind != Declaration && "use the other constructor");
650652
assert(CompletionString);
@@ -664,12 +666,13 @@ class CodeCompletionResult {
664666
SemanticContextKind SemanticContext,
665667
unsigned NumBytesToErase,
666668
CodeCompletionString *CompletionString,
667-
ExpectedTypeRelation TypeDistance)
669+
ExpectedTypeRelation TypeDistance,
670+
StringRef BriefDocComment = StringRef())
668671
: Kind(Keyword), KnownOperatorKind(0),
669672
SemanticContext(unsigned(SemanticContext)), NotRecommended(false),
670673
NotRecReason(NotRecommendedReason::NoReason),
671674
NumBytesToErase(NumBytesToErase), CompletionString(CompletionString),
672-
TypeDistance(TypeDistance) {
675+
BriefDocComment(BriefDocComment), TypeDistance(TypeDistance) {
673676
assert(CompletionString);
674677
AssociatedKind = static_cast<unsigned>(Kind);
675678
IsSystem = 0;

include/swift/Sema/IDETypeChecking.h

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "swift/AST/Identifier.h"
2323
#include "swift/Basic/SourceLoc.h"
2424
#include <memory>
25+
#include <tuple>
2526

2627
namespace swift {
2728
class AbstractFunctionDecl;
@@ -254,6 +255,34 @@ namespace swift {
254255
bool IncludeProtocolRequirements = true,
255256
bool Transitive = false);
256257

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

259288
#endif

lib/IDE/CodeCompletion.cpp

Lines changed: 105 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1345,13 +1345,17 @@ CodeCompletionResult *CodeCompletionResultBuilder::takeResult() {
13451345

13461346
case CodeCompletionResult::ResultKind::Keyword:
13471347
return new (*Sink.Allocator)
1348-
CodeCompletionResult(KeywordKind, SemanticContext, NumBytesToErase,
1349-
CCS, ExpectedTypeRelation);
1348+
CodeCompletionResult(
1349+
KeywordKind, SemanticContext, NumBytesToErase,
1350+
CCS, ExpectedTypeRelation,
1351+
copyString(*Sink.Allocator, BriefDocComment));
13501352

13511353
case CodeCompletionResult::ResultKind::BuiltinOperator:
13521354
case CodeCompletionResult::ResultKind::Pattern:
13531355
return new (*Sink.Allocator) CodeCompletionResult(
1354-
Kind, SemanticContext, NumBytesToErase, CCS, ExpectedTypeRelation);
1356+
Kind, SemanticContext, NumBytesToErase, CCS, ExpectedTypeRelation,
1357+
CodeCompletionOperatorKind::None,
1358+
copyString(*Sink.Allocator, BriefDocComment));
13551359

13561360
case CodeCompletionResult::ResultKind::Literal:
13571361
assert(LiteralKind.hasValue());
@@ -5136,6 +5140,100 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
51365140
}
51375141
}
51385142

5143+
static StringRef getFunctionBuilderDocComment(
5144+
FunctionBuilderBuildFunction function) {
5145+
switch (function) {
5146+
case FunctionBuilderBuildFunction::BuildArray:
5147+
return "Enables support for..in loops in a function builder by "
5148+
"combining the results of all iterations into a single result";
5149+
5150+
case FunctionBuilderBuildFunction::BuildBlock:
5151+
return "Required by every function builder to build combined results "
5152+
"from statement blocks";
5153+
5154+
case FunctionBuilderBuildFunction::BuildEitherFirst:
5155+
return "With buildEither(second:), enables support for 'if-else' and "
5156+
"'switch' statements by folding conditional results into a single "
5157+
"result";
5158+
5159+
case FunctionBuilderBuildFunction::BuildEitherSecond:
5160+
return "With buildEither(first:), enables support for 'if-else' and "
5161+
"'switch' statements by folding conditional results into a single "
5162+
"result";
5163+
5164+
case FunctionBuilderBuildFunction::BuildExpression:
5165+
return "If declared, provides contextual type information for statement "
5166+
"expressions to translate them into partial results";
5167+
5168+
case FunctionBuilderBuildFunction::BuildFinalResult:
5169+
return "If declared, this will be called on the partial result from the "
5170+
"outermost block statement to produce the final returned result";
5171+
5172+
case FunctionBuilderBuildFunction::BuildLimitedAvailability:
5173+
return "If declared, this will be called on the partial result of "
5174+
"an 'if #available' block to allow the function builder to erase "
5175+
"type information";
5176+
5177+
case FunctionBuilderBuildFunction::BuildOptional:
5178+
return "Enables support for `if` statements that do not have an `else`";
5179+
}
5180+
}
5181+
5182+
void addFunctionBuilderBuildCompletion(
5183+
NominalTypeDecl *builder, Type componentType,
5184+
FunctionBuilderBuildFunction function) {
5185+
CodeCompletionResultBuilder Builder(
5186+
Sink,
5187+
CodeCompletionResult::ResultKind::Pattern,
5188+
SemanticContextKind::CurrentNominal, {});
5189+
Builder.setExpectedTypeRelation(
5190+
CodeCompletionResult::ExpectedTypeRelation::NotApplicable);
5191+
5192+
if (!hasFuncIntroducer) {
5193+
if (!hasAccessModifier &&
5194+
builder->getFormalAccess() >= AccessLevel::Public)
5195+
Builder.addAccessControlKeyword(AccessLevel::Public);
5196+
5197+
if (!hasStaticOrClass)
5198+
Builder.addTextChunk("static ");
5199+
5200+
Builder.addTextChunk("func ");
5201+
}
5202+
5203+
std::string declStringWithoutFunc;
5204+
{
5205+
llvm::raw_string_ostream out(declStringWithoutFunc);
5206+
printFunctionBuilderBuildFunction(
5207+
builder, componentType, function, None, out);
5208+
}
5209+
Builder.addTextChunk(declStringWithoutFunc);
5210+
Builder.addBraceStmtWithCursor();
5211+
Builder.setBriefDocComment(getFunctionBuilderDocComment(function));
5212+
}
5213+
5214+
/// Add completions for the various "build" functions in a function builder.
5215+
void addFunctionBuilderBuildCompletions(NominalTypeDecl *builder) {
5216+
Type componentType = inferFunctionBuilderComponentType(builder);
5217+
5218+
addFunctionBuilderBuildCompletion(
5219+
builder, componentType, FunctionBuilderBuildFunction::BuildBlock);
5220+
addFunctionBuilderBuildCompletion(
5221+
builder, componentType, FunctionBuilderBuildFunction::BuildExpression);
5222+
addFunctionBuilderBuildCompletion(
5223+
builder, componentType, FunctionBuilderBuildFunction::BuildOptional);
5224+
addFunctionBuilderBuildCompletion(
5225+
builder, componentType, FunctionBuilderBuildFunction::BuildEitherFirst);
5226+
addFunctionBuilderBuildCompletion(
5227+
builder, componentType, FunctionBuilderBuildFunction::BuildEitherSecond);
5228+
addFunctionBuilderBuildCompletion(
5229+
builder, componentType, FunctionBuilderBuildFunction::BuildArray);
5230+
addFunctionBuilderBuildCompletion(
5231+
builder, componentType,
5232+
FunctionBuilderBuildFunction::BuildLimitedAvailability);
5233+
addFunctionBuilderBuildCompletion(
5234+
builder, componentType, FunctionBuilderBuildFunction::BuildFinalResult);
5235+
}
5236+
51395237
void getOverrideCompletions(SourceLoc Loc) {
51405238
if (!CurrDeclContext->isTypeContext())
51415239
return;
@@ -5154,6 +5252,10 @@ class CompletionOverrideLookup : public swift::VisibleDeclConsumer {
51545252
addDesignatedInitializers(NTD);
51555253
addAssociatedTypes(NTD);
51565254
}
5255+
5256+
if (NTD && NTD->getAttrs().hasAttribute<FunctionBuilderAttr>()) {
5257+
addFunctionBuilderBuildCompletions(NTD);
5258+
}
51575259
}
51585260
};
51595261
} // end anonymous namespace

lib/IDE/CodeCompletionResultBuilder.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ class CodeCompletionResultBuilder {
9292
bool IsNotRecommended = false;
9393
CodeCompletionResult::NotRecommendedReason NotRecReason =
9494
CodeCompletionResult::NotRecommendedReason::NoReason;
95+
StringRef BriefDocComment = StringRef();
9596

9697
void addChunkWithText(CodeCompletionString::Chunk::ChunkKind Kind,
9798
StringRef Text);
@@ -446,6 +447,10 @@ class CodeCompletionResultBuilder {
446447
addWhitespace(space);
447448
getLastChunk().setIsAnnotation();
448449
}
450+
451+
void setBriefDocComment(StringRef comment) {
452+
BriefDocComment = comment;
453+
}
449454
};
450455

451456
} // namespace ide

0 commit comments

Comments
 (0)