[clang-tools-extra] [clangd] Navigate go-to-definition through forwarding wrappers to the constructor (PR #199480)

Nathan Ridge via cfe-commits cfe-commits at lists.llvm.org
Tue Jun 9 07:03:58 PDT 2026


================
@@ -419,6 +419,44 @@ locateASTReferent(SourceLocation CurLoc, const syntax::Token *TouchedIdentifier,
           AST.getASTContext(), nameLocation(*Def, SM), MainFilePath);
   };
 
+  // Special case: if the cursor lands directly on a call expression (i.e.
+  // its enclosing SelectionTree node is the CallExpr itself, not the callee
+  // identifier or an argument), and the call invokes a forwarding wrapper
+  // such as `std::make_unique<T>(...)`, navigate to the constructor of `T`
+  // that the wrapper ultimately calls. This mirrors the existing
+  // constructor-call behaviour: `Abc^()` jumps to the constructor while
+  // `A^bc()` jumps to the type. The hook does not fire when the cursor is
+  // on the wrapper's identifier; that path continues to navigate to the
+  // wrapper itself via the candidate loop below.
+  {
+    unsigned Offset = SM.getDecomposedSpellingLoc(CurLoc).second;
----------------
HighCommander4 wrote:

> just wondering if you stumbled upon the code that actually does todays "`Abc^()` goes to constructor" behaviour, because I think looking at that might give some clues as to where we should put the new code (and maybe some inspirations about how to do so)

I believe the way it works is, given a constructor call like `Waldo(arg1, arg2)`,

 * the constructor expression node takes up the entire range, `Waldo(arg1, arg2)`
 * the type node for the type being constructed takes up the range `Waldo`
 * the argument expression nodes take up the ranges `arg1` and `arg2` respectively

Descendant nodes take precedence, so characters in the ranges `Waldo`, `arg1`, and `arg2` are mapped to the respective type of argument expression nodes, leaving the characters `(`, `,`, and `)` mapped to the constructor expression node itself.

For editors with between-character cursors, the character to the right of the cursor is checked first and preferred if it produces a valid target, and so this is how `Waldo^(arg1, arg2)`, `Waldo(arg1^, arg2)`, and `Waldo(arg1, arg2^)` end up targeting the constructor.

https://github.com/llvm/llvm-project/pull/199480


More information about the cfe-commits mailing list