Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions ICSharpCode.Decompiler.Tests/TestCases/Pretty/OutVariables.cs
Original file line number Diff line number Diff line change
Expand Up @@ -92,5 +92,43 @@ public static void CapturedBoolResult(Dictionary<int, int> d, int key)
Console.WriteLine(value2);
Console.WriteLine(value2);
}

private static void GetTwo(out int a, out int b)
{
a = 1;
b = 2;
}

public static void SameVariableUsedForTwoOutParameters()
{
// The declaration must use the explicit type: referencing an implicitly-typed
// out variable in another argument of the declaring call is an error (CS8196).
GetTwo(out int a, out a);
}

public static int SameVariableUsedForTwoOutParametersAndRead()
{
GetTwo(out int a, out a);
return a;
}

private static void OutAndValue(out int a, int b)
{
a = b;
}

private static int UseAndReturn(out int a)
{
a = 1;
return a;
}

public static void SameVariableUsedInNestedCallArgument()
{
// CS8196 also applies when the second reference is nested within
// another argument of the declaring call.
OutAndValue(out int a, UseAndReturn(out a));
Console.WriteLine(a);
}
}
}
42 changes: 41 additions & 1 deletion ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,8 @@ void InsertVariableDeclarations(TransformContext context)
type = new SimpleType("var");
isOutVar = true;
}
else if (dirExpr.Annotation<UseImplicitlyTypedOutAnnotation>() != null)
else if (dirExpr.Annotation<UseImplicitlyTypedOutAnnotation>() != null
&& !IsReferencedWithinDeclaringCall(dirExpr, v))
{
type = new SimpleType("var");
isOutVar = true;
Expand Down Expand Up @@ -788,6 +789,45 @@ void InsertVariableDeclarations(TransformContext context)
}
}

/// <summary>
/// Gets whether the variable declared by <paramref name="dirExpr"/> is referenced again within
/// another argument of the call containing the declaration. In that case the declaration must use
/// the explicit type: referencing an implicitly-typed out variable is not permitted until overload
/// resolution of the declaring call has inferred its type (CS8196).
/// </summary>
bool IsReferencedWithinDeclaringCall(DirectionExpression dirExpr, VariableToDeclare v)
{
AstNode? call = dirExpr.Parent;
if (call == null)
return false;
for (AstNode? argument = call.FirstChild; argument != null; argument = argument.NextSibling)
{
if (argument == dirExpr)
continue;
foreach (AstNode node in argument.DescendantsAndSelf)
{
if (node is IdentifierExpression identifier && ResolveVariableToDeclare(identifier.GetILVariable()) == v)
return true;
}
}
return false;
}

/// <summary>
/// Maps an ILVariable to the variable declaration it will end up in, following merges
/// performed by ResolveCollisions.
/// </summary>
VariableToDeclare? ResolveVariableToDeclare(ILVariable? variable)
{
if (variable == null || !variableDict.TryGetValue(variable, out VariableToDeclare? v))
return null;
while (v.ReplacementDueToCollision is { } replacement)
{
v = replacement;
}
return v;
}

private bool CanBeDeclaredAsOutVariable(VariableToDeclare v, [NotNullWhen(true)] out DirectionExpression? dirExpr)
{
dirExpr = v.FirstUse.Parent as DirectionExpression;
Expand Down
Loading