Skip to content

[Completion] Only skip result builder expressions in the same builder #74219

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions lib/Sema/BuilderTransform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ class ResultBuilderTransform
DeclContext *dc;
ResultBuilder builder;

/// The source range of the body.
SourceRange bodyRange;

/// The result type of this result builder body.
Type ResultType;

Expand All @@ -80,9 +83,9 @@ class ResultBuilderTransform

public:
ResultBuilderTransform(ConstraintSystem &cs, DeclContext *dc,
Type builderType, Type resultTy)
SourceRange bodyRange, Type builderType, Type resultTy)
: ctx(cs.getASTContext()), dc(dc), builder(cs, dc, builderType),
ResultType(resultTy) {}
bodyRange(bodyRange), ResultType(resultTy) {}

UnsupportedElt getUnsupportedElement() const { return FirstUnsupported; }

Expand Down Expand Up @@ -238,12 +241,14 @@ class ResultBuilderTransform
// buildBlock higher.
buildBlockArguments.push_back(expr);
} else if (ctx.CompletionCallback && expr->getSourceRange().isValid() &&
containsIDEInspectionTarget(bodyRange, ctx.SourceMgr) &&
!containsIDEInspectionTarget(expr->getSourceRange(),
ctx.SourceMgr)) {
// A statement that doesn't contain the code completion expression can't
// influence the type of the code completion expression. Add a variable
// for it that we can put into the buildBlock call but don't add the
// expression itself into the transformed body to improve performance.
// A top-level expression that doesn't contain the code completion
// expression can't influence the type of the code completion expression
// if they're in the same result builder. Add a variable for it that we
// can put into the buildBlock call but don't add the expression itself
// into the transformed body to improve performance.
auto *resultVar = buildPlaceholderVar(expr->getStartLoc(), newBody);
buildBlockArguments.push_back(
builder.buildVarRef(resultVar, expr->getStartLoc()));
Expand Down Expand Up @@ -1176,6 +1181,7 @@ ConstraintSystem::matchResultBuilder(AnyFunctionRef fn, Type builderType,
// let's do it and cache the result.
if (!transformedBody) {
ResultBuilderTransform transform(*this, fn.getAsDeclContext(),
fn.getBody()->getSourceRange(),
builderType, bodyResultType);
auto *body = transform.apply(fn.getBody());

Expand Down
36 changes: 36 additions & 0 deletions test/IDE/complete_rdar127154780.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// RUN: %empty-directory(%t)
// RUN: %swift-ide-test -batch-code-completion -source-filename %s -filecheck %raw-FileCheck -completion-output-dir %t

// rdar://127154780 - Make sure we provide completions on variables that rely
// on result builders being solved.

@resultBuilder
enum Builder {
static func buildBlock<T>(_ x: T) -> T { x }
}

struct S {}

struct R<T> {
init(@Builder _: () -> T) {}
}

extension R where T == S {
func bar() {}
}

func foo() {
let r = R() {
S()
}
r.#^COMPLETE1?check=COMPLETE^#

let fn = {
let r = R() {
S()
}
r.#^COMPLETE2?check=COMPLETE^#
}
}
// COMPLETE-DAG: Keyword[self]/CurrNominal: self[#R<S>#]; name=self
// COMPLETE-DAG: Decl[InstanceMethod]/CurrNominal: bar()[#Void#]; name=bar()