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";