From 41d04933a30e836fca5d63ac1a04bbe6b51b720f Mon Sep 17 00:00:00 2001 From: Ian Leitch Date: Thu, 18 Dec 2025 21:07:16 +0100 Subject: [PATCH] [Index] Fix missing constructor reference for Self.NestedType() calls Constructors called via Self.NestedType() weren't being indexed because the AST uses DotSyntaxCallExpr (not ConstructorRefCallExpr), causing the implicit DeclRefExpr to the constructor to be skipped. Fix by: 1. Not skipping implicit constructor DeclRefExpr when CtorRefs is empty (i.e., when not already handled by ConstructorRefCallExpr) 2. Unwrapping AutoClosureExpr/CallExpr in isBeingCalled() to correctly detect the call context and add the Call role --- lib/IDE/SourceEntityWalker.cpp | 15 ++++++++++++++- lib/IDE/Utils.cpp | 17 ++++++++++++++++- test/Index/expressions.swift | 17 +++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/lib/IDE/SourceEntityWalker.cpp b/lib/IDE/SourceEntityWalker.cpp index 40dacd93193bc..a3d324bf44f09 100644 --- a/lib/IDE/SourceEntityWalker.cpp +++ b/lib/IDE/SourceEntityWalker.cpp @@ -362,11 +362,24 @@ ASTWalker::PreWalkResult SemaAnnotator::walkToExprPre(Expr *E) { } } + // Check if this is an implicit DeclRefExpr to a constructor - we should + // NOT skip these as they need to be indexed for Self.NestedType() calls. + // However, only do this when we're NOT inside a ConstructorRefCallExpr + // (tracked by CtorRefs), as that case is already handled and we'd create + // duplicate references. + bool isImplicitCtorRef = false; + if (CtorRefs.empty()) { + if (auto *DRE = dyn_cast(E)) { + if (isa(DRE->getDecl())) + isImplicitCtorRef = true; + } + } + if (!isa(E) && !isa(E) && !isa(E) && !isa(E) && !isa(E) && !isa(E) && !isa(E) && !isa(E) && !isa(E) && - !isa(E) && E->isImplicit()) + !isa(E) && E->isImplicit() && !isImplicitCtorRef) return Action::Continue(E); if (auto LE = dyn_cast(E)) { diff --git a/lib/IDE/Utils.cpp b/lib/IDE/Utils.cpp index 9e6721b5f9c5d..b7a7b1f084b48 100644 --- a/lib/IDE/Utils.cpp +++ b/lib/IDE/Utils.cpp @@ -963,7 +963,22 @@ bool swift::ide::isBeingCalled(ArrayRef ExprStack) { } if (isa(AE)) continue; - if (getReferencedDecl(AE->getFn()).second == UnderlyingDecl) + Expr *Fn = AE->getFn(); + // Unwrap AutoClosureExpr and nested CallExpr to get the actual function + // expression. This handles cases like Self.NestedType() where the + // constructor call is wrapped in an autoclosure containing another call. + while (true) { + if (auto *ACE = dyn_cast(Fn)) { + Fn = ACE->getSingleExpressionBody(); + continue; + } + if (auto *InnerCall = dyn_cast(Fn)) { + Fn = InnerCall->getFn(); + continue; + } + break; + } + if (getReferencedDecl(Fn).second == UnderlyingDecl) return true; } return false; diff --git a/test/Index/expressions.swift b/test/Index/expressions.swift index 9d92a191757f7..cca28f55d0de1 100644 --- a/test/Index/expressions.swift +++ b/test/Index/expressions.swift @@ -57,3 +57,20 @@ func castExpr(x: Any) { // CHECK: [[@LINE+1]]:15 | struct/Swift | S1 | [[S1_USR]] | Ref _ = x as? S1 } + +// Test that initializers are indexed when called through Self.NestedType +// CHECK: [[@LINE+1]]:7 | class(internal)/Swift | Container | [[Container_USR:.*]] | Def +class Container { + // CHECK: [[@LINE+1]]:11 | class(internal)/Swift | NestedType | [[NestedType_USR:.*]] | Def + class NestedType { + // CHECK: [[@LINE+1]]:9 | constructor(internal)/Swift | init(value:) | [[NestedType_init_USR:.*]] | Def + init(value: Int) {} + } + + func someFunc() { + // CHECK: [[@LINE+3]]:13 | class/Swift | Container | [[Container_USR]] | Ref + // CHECK: [[@LINE+2]]:18 | class/Swift | NestedType | [[NestedType_USR]] | Ref + // CHECK: [[@LINE+1]]:18 | constructor/Swift | init(value:) | [[NestedType_init_USR]] | Ref,Call + _ = Self.NestedType(value: 1) + } +}