From 3d9b7f3ac29ca78c25d2b212c9e90fa4e85c47d2 Mon Sep 17 00:00:00 2001 From: Hamish Knight Date: Thu, 28 Aug 2025 01:08:00 +0100 Subject: [PATCH] [Completion] Avoid type-checking parent closure for unattached node We should have already type-checked a parent closure, and we wouldn't be able to correctly locate the node anyway since it's not actually part of the AST. While here, also walk up to the parent-most closure instead of recursing to avoid unnecessary stack frames for nested closures. --- lib/Sema/TypeCheckStmt.cpp | 34 ++++++++++++------- .../7b2825e90bfce47.swift | 2 +- .../928f167118f85cf.swift | 2 +- 3 files changed, 24 insertions(+), 14 deletions(-) rename validation-test/IDE/{crashers => crashers_fixed}/7b2825e90bfce47.swift (62%) rename validation-test/IDE/{crashers => crashers_fixed}/928f167118f85cf.swift (70%) diff --git a/lib/Sema/TypeCheckStmt.cpp b/lib/Sema/TypeCheckStmt.cpp index bff6cec1bee7e..1b478bae1ce6d 100644 --- a/lib/Sema/TypeCheckStmt.cpp +++ b/lib/Sema/TypeCheckStmt.cpp @@ -2671,18 +2671,28 @@ bool TypeCheckASTNodeAtLocRequest::evaluate( // function, we unfortunately need to type-check everything since we need to // apply the solution. // FIXME: We ought to see if we can do better in that case. - if (auto *CE = DC->getInnermostClosureForCaptures()) { - if (CE->getBodyState() == ClosureExpr::BodyState::Parsed) { - swift::typeCheckASTNodeAtLoc( - TypeCheckASTNodeAtLocContext::declContext(CE->getParent()), - CE->getLoc()); - - // If the context itself is a ClosureExpr, we should have type-checked - // the completion expression now. If it's a nested local declaration, - // fall through to type-check the AST node now that we've type-checked - // the surrounding closure. - if (isa(DC)) - return false; + // + // We don't need to do this for unattached nodes since we already would have + // type-checked the surrounding context, and unattached nodes cannot be + // typechecked via DeclContext since they aren't actually part of the AST. + if (!typeCheckCtx.isForUnattachedNode()) { + if (auto *CE = DC->getInnermostClosureForCaptures()) { + // Walk up to the parent-most closure. + while (auto *parent = dyn_cast_or_null(CE->getParent())) + CE = parent; + + if (CE->getBodyState() == ClosureExpr::BodyState::Parsed) { + swift::typeCheckASTNodeAtLoc( + TypeCheckASTNodeAtLocContext::declContext(CE->getParent()), + CE->getLoc()); + + // If the context itself is a ClosureExpr, we should have type-checked + // the completion expression now. If it's a nested local declaration, + // fall through to type-check the AST node now that we've type-checked + // the surrounding closure. + if (isa(DC)) + return false; + } } } diff --git a/validation-test/IDE/crashers/7b2825e90bfce47.swift b/validation-test/IDE/crashers_fixed/7b2825e90bfce47.swift similarity index 62% rename from validation-test/IDE/crashers/7b2825e90bfce47.swift rename to validation-test/IDE/crashers_fixed/7b2825e90bfce47.swift index 84df96813e0ed..0b015a60019e1 100644 --- a/validation-test/IDE/crashers/7b2825e90bfce47.swift +++ b/validation-test/IDE/crashers_fixed/7b2825e90bfce47.swift @@ -1,3 +1,3 @@ // {"kind":"complete","signature":"swift::SourceManager::findBufferContainingLocInternal(swift::SourceLoc) const","signatureAssert":"Assertion failed: (Loc.isValid()), function findBufferContainingLocInternal"} -// RUN: not --crash %target-swift-ide-test -code-completion -batch-code-completion -skip-filecheck -code-completion-diagnostics -source-filename %s +// RUN: %target-swift-ide-test -code-completion -batch-code-completion -skip-filecheck -code-completion-diagnostics -source-filename %s class a { lazy b: () = { answer {}#^^# diff --git a/validation-test/IDE/crashers/928f167118f85cf.swift b/validation-test/IDE/crashers_fixed/928f167118f85cf.swift similarity index 70% rename from validation-test/IDE/crashers/928f167118f85cf.swift rename to validation-test/IDE/crashers_fixed/928f167118f85cf.swift index dabd64b1a2404..5f9044dbcc58f 100644 --- a/validation-test/IDE/crashers/928f167118f85cf.swift +++ b/validation-test/IDE/crashers_fixed/928f167118f85cf.swift @@ -1,5 +1,5 @@ // {"kind":"complete","original":"7bbb1dc5","signature":"swift::TypeRepr::print(swift::ASTPrinter&, swift::PrintOptions const&, swift::optionset::OptionSet) const","signatureAssert":"Assertion failed: (false && \"Expression wasn't type checked?\"), function getTypeForCompletion"} -// RUN: not --crash %target-swift-ide-test -code-completion -batch-code-completion -skip-filecheck -code-completion-diagnostics -source-filename %s +// RUN: %target-swift-ide-test -code-completion -batch-code-completion -skip-filecheck -code-completion-diagnostics -source-filename %s a ? { init { b { extension }#^^#