@@ -855,7 +855,6 @@ instructions[i].Operand is FieldDefinition field &&
855855 return false ;
856856 }
857857
858-
859858 static bool DisposeCheck ( List < Instruction > instructions , Instruction instruction , int currentIndex )
860859 {
861860 // Within the compiler-generated async iterator, there are at least a
@@ -891,6 +890,40 @@ static bool DisposeCheck(List<Instruction> instructions, Instruction instruction
891890 }
892891 }
893892
893+ private bool SkipGeneratedBranchesForEnumeratorCancellationAttribute ( List < Instruction > instructions , Instruction instruction )
894+ {
895+ // For async-enumerable methods an additional cancellation token despite the default one can be passed.
896+ // The EnumeratorCancellation attribute marks the parameter whose value is received by GetAsyncEnumerator(CancellationToken).
897+ // Therefore the compiler generates the field x__combinedTokens and generates some additional branch points.
898+ //
899+ // IL_0118: ldarg.0
900+ // IL_0119: ldfld class [System.Runtime]System.Threading.CancellationTokenSource Issue1275.AwaitForeachReproduction/'<AsyncEnumerable>d__1'::'<>x__combinedTokens'
901+ // IL_011E: brfalse.s IL_0133
902+ //
903+ // We'll eliminate these wherever they appear. It's reasonable to just look for a "brfalse" or "brfalse.s" instruction, preceded
904+ // immediately by "ldfld" of the compiler-generated "<>x__combinedTokens" field.
905+
906+ int branchIndex = instructions . BinarySearch ( instruction , new InstructionByOffsetComparer ( ) ) ;
907+
908+ if ( instruction . OpCode != OpCodes . Brfalse &&
909+ instruction . OpCode != OpCodes . Brfalse_S )
910+ {
911+ return false ;
912+ }
913+
914+ if ( branchIndex >= 2 &&
915+ instructions [ branchIndex - 1 ] . OpCode == OpCodes . Ldfld &&
916+ instructions [ branchIndex - 1 ] . Operand is FieldDefinition field &&
917+ field . FieldType . FullName . Equals ( "System.Threading.CancellationTokenSource" ) &&
918+ field . FullName . EndsWith ( "x__combinedTokens" ) &&
919+ ( instructions [ branchIndex - 2 ] . OpCode == OpCodes . Ldarg ||
920+ instructions [ branchIndex - 2 ] . OpCode == OpCodes . Ldarg_0 ) )
921+ {
922+ return true ;
923+ }
924+ return false ;
925+ }
926+
894927 // https://github.com/dotnet/roslyn/blob/master/docs/compilers/CSharp/Expression%20Breakpoints.md
895928 private static bool SkipExpressionBreakpointsBranches ( Instruction instruction ) => instruction . Previous is not null && instruction . Previous . OpCode == OpCodes . Ldc_I4 &&
896929 instruction . Previous . Operand is int operandValue && operandValue == 1 &&
@@ -973,6 +1006,11 @@ public IReadOnlyList<BranchPoint> GetBranchPoints(MethodDefinition methodDefinit
9731006 }
9741007 }
9751008
1009+ if ( SkipGeneratedBranchesForEnumeratorCancellationAttribute ( instructions , instruction ) )
1010+ {
1011+ continue ;
1012+ }
1013+
9761014 if ( SkipBranchGeneratedExceptionFilter ( instruction , methodDefinition ) )
9771015 {
9781016 continue ;
0 commit comments