From ab70795a522494d4394b2c5efe6cada6a6f329ef Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Fri, 10 Nov 2017 22:24:37 +0100 Subject: [PATCH] Add NormalizeBlockStatements transform --- .../TestCases/Pretty/Loops.cs | 9 +- .../CSharp/CSharpDecompiler.cs | 1 + .../Transforms/NormalizeBlockStatements.cs | 110 ++++++++++++++++++ ICSharpCode.Decompiler/DecompilerSettings.cs | 15 +++ .../ICSharpCode.Decompiler.csproj | 1 + ILSpy/Options/DecompilerSettingsPanel.xaml | 1 + ILSpy/Options/DecompilerSettingsPanel.xaml.cs | 2 + 7 files changed, 136 insertions(+), 3 deletions(-) create mode 100644 ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs index 5134c7e42..52705b885 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/Loops.cs @@ -379,8 +379,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty } } finally { IDisposable disposable = enumerator as IDisposable; - if (disposable != null) + if (disposable != null) { disposable.Dispose(); + } } Console.WriteLine("After finally!"); } @@ -527,8 +528,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty if (this.Condition("continue")) { continue; } - if (!this.Condition("break")) + if (!this.Condition("break")) { break; + } } Console.WriteLine("End of loop body"); } @@ -582,8 +584,9 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty if (this.Condition("continue")) { continue; } - if (!this.Condition("not-break")) + if (!this.Condition("not-break")) { break; + } } Console.WriteLine("End of loop body"); } diff --git a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs index 96bcd9e16..ead47885b 100644 --- a/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs +++ b/ICSharpCode.Decompiler/CSharp/CSharpDecompiler.cs @@ -152,6 +152,7 @@ namespace ICSharpCode.Decompiler.CSharp new IntroduceExtensionMethods(), // must run after IntroduceUsingDeclarations new IntroduceQueryExpressions(), // must run after IntroduceExtensionMethods new CombineQueryExpressions(), + new NormalizeBlockStatements(), new FlattenSwitchBlocks(), new FixNameCollisions(), new AddXmlDocumentationTransform(), diff --git a/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs new file mode 100644 index 000000000..2715e172e --- /dev/null +++ b/ICSharpCode.Decompiler/CSharp/Transforms/NormalizeBlockStatements.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using ICSharpCode.Decompiler.CSharp.Syntax; + +namespace ICSharpCode.Decompiler.CSharp.Transforms +{ + class NormalizeBlockStatements : DepthFirstAstVisitor, IAstTransform + { + TransformContext context; + + public override void VisitIfElseStatement(IfElseStatement ifElseStatement) + { + base.VisitIfElseStatement(ifElseStatement); + DoTransform(ifElseStatement.TrueStatement, ifElseStatement); + DoTransform(ifElseStatement.FalseStatement, ifElseStatement); + } + + public override void VisitWhileStatement(WhileStatement whileStatement) + { + base.VisitWhileStatement(whileStatement); + InsertBlock(whileStatement.EmbeddedStatement); + } + + public override void VisitDoWhileStatement(DoWhileStatement doWhileStatement) + { + base.VisitDoWhileStatement(doWhileStatement); + InsertBlock(doWhileStatement.EmbeddedStatement); + } + + public override void VisitForeachStatement(ForeachStatement foreachStatement) + { + base.VisitForeachStatement(foreachStatement); + InsertBlock(foreachStatement.EmbeddedStatement); + } + + public override void VisitForStatement(ForStatement forStatement) + { + base.VisitForStatement(forStatement); + InsertBlock(forStatement.EmbeddedStatement); + } + + public override void VisitFixedStatement(FixedStatement fixedStatement) + { + base.VisitFixedStatement(fixedStatement); + InsertBlock(fixedStatement.EmbeddedStatement); + } + + public override void VisitLockStatement(LockStatement lockStatement) + { + base.VisitLockStatement(lockStatement); + InsertBlock(lockStatement.EmbeddedStatement); + } + + public override void VisitUsingStatement(UsingStatement usingStatement) + { + base.VisitUsingStatement(usingStatement); + DoTransform(usingStatement.EmbeddedStatement, usingStatement); + } + + void DoTransform(Statement statement, Statement parent) + { + if (statement.IsNull) return; + if (context.Settings.AlwaysUseBraces) { + InsertBlock(statement); + } else { + if (statement is BlockStatement b && b.Statements.Count == 1 && IsAllowed(b.Statements.First(), parent)) { + statement.ReplaceWith(b.Statements.First().Detach()); + } + } + } + + static void InsertBlock(Statement statement) + { + if (statement.IsNull) return; + if (!(statement is BlockStatement)) { + var b = new BlockStatement(); + statement.ReplaceWith(b); + b.Add(statement); + } + } + + bool IsAllowed(Statement statement, Statement parent) + { + switch (statement) { + case VariableDeclarationStatement vds: + case IfElseStatement ies: + case WhileStatement ws: + case DoWhileStatement dws: + case SwitchStatement ss: + case ForeachStatement fes: + case ForStatement fs: + case LockStatement ls: + case FixedStatement fxs: + return false; + case UsingStatement us: + return parent is UsingStatement; + default: + return true; + } + } + + void IAstTransform.Run(AstNode rootNode, TransformContext context) + { + this.context = context; + rootNode.AcceptVisitor(this); + } + } +} diff --git a/ICSharpCode.Decompiler/DecompilerSettings.cs b/ICSharpCode.Decompiler/DecompilerSettings.cs index 62247af52..77e7f9e4d 100644 --- a/ICSharpCode.Decompiler/DecompilerSettings.cs +++ b/ICSharpCode.Decompiler/DecompilerSettings.cs @@ -178,6 +178,21 @@ namespace ICSharpCode.Decompiler } } + bool alwaysUseBraces = true; + + /// + /// Gets/Sets whether to use braces for single-statement-blocks. + /// + public bool AlwaysUseBraces { + get { return alwaysUseBraces; } + set { + if (alwaysUseBraces != value) { + alwaysUseBraces = value; + OnPropertyChanged(); + } + } + } + bool forEachStatement = true; /// diff --git a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj index bb98e3b53..b2328156b 100644 --- a/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj +++ b/ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj @@ -230,6 +230,7 @@ + diff --git a/ILSpy/Options/DecompilerSettingsPanel.xaml b/ILSpy/Options/DecompilerSettingsPanel.xaml index d18e38b3f..ec976b7b2 100644 --- a/ILSpy/Options/DecompilerSettingsPanel.xaml +++ b/ILSpy/Options/DecompilerSettingsPanel.xaml @@ -17,5 +17,6 @@ Remove dead and side effect free code Insert using declarations Fully qualify ambiguous type names + Always use braces \ No newline at end of file diff --git a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs index a420c6c43..b261faa38 100644 --- a/ILSpy/Options/DecompilerSettingsPanel.xaml.cs +++ b/ILSpy/Options/DecompilerSettingsPanel.xaml.cs @@ -63,6 +63,7 @@ namespace ICSharpCode.ILSpy.Options s.FoldBraces = (bool?)e.Attribute("foldBraces") ?? s.FoldBraces; s.UsingDeclarations = (bool?)e.Attribute("usingDeclarations") ?? s.UsingDeclarations; s.FullyQualifyAmbiguousTypeNames = (bool?)e.Attribute("fullyQualifyAmbiguousTypeNames") ?? s.FullyQualifyAmbiguousTypeNames; + s.AlwaysUseBraces = (bool?)e.Attribute("alwaysUseBraces") ?? s.AlwaysUseBraces; return s; } @@ -84,6 +85,7 @@ namespace ICSharpCode.ILSpy.Options section.SetAttributeValue("foldBraces", s.RemoveDeadCode); section.SetAttributeValue("usingDeclarations", s.UsingDeclarations); section.SetAttributeValue("fullyQualifyAmbiguousTypeNames", s.FullyQualifyAmbiguousTypeNames); + section.SetAttributeValue("alwaysUseBraces", s.AlwaysUseBraces); XElement existingElement = root.Element("DecompilerSettings"); if (existingElement != null)