From b28281c890cd6a10720da7290fac5df186b786b4 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 30 Nov 2025 18:23:11 +0000
Subject: [PATCH] Refactor semi-auto properties to simplify simple accessors to
get; and set;
Co-authored-by: christophwille <344208+christophwille@users.noreply.github.com>
---
.../TestCases/Pretty/SemiAutoProperties.cs | 12 +---
.../Transforms/PatternStatementTransform.cs | 70 +++++++++++++++++++
2 files changed, 73 insertions(+), 9 deletions(-)
diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/SemiAutoProperties.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/SemiAutoProperties.cs
index d74ce23a1..cfd7a6b0a 100644
--- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/SemiAutoProperties.cs
+++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/SemiAutoProperties.cs
@@ -23,9 +23,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
internal class SemiAutoProperties
{
public int ValidatedProperty {
- get {
- return field;
- }
+ get;
set {
if (value < 0)
{
@@ -43,15 +41,11 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
}
return field;
}
- set {
- field = value;
- }
+ set;
}
public int NotifyProperty {
- get {
- return field;
- }
+ get;
set {
if (field != value)
{
diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
index dbe64c605..636fc898e 100644
--- a/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
+++ b/ICSharpCode.Decompiler/CSharp/Transforms/PatternStatementTransform.cs
@@ -671,6 +671,8 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
/// Transforms a property that uses the backing field into a semi-auto property
/// by removing the backing field declaration. The field keyword replacements
/// are already done in ReplaceBackingFieldUsage during child visiting.
+ /// Also simplifies simple accessors like `get { return field; }` to `get;`
+ /// and `set { field = value; }` to `set;`.
///
void TransformSemiAutoProperty(PropertyDeclaration propertyDeclaration)
{
@@ -711,6 +713,74 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms
}
fieldDecl.Remove();
}
+ // Now simplify simple accessors: get { return field; } -> get; and set { field = value; } -> set;
+ SimplifySemiAutoPropertyAccessors(propertyDeclaration);
+ }
+
+ ///
+ /// Checks if a getter body is simply `{ return field; }` and if so, removes the body.
+ /// Checks if a setter body is simply `{ field = value; }` and if so, removes the body.
+ ///
+ void SimplifySemiAutoPropertyAccessors(PropertyDeclaration propertyDeclaration)
+ {
+ // Simplify getter: get { return field; } -> get;
+ if (!propertyDeclaration.Getter.IsNull && !propertyDeclaration.Getter.Body.IsNull)
+ {
+ if (IsSimpleFieldGetter(propertyDeclaration.Getter.Body))
+ {
+ RemoveCompilerGeneratedAttribute(propertyDeclaration.Getter.Attributes);
+ propertyDeclaration.Getter.Body = null;
+ }
+ }
+ // Simplify setter: set { field = value; } -> set;
+ if (!propertyDeclaration.Setter.IsNull && !propertyDeclaration.Setter.Body.IsNull)
+ {
+ if (IsSimpleFieldSetter(propertyDeclaration.Setter.Body))
+ {
+ RemoveCompilerGeneratedAttribute(propertyDeclaration.Setter.Attributes);
+ propertyDeclaration.Setter.Body = null;
+ }
+ }
+ }
+
+ ///
+ /// Checks if the block is simply `{ return field; }` where field has the SemiAutoPropertyFieldKeywordAnnotation.
+ ///
+ bool IsSimpleFieldGetter(BlockStatement body)
+ {
+ if (body.Statements.Count != 1)
+ return false;
+ if (body.Statements.Single() is not ReturnStatement returnStmt)
+ return false;
+ if (returnStmt.Expression is not IdentifierExpression identExpr)
+ return false;
+ return identExpr.IdentifierToken.Annotation() != null
+ && identExpr.Identifier == "field";
+ }
+
+ ///
+ /// Checks if the block is simply `{ field = value; }` where field has the SemiAutoPropertyFieldKeywordAnnotation.
+ ///
+ bool IsSimpleFieldSetter(BlockStatement body)
+ {
+ if (body.Statements.Count != 1)
+ return false;
+ if (body.Statements.Single() is not ExpressionStatement exprStmt)
+ return false;
+ if (exprStmt.Expression is not AssignmentExpression assignExpr)
+ return false;
+ if (assignExpr.Operator != AssignmentOperatorType.Assign)
+ return false;
+ // Check left side is "field" with annotation
+ if (assignExpr.Left is not IdentifierExpression leftIdent)
+ return false;
+ if (leftIdent.IdentifierToken.Annotation() == null
+ || leftIdent.Identifier != "field")
+ return false;
+ // Check right side is "value"
+ if (assignExpr.Right is not IdentifierExpression rightIdent)
+ return false;
+ return rightIdent.Identifier == "value";
}
void RemoveCompilerGeneratedAttribute(AstNodeCollection attributeSections)