From c38298990408be71f71d05daf61d5ccffba4d4be Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sat, 3 Mar 2018 22:48:25 +0100 Subject: [PATCH] Add expression body syntax for getter-only properties. --- .../OutputVisitor/CSharpOutputVisitor.cs | 36 +++++++++++-------- .../Syntax/Expressions/LambdaExpression.cs | 3 +- ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs | 1 + .../Syntax/TypeMembers/PropertyDeclaration.cs | 11 ++++-- .../Transforms/NormalizeBlockStatements.cs | 24 +++++++++++++ ICSharpCode.Decompiler/DecompilerSettings.cs | 15 ++++++++ 6 files changed, 72 insertions(+), 18 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs index 8426d8f1e..a6d747271 100644 --- a/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs +++ b/ICSharpCode.Decompiler/CSharp/OutputVisitor/CSharpOutputVisitor.cs @@ -866,7 +866,7 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor lambdaExpression.Parameters.Single().AcceptVisitor(this); } Space(); - WriteToken(LambdaExpression.ArrowRole); + WriteToken(Roles.Arrow); if (lambdaExpression.Body is BlockStatement) { WriteBlock((BlockStatement)lambdaExpression.Body, policy.AnonymousMethodBraceStyle); } else { @@ -2179,22 +2179,30 @@ namespace ICSharpCode.Decompiler.CSharp.OutputVisitor Space(); WritePrivateImplementationType(propertyDeclaration.PrivateImplementationType); WriteIdentifier(propertyDeclaration.NameToken); - OpenBrace(policy.PropertyBraceStyle); - // output get/set in their original order - foreach (AstNode node in propertyDeclaration.Children) { - if (node.Role == IndexerDeclaration.GetterRole || node.Role == IndexerDeclaration.SetterRole) { - node.AcceptVisitor(this); + if (propertyDeclaration.ExpressionBody.IsNull) { + OpenBrace(policy.PropertyBraceStyle); + // output get/set in their original order + foreach (AstNode node in propertyDeclaration.Children) { + if (node.Role == IndexerDeclaration.GetterRole || node.Role == IndexerDeclaration.SetterRole) { + node.AcceptVisitor(this); + } } - } - CloseBrace(policy.PropertyBraceStyle); - if (!propertyDeclaration.Initializer.IsNull) { - Space(policy.SpaceAroundAssignment); - WriteToken(Roles.Assign); - Space(policy.SpaceAroundAssignment); - propertyDeclaration.Initializer.AcceptVisitor(this); + CloseBrace(policy.PropertyBraceStyle); + if (!propertyDeclaration.Initializer.IsNull) { + Space(policy.SpaceAroundAssignment); + WriteToken(Roles.Assign); + Space(policy.SpaceAroundAssignment); + propertyDeclaration.Initializer.AcceptVisitor(this); + Semicolon(); + } + NewLine(); + } else { + Space(); + WriteToken(Roles.Arrow); + Space(); + propertyDeclaration.ExpressionBody.AcceptVisitor(this); Semicolon(); } - NewLine(); EndNode(propertyDeclaration); } diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/LambdaExpression.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/LambdaExpression.cs index ba1449c7b..30ed76fa1 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/LambdaExpression.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Expressions/LambdaExpression.cs @@ -32,7 +32,6 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public class LambdaExpression : Expression { public readonly static TokenRole AsyncModifierRole = new TokenRole ("async"); - public readonly static TokenRole ArrowRole = new TokenRole ("=>"); public static readonly Role BodyRole = new Role("Body", AstNode.Null); bool isAsync; @@ -55,7 +54,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax } public CSharpTokenNode ArrowToken { - get { return GetChildByRole (ArrowRole); } + get { return GetChildByRole (Roles.Arrow); } } public AstNode Body { diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs b/ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs index 98405c4ab..32d29d71c 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/Roles.cs @@ -66,6 +66,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public static readonly TokenRole Assign = new TokenRole ("="); public static readonly TokenRole Colon = new TokenRole (":"); public static readonly TokenRole DoubleColon = new TokenRole ("::"); + public static readonly TokenRole Arrow = new TokenRole("=>"); public static readonly Role Comment = new Role ("Comment"); public static readonly Role NewLine = new Role ("NewLine"); public static readonly Role Whitespace = new Role ("Whitespace"); diff --git a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs index 81120b6cd..00f163122 100644 --- a/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs +++ b/ICSharpCode.Decompiler/CSharp/Syntax/TypeMembers/PropertyDeclaration.cs @@ -34,7 +34,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax public static readonly TokenRole SetKeywordRole = new TokenRole ("set"); public static readonly Role GetterRole = new Role("Getter", Accessor.Null); public static readonly Role SetterRole = new Role("Setter", Accessor.Null); - + public static readonly Role ExpressionBodyRole = new Role("ExpressionBody", Expression.Null); + public override SymbolKind SymbolKind { get { return SymbolKind.Property; } } @@ -75,6 +76,11 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax set { SetChildByRole(Roles.Expression, value); } } + public Expression ExpressionBody { + get { return GetChildByRole(ExpressionBodyRole); } + set { SetChildByRole(ExpressionBodyRole, value); } + } + public override void AcceptVisitor (IAstVisitor visitor) { visitor.VisitPropertyDeclaration (this); @@ -97,7 +103,8 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax && this.MatchAttributesAndModifiers(o, match) && this.ReturnType.DoMatch(o.ReturnType, match) && this.PrivateImplementationType.DoMatch(o.PrivateImplementationType, match) && this.Getter.DoMatch(o.Getter, match) && this.Setter.DoMatch(o.Setter, match) - && this.Initializer.DoMatch(o.Initializer, match); + && this.Initializer.DoMatch(o.Initializer, match) + && this.ExpressionBody.DoMatch(o.ExpressionBody, match); } } } diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs index 89b27f40c..d9120755a 100644 --- a/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs +++ b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using ICSharpCode.Decompiler.CSharp.Syntax; +using ICSharpCode.Decompiler.CSharp.Syntax.PatternMatching; namespace ICSharpCode.Decompiler.CSharp.Transforms { @@ -116,5 +117,28 @@ namespace ICSharpCode.Decompiler.CSharp.Transforms this.context = context; rootNode.AcceptVisitor(this); } + + public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) + { + if (context.Settings.UseExpressionBodyForCalculatedGetterOnlyProperties) { + SimplifyPropertyDeclaration(propertyDeclaration); + } + base.VisitPropertyDeclaration(propertyDeclaration); + } + + static readonly PropertyDeclaration CalculatedGetterOnlyPropertyPattern = new PropertyDeclaration() { + Modifiers = Modifiers.Any, + Name = Pattern.AnyString, + ReturnType = new AnyNode(), + Getter = new Accessor() { Body = new BlockStatement() { new ReturnStatement(new AnyNode("expression")) } } + }; + + void SimplifyPropertyDeclaration(PropertyDeclaration propertyDeclaration) + { + var m = CalculatedGetterOnlyPropertyPattern.Match(propertyDeclaration); + if (!m.Success) + return; + propertyDeclaration.ExpressionBody = m.Get("expression").Single().Detach(); + } } } diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index cc729a824..77470b814 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -421,6 +421,21 @@ namespace ICSharpCode.Decompiler } } + bool useExpressionBodyForCalculatedGetterOnlyProperties = true; + + /// + /// Gets/Sets whether simple calculated getter-only property declarations should use expression body syntax. + /// + public bool UseExpressionBodyForCalculatedGetterOnlyProperties { + get { return useExpressionBodyForCalculatedGetterOnlyProperties; } + set { + if (useExpressionBodyForCalculatedGetterOnlyProperties != value) { + useExpressionBodyForCalculatedGetterOnlyProperties = value; + OnPropertyChanged(); + } + } + } + #region Options to aid VB decompilation bool introduceIncrementAndDecrement = true;