Browse Source

Fix #2140: ILSpy 6.2p1 no longer respects "use discards" setting

pull/2145/head
Siegfried Pammer 5 years ago
parent
commit
04f9603768
  1. 35
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/Discards.cs
  2. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs
  3. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs
  4. 4
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/OutVariables.cs
  5. 2
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/ThrowExpressions.cs
  6. 8
      ICSharpCode.Decompiler/CSharp/Annotations.cs
  7. 98
      ICSharpCode.Decompiler/CSharp/CallBuilder.cs
  8. 23
      ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
  9. 2
      ICSharpCode.Decompiler/Semantics/OutVarResolveResult.cs

35
ICSharpCode.Decompiler.Tests/TestCases/Pretty/Discards.cs

@ -14,6 +14,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -14,6 +14,16 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
value = 0;
}
public void GetOutOverloaded(out int value)
{
value = 0;
}
public void GetOutOverloaded(out string value)
{
value = "Hello World";
}
public void MakeValue(Func<object, string, int> func)
{
@ -28,16 +38,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -28,16 +38,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
}
// out _ is currently not supported: the test cases below are not useful
//public void ParameterHiddenByLocal(@_ _)
//{
// GetOut(out int value);
//}
public void ParameterHiddenByLocal(@_ _)
{
GetOut(out var _);
}
//public void DiscardedOutVsLambdaParameter()
//{
// GetOut(out int value);
// MakeValue((@_ _) => 5);
//}
public void DiscardedOutVsLambdaParameter()
{
GetOut(out var _);
MakeValue((@_ _) => 5);
}
public void ExplicitlyTypedDiscard()
{
GetOutOverloaded(out string _);
GetOutOverloaded(out int _);
}
}
}

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/DynamicTests.cs

@ -82,7 +82,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -82,7 +82,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
private static void RefCallSiteTests()
{
#if CS70
CallWithOut(out dynamic d);
CallWithOut(out var d);
CallWithIn(in d);
#else
dynamic d;

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs

@ -1040,7 +1040,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -1040,7 +1040,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
await Task.Delay(100);
#if CS70
if (string.IsNullOrEmpty(str) && int.TryParse(str, out int id))
if (string.IsNullOrEmpty(str) && int.TryParse(str, out var id))
{
#else
int id;

4
ICSharpCode.Decompiler.Tests/TestCases/Pretty/OutVariables.cs

@ -25,7 +25,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -25,7 +25,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
public static void OutVarInShortCircuit(Dictionary<int, string> d)
{
if (d.Count > 2 && d.TryGetValue(42, out string value))
if (d.Count > 2 && d.TryGetValue(42, out var value))
{
Console.WriteLine(value);
}
@ -35,7 +35,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -35,7 +35,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
{
// Note: needs reasoning about "definitely assigned if true"
// to ensure that the value is initialized when the delegate is declared.
if (d.Count > 2 && d.TryGetValue(42, out string value))
if (d.Count > 2 && d.TryGetValue(42, out var value))
{
return delegate {
Console.WriteLine(value);

2
ICSharpCode.Decompiler.Tests/TestCases/Pretty/ThrowExpressions.cs

@ -32,7 +32,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -32,7 +32,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
private static int? GetIntOrNull(string v)
{
if (int.TryParse(v, out int result))
if (int.TryParse(v, out var result))
{
return result;
}

8
ICSharpCode.Decompiler/CSharp/Annotations.cs

@ -345,4 +345,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -345,4 +345,12 @@ namespace ICSharpCode.Decompiler.CSharp
this.EqualsLambda = equals;
}
}
/// <summary>
/// Annotates an out DirectionExpression if the out variable can be declared implicitly typed.
/// </summary>
public class UseImplicitlyTypedOutAnnotation
{
public static readonly UseImplicitlyTypedOutAnnotation Instance = new UseImplicitlyTypedOutAnnotation();
}
}

98
ICSharpCode.Decompiler/CSharp/CallBuilder.cs

@ -52,6 +52,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -52,6 +52,7 @@ namespace ICSharpCode.Decompiler.CSharp
public IReadOnlyList<int> ArgumentToParameterMap;
public bool AddNamesToPrimitiveValues;
public bool UseImplicitlyTypedOut;
public bool IsExpandedForm;
public int Length => Arguments.Length;
@ -62,9 +63,33 @@ namespace ICSharpCode.Decompiler.CSharp @@ -62,9 +63,33 @@ namespace ICSharpCode.Decompiler.CSharp
return FirstOptionalArgumentIndex;
}
public IEnumerable<ResolveResult> GetArgumentResolveResults(int skipCount = 0)
public IList<ResolveResult> GetArgumentResolveResults(int skipCount = 0)
{
return Arguments.Skip(skipCount).Take(GetActualArgumentCount()).Select(a => a.ResolveResult);
var expectedParameters = ExpectedParameters;
var useImplicitlyTypedOut = UseImplicitlyTypedOut;
return Arguments
.SelectWithIndex(GetResolveResult)
.Skip(skipCount)
.Take(GetActualArgumentCount())
.ToArray();
ResolveResult GetResolveResult(int index, TranslatedExpression expression)
{
var param = expectedParameters[index];
if (useImplicitlyTypedOut && param.IsOut)
return OutVarResolveResult.Instance;
return expression.ResolveResult;
}
}
public IList<ResolveResult> GetArgumentResolveResultsDirect(int skipCount = 0)
{
return Arguments
.Skip(skipCount)
.Take(GetActualArgumentCount())
.Select(a => a.ResolveResult)
.ToArray();
}
public IEnumerable<Expression> GetArgumentExpressions(int skipCount = 0)
@ -87,9 +112,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -87,9 +112,10 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
int argumentCount = GetActualArgumentCount();
var useImplicitlyTypedOut = UseImplicitlyTypedOut;
if (ArgumentNames == null)
{
return Arguments.Skip(skipCount).Take(argumentCount).Select(arg => arg.Expression);
return Arguments.Skip(skipCount).Take(argumentCount).Select(arg => AddAnnotations(arg.Expression));
}
else
{
@ -97,11 +123,23 @@ namespace ICSharpCode.Decompiler.CSharp @@ -97,11 +123,23 @@ namespace ICSharpCode.Decompiler.CSharp
return Arguments.Take(argumentCount).Zip(ArgumentNames.Take(argumentCount),
(arg, name) => {
if (name == null)
return arg.Expression;
return AddAnnotations(arg.Expression);
else
return new NamedArgumentExpression(name, arg);
return new NamedArgumentExpression(name, AddAnnotations(arg.Expression));
});
}
Expression AddAnnotations(Expression expression)
{
if (!useImplicitlyTypedOut)
return expression;
if (expression.GetResolveResult() is ByReferenceResolveResult brrr
&& brrr.IsOut)
{
expression.AddAnnotation(UseImplicitlyTypedOutAnnotation.Instance);
}
return expression;
}
}
public bool CanInferAnonymousTypePropertyNamesFromArguments()
@ -255,13 +293,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -255,13 +293,14 @@ namespace ICSharpCode.Decompiler.CSharp
{
return new InvocationExpression(target, argumentList.GetArgumentExpressions())
.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method,
argumentList.GetArgumentResolveResults().ToList(), isExpandedForm: argumentList.IsExpandedForm));
argumentList.GetArgumentResolveResults(), isExpandedForm: argumentList.IsExpandedForm));
}
if (method is VarArgInstanceMethod)
{
argumentList.FirstOptionalArgumentIndex = -1;
argumentList.AddNamesToPrimitiveValues = false;
argumentList.UseImplicitlyTypedOut = false;
int regularParameterCount = ((VarArgInstanceMethod)method).RegularParameterCount;
var argListArg = new UndocumentedExpression();
argListArg.UndocumentedExpressionType = UndocumentedExpressionType.ArgList;
@ -293,7 +332,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -293,7 +332,7 @@ namespace ICSharpCode.Decompiler.CSharp
{
return new InvocationExpression(target, argumentList.GetArgumentExpressions())
.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method,
argumentList.GetArgumentResolveResults().ToList(), isExpandedForm: argumentList.IsExpandedForm, isDelegateInvocation: true));
argumentList.GetArgumentResolveResults(), isExpandedForm: argumentList.IsExpandedForm, isDelegateInvocation: true));
}
if (settings.StringInterpolation && IsInterpolatedStringCreation(method, argumentList) &&
@ -340,7 +379,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -340,7 +379,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
var formattableStringType = expressionBuilder.compilation.FindType(KnownTypeCode.FormattableString);
var isrr = new InterpolatedStringResolveResult(expressionBuilder.compilation.FindType(KnownTypeCode.String),
format, argumentList.GetArgumentResolveResults().Skip(1).ToArray());
format, argumentList.GetArgumentResolveResults(1).ToArray());
var expr = new InterpolatedStringExpression();
expr.Content.AddRange(content);
if (method.Name == "Format")
@ -363,7 +402,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -363,7 +402,7 @@ namespace ICSharpCode.Decompiler.CSharp
argumentList.CheckNoNamedOrOptionalArguments();
return HandleDelegateEqualityComparison(method, argumentList.Arguments)
.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, method,
argumentList.GetArgumentResolveResults().ToList(), isExpandedForm: argumentList.IsExpandedForm));
argumentList.GetArgumentResolveResults(), isExpandedForm: argumentList.IsExpandedForm));
}
if (method.IsOperator && method.Name == "op_Implicit" && argumentList.Length == 1)
@ -420,7 +459,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -420,7 +459,7 @@ namespace ICSharpCode.Decompiler.CSharp
typeArgumentList.AddRange(method.TypeArguments.Select(expressionBuilder.ConvertType));
return new InvocationExpression(targetExpr, argumentList.GetArgumentExpressions())
.WithRR(new CSharpInvocationResolveResult(target.ResolveResult, foundMethod,
argumentList.GetArgumentResolveResults().ToList(), isExpandedForm: argumentList.IsExpandedForm));
argumentList.GetArgumentResolveResultsDirect(), isExpandedForm: argumentList.IsExpandedForm));
}
/// <summary>
@ -457,6 +496,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -457,6 +496,7 @@ namespace ICSharpCode.Decompiler.CSharp
firstParamIndex: 0, args, null);
argumentList.ArgumentNames = null;
argumentList.AddNamesToPrimitiveValues = false;
argumentList.UseImplicitlyTypedOut = false;
var transform = GetRequiredTransformationsForCall(expectedTargetDetails, method, ref unused,
ref argumentList, CallTransformation.None, out _);
Debug.Assert(transform == CallTransformation.None || transform == CallTransformation.NoOptionalArgumentAllowed);
@ -755,6 +795,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -755,6 +795,7 @@ namespace ICSharpCode.Decompiler.CSharp
list.IsExpandedForm = isExpandedForm;
list.IsPrimitiveValue = isPrimitiveValue;
list.FirstOptionalArgumentIndex = firstOptionalArgumentIndex;
list.UseImplicitlyTypedOut = true;
list.AddNamesToPrimitiveValues = expressionBuilder.settings.NamedArguments && expressionBuilder.settings.NonTrailingNamedArguments;
return list;
}
@ -787,7 +828,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -787,7 +828,8 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
if (IsUnambiguousCall(expectedTargetDetails, method, targetResolveResult, Empty<IType>.Array,
expandedArguments, argumentNames: null, firstOptionalArgumentIndex: -1, out _,
expandedArguments.SelectArray(a => a.ResolveResult), argumentNames: null,
firstOptionalArgumentIndex: -1, out _,
out var bestCandidateIsExpandedForm) == OverloadResolutionErrors.None && bestCandidateIsExpandedForm)
{
expectedParameters = expandedParameters;
@ -917,7 +959,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -917,7 +959,7 @@ namespace ICSharpCode.Decompiler.CSharp
bool argumentsCasted = false;
OverloadResolutionErrors errors;
while ((errors = IsUnambiguousCall(expectedTargetDetails, method, targetResolveResult, typeArguments,
argumentList.Arguments, argumentList.ArgumentNames, argumentList.FirstOptionalArgumentIndex, out foundMethod,
argumentList.GetArgumentResolveResults().ToArray(), argumentList.ArgumentNames, argumentList.FirstOptionalArgumentIndex, out foundMethod,
out var bestCandidateIsExpandedForm)) != OverloadResolutionErrors.None || bestCandidateIsExpandedForm != argumentList.IsExpandedForm)
{
switch (errors)
@ -958,6 +1000,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -958,6 +1000,7 @@ namespace ICSharpCode.Decompiler.CSharp
appliedRequireTypeArgumentsShortcut = false;
}
argumentsCasted = true;
argumentList.UseImplicitlyTypedOut = false;
CastArguments(argumentList.Arguments, argumentList.ExpectedParameters);
}
else if ((allowedTransforms & CallTransformation.RequireTarget) != 0 && !requireTarget)
@ -1116,27 +1159,32 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1116,27 +1159,32 @@ namespace ICSharpCode.Decompiler.CSharp
}
OverloadResolutionErrors IsUnambiguousCall(ExpectedTargetDetails expectedTargetDetails, IMethod method,
ResolveResult target, IType[] typeArguments, IList<TranslatedExpression> arguments,
ResolveResult target, IType[] typeArguments, ResolveResult[] arguments,
string[] argumentNames, int firstOptionalArgumentIndex,
out IParameterizedMember foundMember, out bool bestCandidateIsExpandedForm)
{
foundMember = null;
bestCandidateIsExpandedForm = false;
var lookup = new MemberLookup(resolver.CurrentTypeDefinition, resolver.CurrentTypeDefinition.ParentModule);
var currentTypeDefinition = resolver.CurrentTypeDefinition;
var lookup = new MemberLookup(currentTypeDefinition, currentTypeDefinition.ParentModule);
Log.WriteLine("IsUnambiguousCall: Performing overload resolution for " + method);
Log.WriteCollection(" Arguments: ", arguments.Select(a => a.ResolveResult));
Log.WriteCollection(" Arguments: ", arguments);
argumentNames = firstOptionalArgumentIndex < 0 || argumentNames == null
? argumentNames
: argumentNames.Take(firstOptionalArgumentIndex).ToArray();
var or = new OverloadResolution(resolver.Compilation,
firstOptionalArgumentIndex < 0 ? arguments.SelectArray(a => a.ResolveResult) : arguments.Take(firstOptionalArgumentIndex).Select(a => a.ResolveResult).ToArray(),
argumentNames: firstOptionalArgumentIndex < 0 || argumentNames == null ? argumentNames : argumentNames.Take(firstOptionalArgumentIndex).ToArray(),
typeArguments: typeArguments,
arguments, argumentNames, typeArguments,
conversions: expressionBuilder.resolver.conversions);
if (expectedTargetDetails.CallOpCode == OpCode.NewObj)
{
foreach (IMethod ctor in method.DeclaringType.GetConstructors())
{
if (lookup.IsAccessible(ctor, allowProtectedAccess: resolver.CurrentTypeDefinition == method.DeclaringTypeDefinition))
bool allowProtectedAccess =
resolver.CurrentTypeDefinition == method.DeclaringTypeDefinition;
if (lookup.IsAccessible(ctor, allowProtectedAccess))
{
or.AddCandidate(ctor);
}
@ -1145,12 +1193,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1145,12 +1193,12 @@ namespace ICSharpCode.Decompiler.CSharp
else if (method.IsOperator)
{
IEnumerable<IParameterizedMember> operatorCandidates;
if (arguments.Count == 1)
if (arguments.Length == 1)
{
IType argType = NullableType.GetUnderlyingType(arguments[0].Type);
operatorCandidates = resolver.GetUserDefinedOperatorCandidates(argType, method.Name);
}
else if (arguments.Count == 2)
else if (arguments.Length == 2)
{
IType lhsType = NullableType.GetUnderlyingType(arguments[0].Type);
IType rhsType = NullableType.GetUnderlyingType(arguments[1].Type);
@ -1170,7 +1218,8 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1170,7 +1218,8 @@ namespace ICSharpCode.Decompiler.CSharp
}
else if (target == null)
{
var result = resolver.ResolveSimpleName(method.Name, typeArguments, isInvocationTarget: true) as MethodGroupResolveResult;
var result = resolver.ResolveSimpleName(method.Name, typeArguments, isInvocationTarget: true)
as MethodGroupResolveResult;
if (result == null)
return OverloadResolutionErrors.AmbiguousMatch;
or.AddMethodLists(result.MethodsGroupedByDeclaringType.ToArray());
@ -1386,13 +1435,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1386,13 +1435,14 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
return atce.WithRR(new CSharpInvocationResolveResult(
target, method, argumentList.GetArgumentResolveResults().ToList(),
target, method, argumentList.GetArgumentResolveResults(),
isExpandedForm: argumentList.IsExpandedForm, argumentToParameterMap: argumentList.ArgumentToParameterMap
));
}
else
{
while (IsUnambiguousCall(expectedTargetDetails, method, null, Empty<IType>.Array, argumentList.Arguments,
while (IsUnambiguousCall(expectedTargetDetails, method, null, Empty<IType>.Array,
argumentList.GetArgumentResolveResults().ToArray(),
argumentList.ArgumentNames, argumentList.FirstOptionalArgumentIndex, out _,
out var bestCandidateIsExpandedForm) != OverloadResolutionErrors.None || bestCandidateIsExpandedForm != argumentList.IsExpandedForm)
{

23
ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs

@ -580,17 +580,36 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms @@ -580,17 +580,36 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
else if (CanBeDeclaredAsOutVariable(v, out var dirExpr))
{
// 'T v; SomeCall(out v);' can be combined to 'SomeCall(out var v);'
// 'T v; SomeCall(out v);' can be combined to 'SomeCall(out T v);'
AstType type;
if (context.Settings.AnonymousTypes && v.Type.ContainsAnonymousType())
{
type = new SimpleType("var");
}
else if (dirExpr.Annotation<UseImplicitlyTypedOutAnnotation>() != null)
{
type = new SimpleType("var");
}
else
{
type = context.TypeSystemAstBuilder.ConvertType(v.Type);
}
var ovd = new OutVarDeclarationExpression(type, v.Name);
string name;
// Variable is not used and discards are allowed, we can simplify this to 'out T _'.
// TODO: if no variable named _ is declared and var is used instead of T, use out _.
// Note: ExpressionBuilder.HidesVariableWithName produces inaccurate results, because it
// does not take lambdas and local functions into account, that are defined in the same
// scope as v.
if (context.Settings.Discards && v.ILVariable.LoadCount == 0
&& v.ILVariable.StoreCount == 0 && v.ILVariable.AddressCount == 1)
{
name = "_";
}
else
{
name = v.Name;
}
var ovd = new OutVarDeclarationExpression(type, name);
ovd.Variable.AddAnnotation(new ILVariableResolveResult(ilVariable));
ovd.CopyAnnotationsFrom(dirExpr);
replacements.Add((dirExpr, ovd));

2
ICSharpCode.Decompiler/Semantics/OutVarResolveResult.cs

@ -26,6 +26,8 @@ namespace ICSharpCode.Decompiler.Semantics @@ -26,6 +26,8 @@ namespace ICSharpCode.Decompiler.Semantics
/// </summary>
class OutVarResolveResult : ResolveResult
{
public static readonly OutVarResolveResult Instance = new OutVarResolveResult();
public OutVarResolveResult() : base(SpecialType.NoType) { }
}
}

Loading…
Cancel
Save