Skip to content

Async breaks MustNotResolveCurrentTimeViaDateTimeConventionSpecification #62

@gkinsman

Description

@gkinsman

Hi there!

Given a type:

public class Foo
{
    public void Bar()
    {
        var thing = DateTime.Now;
    }
}

the following IL is generated:

Foo.Bar:
IL_0000:  nop         
IL_0001:  call        System.DateTime.get_Now
IL_0006:  stloc.0     // thing
IL_0007:  ret  

which is easily detected by the MustNotResolveCurrentTimeViaDateTimeConventionSpecification which searches for any get_Now calls on DateTime.

What happens if this method is made async though (ignoring the void return type)?

public class Foo
{
    public async void Bar()
    {
        var thing = DateTime.Now;
    }
}

Foo.Bar now looks like:

Foo.Bar:
IL_0000:  newobj      UserQuery+Foo+<Bar>d__0..ctor
IL_0005:  stloc.0     
IL_0006:  ldloc.0     
IL_0007:  ldarg.0     
IL_0008:  stfld       UserQuery+Foo+<Bar>d__0.<>4__this
IL_000D:  ldloc.0     
IL_000E:  call        System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Create
IL_0013:  stfld       UserQuery+Foo+<Bar>d__0.<>t__builder
IL_0018:  ldloc.0     
IL_0019:  ldc.i4.m1   
IL_001A:  stfld       UserQuery+Foo+<Bar>d__0.<>1__state
IL_001F:  ldloc.0     
IL_0020:  ldfld       UserQuery+Foo+<Bar>d__0.<>t__builder
IL_0025:  stloc.1     
IL_0026:  ldloca.s    01 
IL_0028:  ldloca.s    00 
IL_002A:  call        System.Runtime.CompilerServices.AsyncVoidMethodBuilder.Start<<Bar>d__0>
IL_002F:  ret

with the call to DateTime.Now pushed to the state machine, here it's the first step:

d__0.MoveNext:
IL_0000:  ldarg.0     
IL_0001:  ldfld       UserQuery+Foo+d__0.<>1__state
IL_0006:  stloc.0     
IL_0007:  nop         
IL_0008:  ldarg.0     
IL_0009:  call        System.DateTime.get_Now
IL_000E:  stfld       UserQuery+Foo+d__0.5__1
IL_0013:  leave.s     IL_002D
IL_0015:  stloc.1     
IL_0016:  ldarg.0     
IL_0017:  ldc.i4.s    FE 
IL_0019:  stfld       UserQuery+Foo+d__0.<>1__state
IL_001E:  ldarg.0     
IL_001F:  ldflda      UserQuery+Foo+d__0.<>t__builder
IL_0024:  ldloc.1     
IL_0025:  call        System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetException
IL_002A:  nop         
IL_002B:  leave.s     IL_0041
IL_002D:  ldarg.0     
IL_002E:  ldc.i4.s    FE 
IL_0030:  stfld       UserQuery+Foo+d__0.<>1__state
IL_0035:  ldarg.0     
IL_0036:  ldflda      UserQuery+Foo+d__0.<>t__builder
IL_003B:  call        System.Runtime.CompilerServices.AsyncVoidMethodBuilder.SetResult
IL_0040:  nop         
IL_0041:  ret

This makes the convention pass when it shouldn't, as the call to the prohibited method is now in a separate type to that that the search is being performed on.

Is there some way to follow the generated state machine's type and search within the generated code? Maybe this is a convention better suited to roslyn, as it has the raw syntax tree?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions