Skip to content

Conversation

@siegfriedpammer
Copy link
Member

No description provided.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors the entity-to-string conversion API by consolidating multiple entity-specific methods (MethodToString, FieldToString, PropertyToString, EventToString) into a unified EntityToString method. The refactoring introduces a new ILAmbience class that handles IL-style formatting of types and entities using ConversionFlags instead of multiple boolean parameters.

Key changes:

  • Introduced ILAmbience class with ConvertSymbol and ConvertType methods to handle formatting
  • Replaced boolean parameters in API methods with ConversionFlags enum for more granular control
  • Deprecated entity-specific methods in favor of unified EntityToString API
  • Migrated EscapeName utility methods from Language class to ILAmbience

Reviewed changes

Copilot reviewed 29 out of 29 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
ICSharpCode.Decompiler/IL/ILAmbience.cs New file implementing the core IL formatting logic with ConvertSymbol/ConvertType methods
ILSpy/Languages/Language.cs Updated TypeToString signature, added EntityToString, deprecated old entity-specific methods, removed TypeToStringVisitor inner class
ILSpy/Languages/CSharpLanguage.cs Removed overrides of deprecated methods, updated TypeToString and added EntityToString override
ICSharpCode.ILSpyX/Abstractions/ILanguage.cs Updated interface to replace deprecated methods with EntityToString
ILSpy/ViewModels/CompareViewModel.cs Updated entity formatting calls to use new EntityToString API with appropriate ConversionFlags
ILSpy/TreeNodes/*.cs Updated all tree node classes to use EntityToString with ConversionFlags instead of deprecated methods
ILSpy/Analyzers/TreeNodes/*.cs Updated analyzer tree nodes to use EntityToString with appropriate flags
ILSpy/Search/SearchResultFactory.cs Updated search result formatting to use new API
ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs Changed EnumNameCollection visibility to internal static, converted from class to struct, updated WriteEnum/WriteFlags signatures
ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpAmbience.cs Minor visibility and nullable annotation updates
ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj Added ILAmbience.cs to project
Comments suppressed due to low confidence (1)

ICSharpCode.Decompiler/Disassembler/ReflectionDisassembler.cs:2031

  • Changing EnumNameCollection from a class to a struct causes issues with the collection initializer syntax used for static readonly fields (lines 102, 118, 127, 136, 143, 1270, 1279, 1418, 1492). When using collection initializers like "new EnumNameCollection() { ... }", the Add method is called on the struct instance. However, since these are static readonly fields, the struct instance is readonly, and calling Add (which modifies the struct) will only modify a temporary copy, leaving the actual field empty. This will cause all enum/flag name lookups to fail silently.
		internal struct EnumNameCollection<T> : IEnumerable<KeyValuePair<long, string>> where T : struct
		{
			List<KeyValuePair<long, string>> names = new List<KeyValuePair<long, string>>();

			public EnumNameCollection()
			{
			}

			public void Add(T flag, string name)
			{
				this.names.Add(new KeyValuePair<long, string>(Convert.ToInt64(flag), name));
			}

			public IEnumerator<KeyValuePair<long, string>> GetEnumerator()
			{
				return names.GetEnumerator();
			}

			System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
			{
				return names.GetEnumerator();
			}
		}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 29 out of 29 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 29 out of 29 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

sealed class EnumNameCollection<T> : IEnumerable<KeyValuePair<long, string>> where T : struct
internal struct EnumNameCollection<T> : IEnumerable<KeyValuePair<long, string>> where T : struct
{
List<KeyValuePair<long, string>> names = new List<KeyValuePair<long, string>>();
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The field names is not initialized in the parameterless constructor at line 2013. In C#, struct field initializers are not executed for default struct construction. When EnumNameCollection instances are created using field initializers (lines 102, 118, 127, etc.), the parameterless constructor won't be called, and names will be null, causing NullReferenceException when Add() is called. The field should be initialized as List<KeyValuePair<long, string>> names = new(); or the struct should remain a class.

Copilot uses AI. Check for mistakes.
Comment on lines +139 to +143
if (string.IsNullOrEmpty(typeDef.Namespace))
{
writer.Write(typeDef.Namespace);
writer.Write('.');
}
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The condition at line 139 checks if (string.IsNullOrEmpty(typeDef.Namespace)), but then writes the namespace. This logic is inverted - it should check if (!string.IsNullOrEmpty(typeDef.Namespace)) to write the namespace only when it's not empty.

Copilot uses AI. Check for mistakes.
{
switch (owner)
{
case IType t:
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to t is useless, since its value is never read.

Suggested change
case IType t:
case IType:

Copilot uses AI. Check for mistakes.
case IType t:
writer.Write("`");
break;
case IMethod m:
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This assignment to m is useless, since its value is never read.

Suggested change
case IMethod m:
case IMethod:

Copilot uses AI. Check for mistakes.
Comment on lines +137 to +143
else if (ConversionFlags.HasFlag(ConversionFlags.UseFullyQualifiedTypeNames))
{
if (string.IsNullOrEmpty(typeDef.Namespace))
{
writer.Write(typeDef.Namespace);
writer.Write('.');
}
Copy link

Copilot AI Dec 14, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These 'if' statements can be combined.

Suggested change
else if (ConversionFlags.HasFlag(ConversionFlags.UseFullyQualifiedTypeNames))
{
if (string.IsNullOrEmpty(typeDef.Namespace))
{
writer.Write(typeDef.Namespace);
writer.Write('.');
}
else if (ConversionFlags.HasFlag(ConversionFlags.UseFullyQualifiedTypeNames) && string.IsNullOrEmpty(typeDef.Namespace))
{
writer.Write(typeDef.Namespace);
writer.Write('.');

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants