Browse Source

Fix anonymous-type lambda early-return emitting unresolvable cast (#3752)

* Fix anonymous-type lambda early-return emitting unresolvable cast

When a lambda's inferred return type contains an anonymous type and one
branch returns null, the decompiler emitted an explicit cast such as
`return (IEnumerable<<>f__AnonymousType0<int>>)null;`, which is invalid C#.

Skip the cast in IsPossibleLossOfTypeInformation for null literals whenever
the expected type contains an anonymous type:
null is implicitly convertible to any reference type, so no cast is needed,
and the anonymous type has no nameable form to cast to anyway.

Fixes #3751
pull/3763/head
Jakob Hellermann 4 weeks ago committed by GitHub
parent
commit
3c062bad0c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 6
      ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
  2. 28
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3751.cs
  3. 28
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

6
ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs

@ -936,6 +936,12 @@ namespace ICSharpCode.Decompiler.Tests @@ -936,6 +936,12 @@ namespace ICSharpCode.Decompiler.Tests
await RunForLibrary(cscOptions: cscOptions);
}
[Test]
public async Task Issue3751([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{
await RunForLibrary(cscOptions: cscOptions);
}
async Task RunForLibrary([CallerMemberName] string testName = null, AssemblerOptions asmOptions = AssemblerOptions.None, CompilerOptions cscOptions = CompilerOptions.None, Action<DecompilerSettings> configureDecompiler = null)
{
await Run(testName, asmOptions | AssemblerOptions.Library, cscOptions | CompilerOptions.Library, configureDecompiler);

28
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3751.cs

@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
using System;
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
internal class Issue3751
{
private static bool Cond;
private static T Infer<T>(Func<T> factory)
{
return factory();
}
public object Trigger()
{
return Infer(delegate {
if (Cond)
{
Console.WriteLine();
return null;
}
return new {
Value = 1
};
});
}
}
}

28
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -382,6 +382,21 @@ namespace ICSharpCode.Decompiler.CSharp @@ -382,6 +382,21 @@ namespace ICSharpCode.Decompiler.CSharp
.WithRR(new ConversionResolveResult(currentResultType, expr.ResolveResult, Conversion.IdentityConversion)).WithoutILInstruction();
}
return new ReturnStatement(expr).WithILInstruction(inst);
static bool IsPossibleLossOfTypeInformation(IType givenType, IType expectedType)
{
if (expectedType.ContainsAnonymousType())
return false;
if (NormalizeTypeVisitor.IgnoreNullability.EquivalentTypes(givenType, expectedType))
return false;
if (expectedType is TupleType { ElementNames.IsEmpty: false })
return true;
if (expectedType == SpecialType.Dynamic)
return true;
if (givenType == SpecialType.NullType)
return true;
return false;
}
}
else
return new ReturnStatement().WithILInstruction(inst);
@ -403,19 +418,6 @@ namespace ICSharpCode.Decompiler.CSharp @@ -403,19 +418,6 @@ namespace ICSharpCode.Decompiler.CSharp
return new GotoStatement(label).WithILInstruction(inst);
}
private bool IsPossibleLossOfTypeInformation(IType givenType, IType expectedType)
{
if (NormalizeTypeVisitor.IgnoreNullability.EquivalentTypes(givenType, expectedType))
return false;
if (expectedType is TupleType { ElementNames.IsEmpty: false })
return true;
if (expectedType == SpecialType.Dynamic)
return true;
if (givenType == SpecialType.NullType)
return true;
return false;
}
protected internal override TranslatedStatement VisitThrow(Throw inst)
{
return new ThrowStatement(exprBuilder.Translate(inst.Argument)).WithILInstruction(inst);

Loading…
Cancel
Save