Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inconsistent and uncompilable decompilation for a combination of null forgiving operator and extension method usage #3180

Open
coxantohi opened this issue Mar 19, 2024 · 0 comments
Labels
Bug Decompiler The decompiler engine itself

Comments

@coxantohi
Copy link

coxantohi commented Mar 19, 2024

Input code

public static class ExtensionMethods
{
    public static int Count(this System.Collections.IEnumerable source)
    {
        return 5;
    }
}
public class ProblemClass
{
    public void ProblemMethod()
    {
        IEnumerable<string> s = new List<string> { "ABC", "DEF" };
        int len = s != null ? Enumerable.Count(s) : 0;
    }
}

Decompiled code

`ExtensionMethods.cs'

using System.Collections;

public static class ExtensionMethods
{
	public static int Count(this IEnumerable source)
	{
		return 5;
	}
}

`ProblemClass.cs'

using System.Collections.Generic;
using System.Linq;

public class ProblemClass
{
	public void ProblemMethod()
	{
		int len = Enumerable.Count(new List<string> { "ABC", "DEF" }?) ?? 0;
	}
}

The decompiled code is not compilable.

When unchecking the decompiler option C# 6.0 / VS 2015: Decompile ?. and ?[] operators or the decompiler option C# 3.0 / VS 2008: Use extension method syntax the saved code in ProblemClass.cs will be compilable:

using System.Collections.Generic;
using System.Linq;

public class ProblemClass
{
	public void ProblemMethod()
	{
		IEnumerable<string> s = new List<string> { "ABC", "DEF" };
		int len = ((s != null) ? Enumerable.Count(s) : 0);
	}
}

Another manifestation of the error is that when switching the focus from the method to the class the decompiled code will change (gif):
ILSpy_Issue_3180

Details

  • Product in use: ILSpy version 8.2.0.7535
  • Version in use: .NET version 6.0.28-servicing.24120.7+34a109148c7d8a2c8e6431e83e4bce5712dd8083

ICSharpCode.Decompiler.CSharp.Resolver.CSharpResolver.GetAllExtensionMethods will return different results when focusing on the method in contrast with when focusing on the class.

When focusing on the method the ICSharpCode.Decompiler.CSharp.Transforms.IntroduceExtensionMethodsCanTransformToExtensionMethodCall method will return false because of the method.Equals(or.GetBestCandidateWithSubstitutedTypeArguments()) subcondition.

The issue was found when trying to decompile Microsoft.Graph.Communications.Common.
The extension method causing the confusion is Microsoft.Graph.Communications.Common.Validator.Any:

/// <summary>
/// Determines whether a sequence contains any elements.
/// </summary>
/// <param name="source">The <see cref="T:System.Collections.IEnumerable" /> to check for emptiness.</param>
/// <returns>
/// <see langword="true" /> if the source sequence contains any elements; otherwise, <see langword="false" />.
/// </returns>
public static bool Any(this IEnumerable source)
{
	if (source == null)
	{
		return false;
	}
	return source.Cast<object>()?.Any((object x) => x != null) == true;
}

The methods failing to be correctly decompiled are Microsoft.Graph.Communications.Common.Transport.HttpRequestMessageExtensions.ToGraphRequestAsync<T> and Microsoft.Graph.Communications.Common.Transport.HttpResponseMessageExtensions.ToGraphResponseAsync<T>.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug Decompiler The decompiler engine itself
Projects
None yet
Development

No branches or pull requests

1 participant