diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs index 1584d0111..b5a40d081 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs @@ -1019,6 +1019,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms return td.HasFlag(System.Reflection.TypeAttributes.BeforeFieldInit); } + /// + /// Maps each backing field to whether its references outside the owning property's + /// accessors can still be expressed once the field declaration is gone: true for + /// rescuable constructor stores only, false for anything else. A field absent from + /// the map has no outside references at all and is therefore also expressible. + /// Dictionary BuildOutsideReferenceIndex(AstNode root) { var verdicts = new Dictionary(); @@ -1190,10 +1196,12 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms } /// - /// True when is the left-hand side of a plain assignment to + /// True when is a target of a plain assignment to /// inside a constructor of the field's declaring type - the /// only outside reference the "field" keyword can still express (as a property - /// initializer, or an assignment to a setter-less property). + /// initializer, or an assignment to a setter-less property). A deconstruction target + /// counts only in the second form: it assigns several members at once, so it can never + /// move into an initializer, and a property that kept a setter would invoke it. /// /// /// Shared by , which decides whether the @@ -1205,8 +1213,25 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms /// static bool IsConstructorStore(AstNode node, IField field, IMethod? enclosingMethod) { - if (node.Parent is not AssignmentExpression { Operator: AssignmentOperatorType.Assign } assignment - || assignment.Left != node) + AstNode currentNode = node; + bool viaDeconstruction = false; + while (true) + { + if (currentNode.Parent is AssignmentExpression { Operator: AssignmentOperatorType.Assign } + && currentNode.Slot == AssignmentExpression.LeftSlot) + { + break; + } + if (currentNode.Parent is TupleExpression) + { + viaDeconstruction = true; + currentNode = currentNode.Parent; + continue; + } + return false; + } + if (viaDeconstruction + && (!IsBackingFieldOfAutomaticProperty(field, out var property) || property.CanSet)) { return false; }