Fix #3180: Spell out type arguments of a null-conditional extension call
When the extension method call cannot be written without type arguments (another extension method with the same name and a different arity is found first), the call falls back to the static form. For 'a?.M()' that fallback produced 'Enumerable.Count(s?)', which is not valid C#. Writing the type arguments excludes the extension methods of a different arity, so for a null-conditional target the type arguments are tried before giving up.
This commit is contained in:
1 parent
a6526a1dab
commit
8c8b8caad8
3 files changed
+52
-5
No files matched your search
@@ -932,6 +932,12 @@ namespace ICSharpCode.Decompiler.Tests
|
|||||||
await RunForLibrary(cscOptions: cscOptions);
|
await RunForLibrary(cscOptions: cscOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task Issue3180([ValueSource(nameof(roslynOnlyOptions))] CompilerOptions cscOptions)
|
||||||
|
{
|
||||||
|
await RunForLibrary(cscOptions: cscOptions);
|
||||||
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public async Task AssemblyCustomAttributes([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
|
public async Task AssemblyCustomAttributes([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue3180
|
||||||
|
{
|
||||||
|
internal static class Extensions
|
||||||
|
{
|
||||||
|
public static int Count(this IEnumerable source)
|
||||||
|
{
|
||||||
|
return 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class NullConditional
|
||||||
|
{
|
||||||
|
public static int StaticCall(IEnumerable<string> s)
|
||||||
|
{
|
||||||
|
return Enumerable.Count(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int CountOrZero(IEnumerable<string> s)
|
||||||
|
{
|
||||||
|
#if EXPECTED_OUTPUT
|
||||||
|
return s?.Count<string>() ?? 0;
|
||||||
|
#else
|
||||||
|
return (s != null) ? Enumerable.Count(s) : 0;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1318,8 +1318,8 @@ namespace ICSharpCode.Decompiler.CSharp
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
var firstArgument = argumentList.Arguments[0];
|
var firstArgument = argumentList.Arguments[0];
|
||||||
bool writeTypeArguments = (transform & ReferenceTransformation.RequireTypeArguments) != 0
|
bool canWriteTypeArguments = !settings.AnonymousTypes || !method.TypeArguments.Any(a => a.ContainsAnonymousType());
|
||||||
&& (!settings.AnonymousTypes || !method.TypeArguments.Any(a => a.ContainsAnonymousType()));
|
bool writeTypeArguments = (transform & ReferenceTransformation.RequireTypeArguments) != 0 && canWriteTypeArguments;
|
||||||
IType[] typeArguments = writeTypeArguments ? method.TypeArguments.ToArray() : Empty<IType>.Array;
|
IType[] typeArguments = writeTypeArguments ? method.TypeArguments.ToArray() : Empty<IType>.Array;
|
||||||
|
|
||||||
var directionExpression = firstArgument.Expression as DirectionExpression;
|
var directionExpression = firstArgument.Expression as DirectionExpression;
|
||||||
@@ -1342,10 +1342,20 @@ namespace ICSharpCode.Decompiler.CSharp
|
|||||||
string[]? remainingNames = argumentNames?.Take(actualArgumentCount).Skip(1).ToArray();
|
string[]? remainingNames = argumentNames?.Take(actualArgumentCount).Skip(1).ToArray();
|
||||||
if (remainingNames != null && remainingNames.All(name => name == null))
|
if (remainingNames != null && remainingNames.All(name => name == null))
|
||||||
remainingNames = null;
|
remainingNames = null;
|
||||||
if (!resolver.CanTransformToExtensionMethodCall(method, typeArguments, target,
|
var remainingArguments = argumentList.GetArgumentResolveResults(skipCount: 1).ToArray();
|
||||||
argumentList.GetArgumentResolveResults(skipCount: 1).ToArray(), remainingNames))
|
if (!resolver.CanTransformToExtensionMethodCall(method, typeArguments, target, remainingArguments, remainingNames))
|
||||||
{
|
{
|
||||||
return false;
|
// 'a?.M()' has no static call form to fall back to. Spelled-out type arguments rule out
|
||||||
|
// extension methods of a different arity, which NullPropagationTransform relies on.
|
||||||
|
if (writeTypeArguments || !canWriteTypeArguments || method.TypeArguments.Count == 0
|
||||||
|
|| !IsNullConditional(firstArgument.Expression))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
writeTypeArguments = true;
|
||||||
|
typeArguments = method.TypeArguments.ToArray();
|
||||||
|
if (!resolver.CanTransformToExtensionMethodCall(method, typeArguments, target, remainingArguments, remainingNames))
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Expression targetExpression;
|
Expression targetExpression;
|
||||||
|
|||||||
Reference in new issue
Block a user