From 79a67fcee3747f93adbb1c362df6ded73a64e420 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Wed, 8 Jul 2026 08:38:43 +0200 Subject: [PATCH] Make synthesized dynamic members hover-only, not navigable A member synthesized for a dynamic access has no metadata token, so the navigation link it produced went nowhere. Emit it as a local-style reference (WriteLocalReference) instead: the hover renderer still shows the signature (it resolves any IEntity reference regardless of IsLocal), but the identifier is no longer a navigation target, matching how local variables are treated. The written text is unchanged. Assisted-by: Claude:claude-fable-5:Claude Code --- .../Output/TextTokenWriter.cs | 26 ++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs index 67cac8870..36cee1c7c 100644 --- a/ICSharpCode.Decompiler/Output/TextTokenWriter.cs +++ b/ICSharpCode.Decompiler/Output/TextTokenWriter.cs @@ -78,7 +78,16 @@ namespace ICSharpCode.Decompiler output.WriteReference(t, name, false); return; case IMember m: - output.WriteReference(m, name, false); + if (IsDynamicMemberReference(nodeStack.Peek())) + { + // A member synthesized for a dynamic access: show its signature on hover, but do not + // make it a navigation target (there is no real member to jump to) - like a local. + output.WriteLocalReference(name, m); + } + else + { + output.WriteReference(m, name, false); + } return; } @@ -140,6 +149,21 @@ namespace ICSharpCode.Decompiler return symbol; } + /// + /// True if the member reference at this node was synthesized for a dynamic member access/invocation. + /// Such members carry a hover tooltip but must not be navigation targets, since they do not exist in + /// metadata. + /// + static bool IsDynamicMemberReference(AstNode node) + { + if (node.Annotation() is CSharp.Resolver.DynamicMemberResolveResult) + return true; + if (node.Slot?.Kind == Slots.TargetExpression && node.Parent is InvocationExpression + && node.Parent.Annotation() is CSharp.Resolver.DynamicInvocationResolveResult) + return true; + return false; + } + object GetCurrentLocalReference() { AstNode node = nodeStack.Peek();