From e34db06a677497ec6a5df5c7a21535c6350064c8 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 5 Sep 2025 10:42:17 +0200 Subject: [PATCH] Fix #3543: Missing parentheses around field assignment expression in list initializers --- .../Output/InsertParenthesesVisitorTests.cs | 27 +++++++++++++++++++ .../OutputVisitor/InsertParenthesesVisitor.cs | 9 +++++++ 2 files changed, 36 insertions(+) diff --git a/ICSharpCode.Decompiler.Tests/Output/InsertParenthesesVisitorTests.cs b/ICSharpCode.Decompiler.Tests/Output/InsertParenthesesVisitorTests.cs index 221775d9b..a2408f10f 100644 --- a/ICSharpCode.Decompiler.Tests/Output/InsertParenthesesVisitorTests.cs +++ b/ICSharpCode.Decompiler.Tests/Output/InsertParenthesesVisitorTests.cs @@ -484,5 +484,32 @@ namespace ICSharpCode.Decompiler.Tests.Output Assert.That(InsertRequired(expr), Is.EqualTo("new int[1] { 42 } [0]")); Assert.That(InsertReadable(expr), Is.EqualTo("(new int[1] { 42 }) [0]")); } + + [Test] + public void AssignmentInObjectOrCollectionInitializer() + { + Expression expr = new ObjectCreateExpression { + Type = AstType.Create("List"), + Initializer = new ArrayInitializerExpression { + Elements = { new AssignmentExpression(new IdentifierExpression("x"), new PrimitiveExpression(42)) } + } + }; + Expression expr2 = new ObjectCreateExpression { + Type = AstType.Create("Data"), + Initializer = new ArrayInitializerExpression { + Elements = { new NamedExpression("X", new PrimitiveExpression(42)) } + } + }; + Expression expr3 = new ObjectCreateExpression { + Type = AstType.Create("Dictionary"), + Initializer = new ArrayInitializerExpression { + Elements = { new AssignmentExpression(new IndexerExpression(null, new PrimitiveExpression(0)), new PrimitiveExpression("42")) } + } + }; + + Assert.That(InsertRequired(expr), Is.EqualTo("new List { (x = 42) }")); + Assert.That(InsertRequired(expr2), Is.EqualTo("new Data { X = 42 }")); + Assert.That(InsertRequired(expr3), Is.EqualTo("new Dictionary { [0] = \"42\" }")); + } } } diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs index 4d0e04095..bb79661e3 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/InsertParenthesesVisitor.cs @@ -458,6 +458,15 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor public override void VisitAssignmentExpression(AssignmentExpression assignmentExpression) { + // Assignments in initializers need additional parentheses to disambiguate assignments + // to variables and assignments to members of the initialized object. + // This works without access to semantic information, because the ExpressionBuilder + // uses NamedExpression for `Member = value` instead of AssignmentExpression. + if (assignmentExpression.Parent is ArrayInitializerExpression + && assignmentExpression.Left is not IndexerExpression) + { + Parenthesize(assignmentExpression); + } // assignment is right-associative ParenthesizeIfRequired(assignmentExpression.Left, PrecedenceLevel.Assignment + 1); HandleAssignmentRHS(assignmentExpression.Right);