Browse Source

StatementBuilder.TransformToForeach: allow both `break;` and `return;` within the using body

pull/2425/head
Daniel Grunwald 4 years ago
parent
commit
2419c2641a
  1. 22
      ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

22
ICSharpCode.Decompiler/CSharp/StatementBuilder.cs

@ -632,7 +632,7 @@ namespace ICSharpCode.Decompiler.CSharp
var enumeratorVar = inst.Variable; var enumeratorVar = inst.Variable;
// If there's another BlockContainer nested in this container and it only has one child block, unwrap it. // If there's another BlockContainer nested in this container and it only has one child block, unwrap it.
// If there's an extra leave inside the block, extract it into optionalReturnAfterLoop. // If there's an extra leave inside the block, extract it into optionalReturnAfterLoop.
var loopContainer = UnwrapNestedContainerIfPossible(container, out var optionalReturnAfterLoop); var loopContainer = UnwrapNestedContainerIfPossible(container, out var optionalLeaveAfterLoop);
// Detect whether we're dealing with a while loop with multiple embedded statements. // Detect whether we're dealing with a while loop with multiple embedded statements.
if (loopContainer.Kind != ContainerKind.While) if (loopContainer.Kind != ContainerKind.While)
return null; return null;
@ -771,22 +771,22 @@ namespace ICSharpCode.Decompiler.CSharp
// If there was an optional return statement, return it as well. // If there was an optional return statement, return it as well.
// If there were labels or any other statements in the whileLoopBlock, move them after the foreach // If there were labels or any other statements in the whileLoopBlock, move them after the foreach
// loop. // loop.
if (optionalReturnAfterLoop != null || whileLoopBlock.Statements.Count > 1) if (optionalLeaveAfterLoop != null || whileLoopBlock.Statements.Count > 1)
{ {
var block = new BlockStatement { var block = new BlockStatement {
Statements = { Statements = {
foreachStmt foreachStmt
} }
}; };
if (optionalReturnAfterLoop != null) if (optionalLeaveAfterLoop != null)
{ {
block.Statements.Add(optionalReturnAfterLoop.AcceptVisitor(this)); block.Statements.Add(optionalLeaveAfterLoop.AcceptVisitor(this));
} }
if (whileLoopBlock.Statements.Count > 1) if (whileLoopBlock.Statements.Count > 1)
{ {
block.Statements.AddRange(whileLoopBlock.Statements block.Statements.AddRange(whileLoopBlock.Statements
.Skip(1) .Skip(1)
.SkipWhile(s => s.Annotations.Any(a => a == optionalReturnAfterLoop)) .SkipWhile(s => s.Annotations.Any(a => a == optionalLeaveAfterLoop))
.Select(SyntaxExtensions.Detach)); .Select(SyntaxExtensions.Detach));
} }
return block; return block;
@ -860,10 +860,10 @@ namespace ICSharpCode.Decompiler.CSharp
/// if the value has no side-effects. /// if the value has no side-effects.
/// Otherwise returns the unmodified container. /// Otherwise returns the unmodified container.
/// </summary> /// </summary>
/// <param name="optionalReturnInst">If the leave is a return and has no side-effects, we can move the return out of the using-block and put it after the loop, otherwise returns null.</param> /// <param name="optionalLeaveInst">If the leave is a return/break and has no side-effects, we can move the return out of the using-block and put it after the loop, otherwise returns null.</param>
BlockContainer UnwrapNestedContainerIfPossible(BlockContainer container, out Leave optionalReturnInst) BlockContainer UnwrapNestedContainerIfPossible(BlockContainer container, out Leave optionalLeaveInst)
{ {
optionalReturnInst = null; optionalLeaveInst = null;
// Check block structure: // Check block structure:
if (container.Blocks.Count != 1) if (container.Blocks.Count != 1)
return container; return container;
@ -875,11 +875,11 @@ namespace ICSharpCode.Decompiler.CSharp
// If the leave has no value, just unwrap the BlockContainer. // If the leave has no value, just unwrap the BlockContainer.
if (leave.MatchLeave(container)) if (leave.MatchLeave(container))
return nestedContainer; return nestedContainer;
// If the leave is a return, we can move the return out of the using-block and put it after the loop // If the leave is a return/break, we can move it out of the using-block and put it after the loop
// (but only if the value doesn't have side-effects) // (but only if the value doesn't have side-effects)
if (leave.IsLeavingFunction && SemanticHelper.IsPure(leave.Value.Flags)) if (SemanticHelper.IsPure(leave.Value.Flags))
{ {
optionalReturnInst = leave; optionalLeaveInst = leave;
return nestedContainer; return nestedContainer;
} }
return container; return container;

Loading…
Cancel
Save