@ -58,12 +58,15 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -58,12 +58,15 @@ namespace ICSharpCode.Decompiler.CSharp
public bool AddNamesToPrimitiveValues ;
public bool UseImplicitlyTypedOut ;
public bool IsExpandedForm ;
public bool IsSetter ;
public int Length = > Arguments . Length ;
private int GetActualArgumentCount ( )
public int GetActualArgumentCount ( )
{
int count = IsSetter ? Arguments . Length - 1 : Arguments . Length ;
if ( FirstOptionalArgumentIndex < 0 )
return Arguments . Length ;
return count ;
Debug . Assert ( FirstOptionalArgumentIndex < = count ) ;
return FirstOptionalArgumentIndex ;
}
@ -88,10 +91,19 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -88,10 +91,19 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
// The names cover the full parameter list and have to stop where the arguments do.
int argumentCount = GetActualArgumentCount ( ) ;
if ( argumentNames ! = null & & argumentNames . Length > argumentCount )
{
var writtenNames = new string [ argumentCount ] ;
Array . Copy ( argumentNames , writtenNames , argumentCount ) ;
argumentNames = writtenNames ;
}
return argumentNames ;
}
public IList < ResolveResult > GetArgumentResolveResults ( int skipCount = 0 )
public ResolveResult [ ] GetArgumentResolveResults ( int skipCount = 0 )
{
var expectedParameters = ExpectedParameters ;
var useImplicitlyTypedOut = UseImplicitlyTypedOut ;
@ -132,7 +144,7 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -132,7 +144,7 @@ namespace ICSharpCode.Decompiler.CSharp
else
{
Debug . Assert ( skipCount = = 0 ) ;
return Arguments . Take ( argumentCount ) . Zip ( argumentNames . Take ( argumentCount ) ,
return Arguments . Take ( argumentCount ) . Zip ( argumentNames ,
( arg , name ) = > {
if ( name = = null )
return AddAnnotations ( arg . Expression ) ;
@ -500,11 +512,17 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -500,11 +512,17 @@ namespace ICSharpCode.Decompiler.CSharp
return result ;
}
int allowedParamCount = ( method . ReturnType . IsKnownType ( KnownTypeCode . Void ) ? 1 : 0 ) ;
if ( method . IsAccessor & & ( method . AccessorOwner . SymbolKind = = SymbolKind . Indexer | | argumentList . ExpectedParameters . Length = = allowedParamCount ) )
// IsSetter carries the answer for an accessor that takes the assigned value, including
// the argument order that keeps it last.
if ( argumentList . IsSetter | | ( ! TakesAssignedValueLast ( method ) & & IsWrittenAsMemberAccess ( method ) ) )
{
argumentList . CheckNoNamedOrOptionalArguments ( ) ;
return HandleAccessorCall ( expectedTargetDetails , method , target , argumentList . Arguments . ToList ( ) , argumentList . ArgumentNames ) ;
// Only an indexer access has an argument list to carry names or leave arguments out of.
if ( method . AccessorOwner ! . SymbolKind ! = SymbolKind . Indexer )
argumentList . CheckNoNamedOrOptionalArguments ( ) ;
// An access spells its index out anyway, and the ladder answers a name the member
// does not have with a cast of the target rather than by giving the name up.
argumentList . AddNamesToPrimitiveValues = false ;
return HandleAccessorCall ( expectedTargetDetails , method , target , argumentList ) ;
}
if ( IsDelegateEqualityComparison ( method , argumentList . Arguments ) )
@ -793,10 +811,15 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -793,10 +811,15 @@ namespace ICSharpCode.Decompiler.CSharp
callArguments . Add ( value ? ? new Nop ( ) ) ;
var argumentList = BuildArgumentList ( expectedTargetDetails , target , method , 1 , callArguments , null ) ;
// An index initializer is an assignment whatever the accessor looks like, even for a
// parameterized property, which has no access syntax of its own.
argumentList . IsSetter = true ;
// The cast the ladder would answer an unresolvable name with is removed again below,
// together with the target.
argumentList . AddNamesToPrimitiveValues = false ;
var unused = new IdentifierExpression ( "initializedObject" ) . WithRR ( target ) . WithoutILInstruction ( ) ;
var assignment = HandleAccessorCall ( expectedTargetDetails , method , unused ,
argumentList . Arguments . ToList ( ) , argumentList . ArgumentNames ) ;
var assignment = HandleAccessorCall ( expectedTargetDetails , method , unused , argumentList ) ;
if ( ( ( AssignmentExpression ) assignment ) . Left is IndexerExpression indexer & & indexer . Target is not null )
indexer . Target . Remove ( ) ;
@ -1013,6 +1036,17 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -1013,6 +1036,17 @@ namespace ICSharpCode.Decompiler.CSharp
// >= 0 - the index of the first argument that can be removed, because it is optional
// and is the default value of the parameter.
int firstOptionalArgumentIndex = expressionBuilder . settings . OptionalArguments ? - 2 : - 1 ;
// Only an accessor written as an access takes its assigned value out of the argument
// list; one written as a call passes it like any other argument. The scan below and
// GetActualArgumentCount() have to agree on that, so it is decided once, here.
bool writtenAsMemberAccess = IsWrittenAsMemberAccess ( method ) ;
bool isSetter = writtenAsMemberAccess & & TakesAssignedValueLast ( method ) ;
// A named argument of an indexer access names a parameter of the indexer, which the type
// system takes from the getter. The accessor being called may name the same parameters
// differently - C# cannot declare that, but other languages can.
IReadOnlyList < IParameter > namedParameters = method . AccessorOwner is IProperty { IsIndexer : true } indexer
? indexer . Parameters
: method . Parameters ;
for ( int i = firstParamIndex ; i < callArguments . Count ; i + + )
{
IParameter parameter ;
@ -1024,10 +1058,13 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -1024,10 +1058,13 @@ namespace ICSharpCode.Decompiler.CSharp
// assign names to that argument and all following arguments:
argumentNames = new string [ method . Parameters . Count ] ;
}
parameter = method . Parameters [ argumentToParameterMap [ i ] ] ;
if ( argumentNames ! = null & & AssignVariableNames . IsValidName ( parameter . Name ) )
int parameterIndex = argumentToParameterMap [ i ] ;
parameter = method . Parameters [ parameterIndex ] ;
// The assigned value is past the end of the indexer's parameters.
if ( argumentNames ! = null & & parameterIndex < namedParameters . Count
& & AssignVariableNames . IsValidName ( namedParameters [ parameterIndex ] . Name ) )
{
argumentNames [ arguments . Count ] = parameter . Name ;
argumentNames [ arguments . Count ] = namedParameters [ parameterIndex ] . Name ;
}
}
else
@ -1039,17 +1076,24 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -1039,17 +1076,24 @@ namespace ICSharpCode.Decompiler.CSharp
{
isPrimitiveValue . Set ( arguments . Count ) ;
}
if ( IsOptionalArgument ( parameter , arg ) )
// The assigned value of a setter is not part of the argument list, so it does not
// end the run of optional arguments either.
if ( ! ( isSetter & & i + 1 = = callArguments . Count ) )
{
if ( firstOptionalArgumentIndex = = - 2 )
firstOptionalArgumentIndex = i - firstParamIndex ;
}
else
{
if ( firstOptionalArgumentIndex ! = - 1 )
if ( IsOptionalArgument ( parameter , arg ) )
{
if ( firstOptionalArgumentIndex = = - 2 )
firstOptionalArgumentIndex = i - firstParamIndex ;
}
else if ( firstOptionalArgumentIndex ! = - 1 )
{
firstOptionalArgumentIndex = - 2 ;
}
}
if ( expressionBuilder . settings . ExpandParamsArguments & & parameter . IsParams & & i + 1 = = callArguments . Count & & argumentToParameterMap = = null )
// An assignment has no argument list to spread a parameter array over, and C#
// cannot declare a property whose value is one.
if ( expressionBuilder . settings . ExpandParamsArguments & & parameter . IsParams & & ! isSetter
& & i + 1 = = callArguments . Count & & argumentToParameterMap = = null )
{
// Parameter is marked params
// If the argument is an array creation, inline all elements into the call and add missing default values.
@ -1111,6 +1155,7 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -1111,6 +1155,7 @@ namespace ICSharpCode.Decompiler.CSharp
list . IsExpandedForm = isExpandedForm ;
list . IsPrimitiveValue = isPrimitiveValue ;
list . FirstOptionalArgumentIndex = firstOptionalArgumentIndex ;
list . IsSetter = isSetter ;
list . UseImplicitlyTypedOut = true ;
list . AddNamesToPrimitiveValues = expressionBuilder . settings . NamedArguments & & expressionBuilder . settings . NonTrailingNamedArguments ;
return list ;
@ -1133,8 +1178,7 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -1133,8 +1178,7 @@ namespace ICSharpCode.Decompiler.CSharp
expandedParameters . InsertRange ( 0 , expectedParameters ) ;
expandedArguments . InsertRange ( 0 , arguments ) ;
if ( IsUnambiguousCall ( expectedTargetDetails , method , targetResolveResult , Empty < IType > . Array ,
expandedArguments . SelectArray ( a = > a . ResolveResult ) , argumentNames : null ,
firstOptionalArgumentIndex : - 1 , out _ ,
expandedArguments . SelectArray ( a = > a . ResolveResult ) , argumentNames : null , out _ ,
out var bestCandidateIsExpandedForm ) = = OverloadResolutionErrors . None & & bestCandidateIsExpandedForm )
{
expectedParameters = expandedParameters ;
@ -1309,7 +1353,7 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -1309,7 +1353,7 @@ namespace ICSharpCode.Decompiler.CSharp
bool skipTargetCast = method . Accessibility < = Accessibility . Protected & & expressionBuilder . IsBaseTypeOfCurrentType ( method . DeclaringTypeDefinition ) ;
OverloadResolutionErrors errors ;
while ( ( errors = IsUnambiguousCall ( expectedTargetDetails , method , targetResolveResult , typeArguments ,
argumentList . GetArgumentResolveResults ( ) . ToArray ( ) , argumentList . GetArgumentNames ( ) , argumentList . FirstOptionalArgumentIndex , out foundMethod ,
argumentList . GetArgumentResolveResults ( ) . ToArray ( ) , argumentList . GetArgumentNames ( ) , out foundMethod ,
out var bestCandidateIsExpandedForm ) ) ! = OverloadResolutionErrors . None | | bestCandidateIsExpandedForm ! = argumentList . IsExpandedForm )
{
switch ( errors )
@ -1634,7 +1678,7 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -1634,7 +1678,7 @@ namespace ICSharpCode.Decompiler.CSharp
OverloadResolutionErrors IsUnambiguousCall ( ExpectedTargetDetails expectedTargetDetails , IMethod method ,
ResolveResult ? target , IType [ ] typeArguments , ResolveResult [ ] arguments ,
string [ ] ? argumentNames , int firstOptionalArgumentIndex ,
string [ ] ? argumentNames ,
out IParameterizedMember ? foundMember , out bool bestCandidateIsExpandedForm )
{
foundMember = null ;
@ -1645,10 +1689,6 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -1645,10 +1689,6 @@ namespace ICSharpCode.Decompiler.CSharp
Log . WriteLine ( "IsUnambiguousCall: Performing overload resolution for " + method ) ;
Log . WriteCollection ( " Arguments: " , arguments ) ;
argumentNames = firstOptionalArgumentIndex < 0 | | argumentNames = = null
? argumentNames
: argumentNames . Take ( firstOptionalArgumentIndex ) . ToArray ( ) ;
var or = new OverloadResolution ( resolver . Compilation ,
arguments , argumentNames , typeArguments ,
conversions : expressionBuilder . resolver . conversions ) ;
@ -1751,10 +1791,10 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -1751,10 +1791,10 @@ namespace ICSharpCode.Decompiler.CSharp
}
bool IsUnambiguousAccess ( ExpectedTargetDetails expectedTargetDetails , ResolveResult ? target , IMethod method ,
IList < TranslatedExpression > arguments , string [ ] ? argumentNames , [ NotNullWhen ( true ) ] out IMember ? foundMember )
IList < ResolveResult > arguments , string [ ] ? argumentNames , [ NotNullWhen ( true ) ] out IMember ? foundMember )
{
Log . WriteLine ( "IsUnambiguousAccess: Performing overload resolution for " + method ) ;
Log . WriteCollection ( " Arguments: " , arguments . Select ( a = > a . ResolveResult ) ) ;
Log . WriteCollection ( " Arguments: " , arguments ) ;
foundMember = null ;
if ( target = = null )
@ -1772,7 +1812,7 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -1772,7 +1812,7 @@ namespace ICSharpCode.Decompiler.CSharp
if ( method . AccessorOwner ! . SymbolKind = = SymbolKind . Indexer )
{
var or = new OverloadResolution ( resolver . Compilation ,
arguments . SelectArray ( a = > a . ResolveResult ) ,
arguments . ToArray ( ) ,
argumentNames : argumentNames ,
typeArguments : Empty < IType > . Array ,
conversions : expressionBuilder . resolver . conversions ) ;
@ -1797,8 +1837,32 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -1797,8 +1837,32 @@ namespace ICSharpCode.Decompiler.CSharp
return foundMember ! = null & & IsAppropriateCallTarget ( expectedTargetDetails , method . AccessorOwner , foundMember ) ;
}
/// <summary>
/// Whether the accessor's last parameter is the assigned value: a setter takes it, and so do
/// the two event accessors, written as += and -=.
/// </summary>
static bool TakesAssignedValueLast ( IMethod method )
{
return method . AccessorKind is System . Reflection . MethodSemanticsAttributes . Setter
or System . Reflection . MethodSemanticsAttributes . Adder
or System . Reflection . MethodSemanticsAttributes . Remover ;
}
/// <summary>
/// Whether the accessor is written as a property or indexer access. One with more parameters
/// than the access syntax has room for is written as a call, assigned value and all.
/// </summary>
static bool IsWrittenAsMemberAccess ( IMethod method )
{
if ( ! method . IsAccessor )
return false ;
if ( method . AccessorOwner ! . SymbolKind = = SymbolKind . Indexer )
return true ;
return method . Parameters . Count = = ( TakesAssignedValueLast ( method ) ? 1 : 0 ) ;
}
ExpressionWithResolveResult HandleAccessorCall ( ExpectedTargetDetails expectedTargetDetails , IMethod method ,
TranslatedExpression target , List < TranslatedExpression > arguments , string [ ] ? argumentNames )
TranslatedExpression target , ArgumentList argumentList )
{
bool requireTarget ;
if ( settings . AlwaysQualifyMemberReferences | | method . AccessorOwner ! . SymbolKind = = SymbolKind . Indexer | | expressionBuilder . HidesVariableWithName ( method . AccessorOwner . Name ) )
@ -1808,24 +1872,32 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -1808,24 +1872,32 @@ namespace ICSharpCode.Decompiler.CSharp
else
requireTarget = ! ( target . Expression is ThisReferenceExpression ) ;
bool targetCasted = false ;
bool isSetter = method . ReturnType . IsKnownType ( KnownTypeCode . Void ) ;
bool isSetter = argumentList . IsSetter ;
bool argumentsCasted = ( isSetter & & method . Parameters . Count = = 1 ) | | ( ! isSetter & & method . Parameters . Count = = 0 ) ;
var targetResolveResult = requireTarget ? target . ResolveResult : null ;
TranslatedExpression value = default ( TranslatedExpression ) ;
if ( isSett er)
// Dropping every argument would turn an indexer access into a property access.
if ( argumentList . FirstOptionalArgumentIndex = = 0 & & method . AccessorOwner ! . SymbolKind = = SymbolKind . Index er)
{
value = arguments . Last ( ) ;
arguments . Remove ( value ) ;
argumentList . FirstOptionalArgumentIndex = 1 ;
}
IMember ? foundMember ;
while ( ! IsUnambiguousAccess ( expectedTargetDetails , targetResolveResult , method , arguments , argumentNames , out foundMember ) )
while ( ! IsUnambiguousAccess ( expectedTargetDetails , targetResolveResult , method ,
argumentList . GetArgumentResolveResults ( ) , argumentList . GetArgumentNames ( ) , out foundMember ) )
{
if ( ! argumentsCasted )
if ( argumentList . FirstOptionalArgumentIndex > = 0 )
{
// Unlike the casts below, writing the omitted arguments out again cannot change
// what the access means, so try that first.
argumentList . FirstOptionalArgumentIndex = - 1 ;
}
else if ( ! argumentsCasted )
{
argumentsCasted = true ;
CastArguments ( arguments , method . Parameters . ToList ( ) ) ;
CastArguments (
new ArraySegment < TranslatedExpression > ( argumentList . Arguments , 0 , argumentList . GetActualArgumentCount ( ) ) ,
argumentList . ExpectedParameters ) ;
}
else if ( ! requireTarget )
{
@ -1845,6 +1917,10 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -1845,6 +1917,10 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
var arguments = argumentList . GetArgumentExpressions ( ) . ToList ( ) ;
// The assigned value is not one of the arguments the ladder casts, so nothing it could
// try makes an access resolve that fails over the value's type.
TranslatedExpression value = isSetter ? argumentList . Arguments [ argumentList . Length - 1 ] : default ;
var rr = new MemberResolveResult ( target . ResolveResult , foundMember ) ;
if ( isSetter )
@ -1853,7 +1929,7 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -1853,7 +1929,7 @@ namespace ICSharpCode.Decompiler.CSharp
if ( arguments . Count ! = 0 )
{
expr = new IndexerExpression ( target . ResolveResult is InitializedObjectResolveResult ? null : target . Expression , arguments . Select ( a = > a . Expression ) )
expr = new IndexerExpression ( target . ResolveResult is InitializedObjectResolveResult ? null : target . Expression , arguments )
. WithoutILInstruction ( ) . WithRR ( rr ) ;
}
else if ( requireTarget )
@ -1885,7 +1961,7 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -1885,7 +1961,7 @@ namespace ICSharpCode.Decompiler.CSharp
{
if ( arguments . Count ! = 0 )
{
return new IndexerExpression ( target . Expression , arguments . Select ( a = > a . Expression ) )
return new IndexerExpression ( target . Expression , arguments )
. WithoutILInstruction ( ) . WithRR ( rr ) ;
}
else if ( requireTarget )
@ -1967,7 +2043,7 @@ namespace ICSharpCode.Decompiler.CSharp
@@ -1967,7 +2043,7 @@ namespace ICSharpCode.Decompiler.CSharp
{
while ( IsUnambiguousCall ( expectedTargetDetails , method , null , Empty < IType > . Array ,
argumentList . GetArgumentResolveResults ( ) . ToArray ( ) ,
argumentList . GetArgumentNames ( ) , argumentList . FirstOptionalArgumentIndex , out _ ,
argumentList . GetArgumentNames ( ) , out _ ,
out var bestCandidateIsExpandedForm ) ! = OverloadResolutionErrors . None | | bestCandidateIsExpandedForm ! = argumentList . IsExpandedForm )
{
if ( argumentList . AddNamesToPrimitiveValues )