diff --git a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
index d34cd96d3..83475a278 100644
--- a/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
+++ b/ICSharpCode.Decompiler.Tests/ICSharpCode.Decompiler.Tests.csproj
@@ -155,6 +155,9 @@
+
+
+
diff --git a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
index 562beb8f2..81cca7639 100644
--- a/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
+++ b/ICSharpCode.Decompiler.Tests/PrettyTestRunner.cs
@@ -678,6 +678,24 @@ namespace ICSharpCode.Decompiler.Tests
await RunForLibrary(cscOptions: cscOptions);
}
+ [Test]
+ public async Task Issue3571_A([ValueSource(nameof(roslyn2OrNewerWithNet40Options))] CompilerOptions cscOptions)
+ {
+ await RunForLibrary(cscOptions: cscOptions);
+ }
+
+ [Test]
+ public async Task Issue3571_B([ValueSource(nameof(roslyn2OrNewerWithNet40Options))] CompilerOptions cscOptions)
+ {
+ await RunForLibrary(cscOptions: cscOptions);
+ }
+
+ [Test]
+ public async Task Issue3571_C([ValueSource(nameof(roslyn2OrNewerWithNet40Options))] CompilerOptions cscOptions)
+ {
+ await RunForLibrary(cscOptions: cscOptions);
+ }
+
[Test]
public async Task AssemblyCustomAttributes([ValueSource(nameof(defaultOptions))] CompilerOptions cscOptions)
{
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3571_A.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3571_A.cs
new file mode 100644
index 000000000..27a69d844
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3571_A.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
+{
+ internal static class Issue3571_A
+ {
+ [StructLayout(LayoutKind.Sequential, Size = 1)]
+ public readonly struct fsResult
+ {
+ public static fsResult Success => default(fsResult);
+ public static fsResult Failure => default(fsResult);
+ public bool Succeeded => true;
+ public bool Failed => false;
+ public static fsResult operator +(fsResult a, fsResult b)
+ {
+ return default(fsResult);
+ }
+ }
+
+ public static fsResult M()
+ {
+ fsResult success = fsResult.Success;
+ fsResult fsResult2 = success + fsResult.Success;
+ if (fsResult2.Succeeded)
+ {
+ return success;
+ }
+ Console.WriteLine("Failed");
+ return fsResult2 + fsResult.Failure;
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3571_B.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3571_B.cs
new file mode 100644
index 000000000..a5dd91f7f
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3571_B.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue3571_B
+{
+ [StructLayout(LayoutKind.Sequential, Size = 1)]
+ public readonly struct fsResult
+ {
+ public static fsResult Success => default(fsResult);
+ public static fsResult Failure => default(fsResult);
+ public bool Succeeded => true;
+ public bool Failed => false;
+ public static fsResult operator +(fsResult a, fsResult b)
+ {
+ return default(fsResult);
+ }
+ }
+
+ internal static class Issue3571_B
+ {
+ public static fsResult M()
+ {
+ fsResult success = fsResult.Success;
+ fsResult fsResult2 = success + fsResult.Success;
+ if (fsResult2.Succeeded)
+ {
+ return success;
+ }
+ Console.WriteLine("Failed");
+ return fsResult2 + fsResult.Failure;
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3571_C.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3571_C.cs
new file mode 100644
index 000000000..e8da53842
--- /dev/null
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Issue3571_C.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Runtime.InteropServices;
+using ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue3571_Helper;
+
+namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
+{
+ internal static class Issue3571_C
+ {
+ public static fsResult M()
+ {
+ fsResult success = fsResult.Success;
+ fsResult fsResult2 = success + fsResult.Success;
+ if (fsResult2.Succeeded)
+ {
+ return success;
+ }
+ Console.WriteLine("Failed");
+ return fsResult2 + fsResult.Failure;
+ }
+ }
+}
+namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.Issue3571_Helper
+{
+ [StructLayout(LayoutKind.Sequential, Size = 1)]
+ public readonly struct fsResult
+ {
+ public static fsResult Success => default(fsResult);
+ public static fsResult Failure => default(fsResult);
+ public bool Succeeded => true;
+ public bool Failed => false;
+ public static fsResult operator +(fsResult a, fsResult b)
+ {
+ return default(fsResult);
+ }
+ }
+}
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs
index a44292dd2..3c11e6517 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/QualifierTests.cs
@@ -308,9 +308,9 @@ namespace ICSharpCode.Decompiler.Tests.Pretty
private void LocalConflictsWithTypeName()
{
- for (int i = 0; i < 10; i++)
+ for (int j = 0; j < 10; j++)
{
- QualifierTests.i.Test();
+ i.Test();
}
}
#if CS70
diff --git a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
index aa44a0474..3c7ff756b 100644
--- a/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
+++ b/ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
@@ -551,7 +551,7 @@ namespace ICSharpCode.Decompiler.CSharp
var.Kind = VariableKind.Local;
var disposeVariable = currentFunction.RegisterVariable(
VariableKind.Local, disposeType,
- AssignVariableNames.GenerateVariableName(currentFunction, disposeType)
+ AssignVariableNames.GenerateVariableName(currentFunction, disposeType, decompileRun.UsingScope)
);
Expression disposeInvocation = new InvocationExpression(new MemberReferenceExpression(exprBuilder.ConvertVariable(disposeVariable).Expression, disposeTypeMethodName));
if (inst.IsAsync)
@@ -712,12 +712,12 @@ namespace ICSharpCode.Decompiler.CSharp
if (foreachVariable.Type.Kind != TypeKind.Dynamic)
foreachVariable.Type = type;
foreachVariable.Kind = VariableKind.ForeachLocal;
- foreachVariable.Name = AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation(), foreachVariable);
+ foreachVariable.Name = AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation(), decompileRun.UsingScope, foreachVariable);
break;
case RequiredGetCurrentTransformation.IntroduceNewVariable:
foreachVariable = currentFunction.RegisterVariable(
VariableKind.ForeachLocal, type,
- AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation())
+ AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation(), decompileRun.UsingScope)
);
instToReplace.ReplaceWith(new LdLoc(foreachVariable));
body.Instructions.Insert(0, new StLoc(foreachVariable, instToReplace));
@@ -725,11 +725,11 @@ namespace ICSharpCode.Decompiler.CSharp
case RequiredGetCurrentTransformation.IntroduceNewVariableAndLocalCopy:
foreachVariable = currentFunction.RegisterVariable(
VariableKind.ForeachLocal, type,
- AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation())
+ AssignVariableNames.GenerateForeachVariableName(currentFunction, collectionExpr.Annotation(), decompileRun.UsingScope)
);
var localCopyVariable = currentFunction.RegisterVariable(
VariableKind.Local, type,
- AssignVariableNames.GenerateVariableName(currentFunction, type)
+ AssignVariableNames.GenerateVariableName(currentFunction, type, decompileRun.UsingScope)
);
instToReplace.Parent.ReplaceWith(new LdLoca(localCopyVariable));
body.Instructions.Insert(0, new StLoc(localCopyVariable, new LdLoc(foreachVariable)));
diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
index 2a339fc79..fd27a9fea 100644
--- a/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
+++ b/ICSharpCode.Decompiler/CSharp/Transforms/DeclareVariables.cs
@@ -227,7 +227,7 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
var v = function.RegisterVariable(
VariableKind.StackSlot,
type,
- AssignVariableNames.GenerateVariableName(function, type,
+ AssignVariableNames.GenerateVariableName(function, type, context.DecompileRun.UsingScope,
stmt.Expression.Annotations.OfType()
.Where(AssignVariableNames.IsSupportedInstruction).FirstOrDefault(),
mustResolveConflicts: true)
diff --git a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
index 5b271f994..a787ea128 100644
--- a/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
+++ b/ICSharpCode.Decompiler/IL/Transforms/AssignVariableNames.cs
@@ -28,6 +28,7 @@ using Humanizer.Inflections;
using ICSharpCode.Decompiler.CSharp;
using ICSharpCode.Decompiler.CSharp.OutputVisitor;
+using ICSharpCode.Decompiler.CSharp.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.TypeSystem.Implementation;
using ICSharpCode.Decompiler.Util;
@@ -110,6 +111,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
currentLowerCaseTypeOrMemberNames.Add(name);
AddExistingName(reservedVariableNames, name);
}
+ foreach (var name in CollectAllLowerCaseTypeNames(context.UsingScope))
+ {
+ currentLowerCaseTypeOrMemberNames.Add(name);
+ AddExistingName(reservedVariableNames, name);
+ }
this.currentLowerCaseTypeOrMemberNames = currentLowerCaseTypeOrMemberNames.ToImmutableHashSet();
// handle implicit parameters of set or event accessors
@@ -618,6 +624,21 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (IsLowerCase(item.Name))
yield return item.Name;
}
+ var current = type;
+ while (current != null)
+ {
+ foreach (var nested in current.NestedTypes)
+ {
+ if (IsLowerCase(nested.Name))
+ yield return nested.Name;
+ }
+ current = current.DeclaringTypeDefinition;
+ }
+ }
+
+ static IEnumerable CollectAllLowerCaseTypeNames(UsingScope usingScope)
+ {
+ return usingScope.Usings.SelectMany(n => n.Types).Select(t => t.Name).Where(IsLowerCase);
}
static bool IsLowerCase(string name)
@@ -870,7 +891,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
static Dictionary CollectReservedVariableNames(ILFunction function,
- ILVariable existingVariable, bool mustResolveConflicts)
+ ILVariable existingVariable, bool mustResolveConflicts, UsingScope usingScope)
{
var reservedVariableNames = new Dictionary();
var rootFunction = function.Ancestors.OfType().Single(f => f.Parent == null);
@@ -889,14 +910,15 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (mustResolveConflicts)
{
var memberNames = CollectAllLowerCaseMemberNames(function.Method.DeclaringTypeDefinition)
- .Concat(CollectAllLowerCaseTypeNames(function.Method.DeclaringTypeDefinition));
+ .Concat(CollectAllLowerCaseTypeNames(function.Method.DeclaringTypeDefinition))
+ .Concat(CollectAllLowerCaseTypeNames(usingScope));
foreach (var name in memberNames)
AddExistingName(reservedVariableNames, name);
}
return reservedVariableNames;
}
- internal static string GenerateForeachVariableName(ILFunction function, ILInstruction valueContext,
+ internal static string GenerateForeachVariableName(ILFunction function, ILInstruction valueContext, UsingScope usingScope,
ILVariable existingVariable = null, bool mustResolveConflicts = false)
{
if (function == null)
@@ -905,7 +927,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
return existingVariable.Name;
}
- var reservedVariableNames = CollectReservedVariableNames(function, existingVariable, mustResolveConflicts);
+ var reservedVariableNames = CollectReservedVariableNames(function, existingVariable, mustResolveConflicts, usingScope);
string baseName = GetNameFromInstruction(valueContext);
if (string.IsNullOrEmpty(baseName))
@@ -955,13 +977,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
- internal static string GenerateVariableName(ILFunction function, IType type,
+ internal static string GenerateVariableName(ILFunction function, IType type, UsingScope usingScope,
ILInstruction valueContext = null, ILVariable existingVariable = null,
bool mustResolveConflicts = false)
{
if (function == null)
throw new ArgumentNullException(nameof(function));
- var reservedVariableNames = CollectReservedVariableNames(function, existingVariable, mustResolveConflicts);
+ var reservedVariableNames = CollectReservedVariableNames(function, existingVariable, mustResolveConflicts, usingScope);
string baseName = valueContext != null ? GetNameFromInstruction(valueContext) ?? GetNameByType(type) : GetNameByType(type);
string proposedName = "obj";