Browse Source

Fix #1563: `Unexpected return in MoveNext()` when mixed `using` and more than one `yield break`

Roslyn re-uses the same "this.Finally(); return v;" block for both "yield break;" instructions, so the yield break pattern needs to support multiple stores to the helper variable.
pull/1612/head
Daniel Grunwald 6 years ago
parent
commit
8cd8a90c22
  1. 21
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs
  2. 11
      ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

21
ICSharpCode.Decompiler.Tests/TestCases/Pretty/YieldReturn.cs

@ -335,5 +335,26 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -335,5 +335,26 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
yield return val;
}
}
public static IEnumerable<int> MultipleYieldBreakInTryFinally(int i)
{
try {
if (i == 2) {
yield break;
}
while (i < 40) {
if (i % 2 == 0) {
yield break;
}
i++;
yield return i;
}
} finally {
Console.WriteLine("finally");
}
Console.WriteLine("normal exit");
}
}
}

11
ICSharpCode.Decompiler/IL/ControlFlow/YieldReturnDecompiler.cs

@ -810,14 +810,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow @@ -810,14 +810,15 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
break;
case Leave leave:
if (leave.MatchReturn(out var value)) {
bool validYieldBreak = value.MatchLdcI4(0);
if (value.MatchLdLoc(out var v)
&& (v.Kind == VariableKind.Local || v.Kind == VariableKind.StackSlot)
&& v.StoreInstructions.Count == 1
&& v.StoreInstructions[0] is StLoc stloc) {
returnStores.Add(stloc);
value = stloc.Value;
&& v.StoreInstructions.All(store => store is StLoc stloc && stloc.Value.MatchLdcI4(0)))
{
validYieldBreak = true;
returnStores.AddRange(v.StoreInstructions.Cast<StLoc>());
}
if (value.MatchLdcI4(0)) {
if (validYieldBreak) {
// yield break
leave.ReplaceWith(new Leave(newBody).WithILRange(leave));
} else {

Loading…
Cancel
Save