Browse Source

Read the full namespace path off tree namespace nodes, not the label

In nested-namespace mode a NamespaceTreeNode's display label is only its
last segment ("Generic"), while the full dotted path
("System.Collections.Generic") is what identifies the namespace in
metadata and to the docs site. Three call sites read the label where they
need the full path, so in nested mode each targets the wrong namespace:
decompiling a namespace node queries an empty one and titles the output
after the last segment; the MSDN URL points at the wrong page; and
scope-search-to-namespace scopes to the wrong name.

These are ds5678's fixes from #3879, reintegrated on top of the eager
namespace rebuild. #3879's other Name -> FullName corrections, in
AssemblyTreeNode.FindNamespaceNode and TreeNodeLocator, are already
covered here by the full-namespace-name and type-handle indexes, so only
the Decompile and search-entry cases carry over.

Assisted-by: Claude:claude-opus-4-8:Claude Code
pull/3886/head
ds5678 2 months ago committed by Siegfried Pammer
parent
commit
c40c299ff8
  1. 32
      ILSpy.Tests/AssemblyList/NestedNamespaceTreeTests.cs
  2. 6
      ILSpy/Commands/SearchMsdnContextMenuEntry.cs
  3. 2
      ILSpy/Search/ScopeSearchToNamespaceContextMenuEntry.cs
  4. 4
      ILSpy/TreeNodes/NamespaceTreeNode.cs

32
ILSpy.Tests/AssemblyList/NestedNamespaceTreeTests.cs

@ -22,12 +22,14 @@ using System.Threading.Tasks; @@ -22,12 +22,14 @@ using System.Threading.Tasks;
using Avalonia.Headless.NUnit;
using ICSharpCode.Decompiler;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.ILSpyX;
using ICSharpCode.ILSpy;
using ICSharpCode.ILSpy.AppEnv;
using ICSharpCode.ILSpy.AssemblyTree;
using ICSharpCode.ILSpy.Languages;
using ICSharpCode.ILSpy.TreeNodes;
using NUnit.Framework;
@ -143,6 +145,36 @@ public class NestedNamespaceTreeTests @@ -143,6 +145,36 @@ public class NestedNamespaceTreeTests
}
}
[AvaloniaTest]
public async Task Decompiling_A_Nested_Namespace_Uses_Its_Full_Path_Not_The_Display_Label()
{
// In nested mode a NamespaceTreeNode's display label is only its last segment ("Generic"),
// while the metadata it has to query -- and the name it titles the output with -- is the full
// path ("System.Collections.Generic"). Decompiling by the label queries the wrong namespace,
// usually an empty one, and titles the output after the wrong name.
try
{
var (_, assemblyNode) = await BootNestedAsync(TreeNavigation.CoreLibName);
var generic = assemblyNode.Children.OfType<NamespaceTreeNode>()
.SelectMany(DescendantNamespaces)
.Single(ns => ns.FullName == "System.Collections.Generic");
var language = AppComposition.Current.GetExport<LanguageService>()
.Languages.OfType<CSharpLanguage>().First();
var output = new PlainTextOutput();
generic.Decompile(language, output, new DecompilationOptions(new DecompilerSettings()));
Assert.That(output.ToString(), Does.Contain("System.Collections.Generic"),
"the namespace is decompiled and titled by its full path, not the 'Generic' display label");
}
finally
{
ResetNestedMode();
}
}
[AvaloniaTest]
public async Task FindNamespaceNode_Resolves_A_Nested_Namespace()
{

6
ILSpy/Commands/SearchMsdnContextMenuEntry.cs

@ -72,8 +72,8 @@ namespace ICSharpCode.ILSpy.Commands @@ -72,8 +72,8 @@ namespace ICSharpCode.ILSpy.Commands
{
switch (node)
{
case NamespaceTreeNode ns when !string.IsNullOrEmpty(ns.Name):
return (MsdnPrefix + ns.Name).ToLowerInvariant();
case NamespaceTreeNode ns when !string.IsNullOrEmpty(ns.FullName):
return (MsdnPrefix + ns.FullName).ToLowerInvariant();
case IMemberTreeNode m when m.Member is { } entity:
// Reflection-name → docs path: backticks (generic arity) become hyphens,
// `+` (nested type) becomes `.`, and ".ctor" gets a "-ctor" tail because
@ -96,7 +96,7 @@ namespace ICSharpCode.ILSpy.Commands @@ -96,7 +96,7 @@ namespace ICSharpCode.ILSpy.Commands
switch (node)
{
case NamespaceTreeNode ns:
return !string.IsNullOrEmpty(ns.Name);
return !string.IsNullOrEmpty(ns.FullName);
case IMemberTreeNode m when m.Member is { } entity:
if (!IsExternallyVisible(entity.Accessibility))
return false;

2
ILSpy/Search/ScopeSearchToNamespaceContextMenuEntry.cs

@ -60,7 +60,7 @@ namespace ICSharpCode.ILSpy.Search @@ -60,7 +60,7 @@ namespace ICSharpCode.ILSpy.Search
static string? Namespace(TextViewContext context)
{
if (context.SelectedTreeNodes is { Length: > 0 } nodes && nodes.All(n => n is NamespaceTreeNode))
return nodes.OfType<NamespaceTreeNode>().FirstOrDefault()?.Name;
return nodes.OfType<NamespaceTreeNode>().FirstOrDefault()?.FullName;
return (context.Reference?.Reference as IEntity)?.Namespace;
}
}

4
ILSpy/TreeNodes/NamespaceTreeNode.cs

@ -96,8 +96,8 @@ namespace ICSharpCode.ILSpy.TreeNodes @@ -96,8 +96,8 @@ namespace ICSharpCode.ILSpy.TreeNodes
return;
}
var types = typeSystem.MainModule.TypeDefinitions
.Where(t => t.Namespace == name && t.DeclaringTypeDefinition == null);
language.DecompileNamespace(name, types, output, options);
.Where(t => t.Namespace == fullName && t.DeclaringTypeDefinition == null);
language.DecompileNamespace(fullName, types, output, options);
}
// A namespace counts as public-API iff at least one type it contains is public-API. The

Loading…
Cancel
Save