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)