Browse Source

Fix #3392: uses of init-setters must use object-initializer syntax.

pull/3403/head
Siegfried Pammer 6 months ago
parent
commit
906d248403
  1. 24
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs
  2. 8
      ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs

24
ICSharpCode.Decompiler.Tests/TestCases/Pretty/InitializerTests.cs

@ -235,6 +235,19 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests
{
int Property { get; set; }
}
#if CS90
public class Issue3392Type
{
public bool Flag { get; init; }
public List<int> List { get; } = new List<int>();
public Issue3392Type(object x)
{
}
}
#endif
#endregion
private S s1;
@ -1010,6 +1023,17 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty.InitializerTests
otherItem.Data2.Nullable = 3m;
return otherItem;
}
#if CS90
public Issue3392Type Issue3392(Issue3392Type x)
{
x = new Issue3392Type(null) {
Flag = false
};
x.List.AddRange(Enumerable.Range(0, 10));
return x;
}
#endif
#if CS60
public OtherItem2 Issue1345c()
{

8
ICSharpCode.Decompiler/IL/Transforms/TransformCollectionAndObjectInitializers.cs

@ -103,6 +103,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return;
}
int initializerItemsCount = 0;
bool initializerContainsInitOnlyItems = false;
possibleIndexVariables.Clear();
currentPath.Clear();
isCollection = false;
@ -113,13 +114,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// if the method is a setter we're dealing with an object initializer
// if the method is named Add and has at least 2 arguments we're dealing with a collection/dictionary initializer
while (pos + initializerItemsCount + 1 < block.Instructions.Count
&& IsPartOfInitializer(block.Instructions, pos + initializerItemsCount + 1, v, instType, ref blockKind, context))
&& IsPartOfInitializer(block.Instructions, pos + initializerItemsCount + 1, v, instType, ref blockKind, ref initializerContainsInitOnlyItems, context))
{
initializerItemsCount++;
}
// Do not convert the statements into an initializer if there's an incompatible usage of the initializer variable
// directly after the possible initializer.
if (IsMethodCallOnVariable(block.Instructions[pos + initializerItemsCount + 1], v))
if (!initializerContainsInitOnlyItems && IsMethodCallOnVariable(block.Instructions[pos + initializerItemsCount + 1], v))
return;
// Calculate the correct number of statements inside the initializer:
// All index variables that were used in the initializer have Index set to -1.
@ -200,7 +201,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
bool isCollection;
readonly Stack<HashSet<AccessPathElement>> pathStack = new Stack<HashSet<AccessPathElement>>();
bool IsPartOfInitializer(InstructionCollection<ILInstruction> instructions, int pos, ILVariable target, IType rootType, ref BlockKind blockKind, StatementTransformContext context)
bool IsPartOfInitializer(InstructionCollection<ILInstruction> instructions, int pos, ILVariable target, IType rootType, ref BlockKind blockKind, ref bool initializerContainsInitOnlyItems, StatementTransformContext context)
{
// Include any stores to local variables that are single-assigned and do not reference the initializer-variable
// in the list of possible index variables.
@ -255,6 +256,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
return false;
if (blockKind != BlockKind.ObjectInitializer && blockKind != BlockKind.WithInitializer)
blockKind = BlockKind.ObjectInitializer;
initializerContainsInitOnlyItems |= lastElement.Member is IProperty { Setter.IsInitOnly: true };
return true;
default:
return false;

Loading…
Cancel
Save