Browse Source

Fix #3952: recognize VB-generated names as anonymous types

Two predicates disagreed on what a generated name looks like. At the metadata
level a '$' in the name counts, so MemberIsHidden treated VB$AnonymousType_0
as an anonymous type and dropped its definition from the output. At the type
system level only '<' counted, so none of the anonymous-type translations in
CallBuilder and ExpressionBuilder fired. VB assemblies therefore lost the
definitions and kept the raw metadata names at every use site, which is not
valid C#.

Both levels now share one predicate and cannot drift apart again. It keeps the
metadata-level behaviour exactly: counting every name that merely contains
'<' would newly capture explicit implementations of generic interface members.

Assisted-by: Claude:claude-fable-5:Claude Code
pull/3964/head
Siegfried Pammer 1 month ago committed by Siegfried Pammer
parent
commit
059a579e27
  1. 2
      ICSharpCode.Decompiler/NRExtensions.cs
  2. 17
      ICSharpCode.Decompiler/SRMExtensions.cs

2
ICSharpCode.Decompiler/NRExtensions.cs

@ -50,7 +50,7 @@ namespace ICSharpCode.Decompiler @@ -50,7 +50,7 @@ namespace ICSharpCode.Decompiler
public static bool HasGeneratedName(this IType type)
{
return type.Name.StartsWith("<", StringComparison.Ordinal) || type.Name.Contains("<");
return SRMExtensions.IsGeneratedName(type.Name);
}
public static bool IsAnonymousType(this IType type)

17
ICSharpCode.Decompiler/SRMExtensions.cs

@ -489,9 +489,20 @@ namespace ICSharpCode.Decompiler @@ -489,9 +489,20 @@ namespace ICSharpCode.Decompiler
public static bool IsGeneratedName(this StringHandle handle, MetadataReader metadata)
{
return !handle.IsNil
&& (metadata.GetString(handle).StartsWith("<", StringComparison.Ordinal)
|| metadata.GetString(handle).Contains("$"));
return !handle.IsNil && IsGeneratedName(metadata.GetString(handle));
}
/// <summary>
/// Detects the mangled names compilers give to entities that have no user-written
/// declaration. The C# compiler prefixes them with '&lt;', the VB compiler separates
/// the parts with '$' (VB$AnonymousType_0, VB$StateMachine_1_Foo). Neither character
/// is legal in a C# or VB identifier.
/// Note that a name may legitimately contain '&lt;' without being generated: explicit
/// implementations of generic interface members are named after the interface.
/// </summary>
internal static bool IsGeneratedName(string name)
{
return name.StartsWith("<", StringComparison.Ordinal) || name.Contains("$");
}
public static bool HasGeneratedName(this MethodDefinitionHandle handle, MetadataReader metadata)

Loading…
Cancel
Save