mirror of https://github.com/icsharpcode/ILSpy.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
194 lines
6.1 KiB
194 lines
6.1 KiB
using System; |
|
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 |
|
{ |
|
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) { |
|
if (!IsElseIf(statement, parent)) { |
|
InsertBlock(statement); |
|
} |
|
} else { |
|
if (statement is BlockStatement b && b.Statements.Count == 1 && IsAllowedAsEmbeddedStatement(b.Statements.First(), parent)) { |
|
statement.ReplaceWith(b.Statements.First().Detach()); |
|
} else if (!IsAllowedAsEmbeddedStatement(statement, parent)) { |
|
InsertBlock(statement); |
|
} |
|
} |
|
} |
|
|
|
bool IsElseIf(Statement statement, Statement parent) |
|
{ |
|
return parent is IfElseStatement && statement.Role == IfElseStatement.FalseRole; |
|
} |
|
|
|
static void InsertBlock(Statement statement) |
|
{ |
|
if (statement.IsNull) return; |
|
if (!(statement is BlockStatement)) { |
|
var b = new BlockStatement(); |
|
statement.ReplaceWith(b); |
|
if (statement is EmptyStatement && !statement.Children.Any()) { |
|
b.CopyAnnotationsFrom(statement); |
|
} else { |
|
b.Add(statement); |
|
} |
|
} |
|
} |
|
|
|
bool IsAllowedAsEmbeddedStatement(Statement statement, Statement parent) |
|
{ |
|
switch (statement) { |
|
case IfElseStatement ies: |
|
return parent is IfElseStatement && ies.Role == IfElseStatement.FalseRole; |
|
case VariableDeclarationStatement vds: |
|
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 !(parent?.Parent is IfElseStatement); |
|
} |
|
} |
|
|
|
void IAstTransform.Run(AstNode rootNode, TransformContext context) |
|
{ |
|
this.context = context; |
|
rootNode.AcceptVisitor(this); |
|
} |
|
|
|
public override void VisitPropertyDeclaration(PropertyDeclaration propertyDeclaration) |
|
{ |
|
if (context.Settings.UseExpressionBodyForCalculatedGetterOnlyProperties) { |
|
SimplifyPropertyDeclaration(propertyDeclaration); |
|
} |
|
base.VisitPropertyDeclaration(propertyDeclaration); |
|
} |
|
|
|
public override void VisitIndexerDeclaration(IndexerDeclaration indexerDeclaration) |
|
{ |
|
if (context.Settings.UseExpressionBodyForCalculatedGetterOnlyProperties) { |
|
SimplifyIndexerDeclaration(indexerDeclaration); |
|
} |
|
base.VisitIndexerDeclaration(indexerDeclaration); |
|
} |
|
|
|
static readonly PropertyDeclaration CalculatedGetterOnlyPropertyPattern = new PropertyDeclaration() { |
|
Attributes = { new Repeat(new AnyNode()) }, |
|
Modifiers = Modifiers.Any, |
|
Name = Pattern.AnyString, |
|
PrivateImplementationType = new AnyNodeOrNull(), |
|
ReturnType = new AnyNode(), |
|
Getter = new Accessor() { |
|
Modifiers = Modifiers.Any, |
|
Body = new BlockStatement() { new ReturnStatement(new AnyNode("expression")) } |
|
} |
|
}; |
|
|
|
static readonly IndexerDeclaration CalculatedGetterOnlyIndexerPattern = new IndexerDeclaration() { |
|
Attributes = { new Repeat(new AnyNode()) }, |
|
Modifiers = Modifiers.Any, |
|
PrivateImplementationType = new AnyNodeOrNull(), |
|
Parameters = { new Repeat(new AnyNode()) }, |
|
ReturnType = new AnyNode(), |
|
Getter = new Accessor() { |
|
Modifiers = Modifiers.Any, |
|
Body = new BlockStatement() { new ReturnStatement(new AnyNode("expression")) } |
|
} |
|
}; |
|
|
|
/// <summary> |
|
/// Modifiers that are emitted on accessors, but can be moved to the property declaration. |
|
/// </summary> |
|
const Modifiers movableModifiers = Modifiers.Readonly; |
|
|
|
void SimplifyPropertyDeclaration(PropertyDeclaration propertyDeclaration) |
|
{ |
|
var m = CalculatedGetterOnlyPropertyPattern.Match(propertyDeclaration); |
|
if (!m.Success) |
|
return; |
|
if ((propertyDeclaration.Getter.Modifiers & ~movableModifiers) != 0) |
|
return; |
|
propertyDeclaration.Modifiers |= propertyDeclaration.Getter.Modifiers; |
|
propertyDeclaration.ExpressionBody = m.Get<Expression>("expression").Single().Detach(); |
|
propertyDeclaration.Getter.Remove(); |
|
} |
|
|
|
void SimplifyIndexerDeclaration(IndexerDeclaration indexerDeclaration) |
|
{ |
|
var m = CalculatedGetterOnlyIndexerPattern.Match(indexerDeclaration); |
|
if (!m.Success) |
|
return; |
|
if ((indexerDeclaration.Getter.Modifiers & ~movableModifiers) != 0) |
|
return; |
|
indexerDeclaration.Modifiers |= indexerDeclaration.Getter.Modifiers; |
|
indexerDeclaration.ExpressionBody = m.Get<Expression>("expression").Single().Detach(); |
|
indexerDeclaration.Getter.Remove(); |
|
} |
|
} |
|
}
|
|
|