Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Coverlet misreports inline functions coverage #1225

Open
AlexeyRaga opened this issue Sep 19, 2021 · 5 comments
Open

Coverlet misreports inline functions coverage #1225

AlexeyRaga opened this issue Sep 19, 2021 · 5 comments
Assignees
Labels
bug Something isn't working fsharp F# source code issue

Comments

@AlexeyRaga
Copy link

AlexeyRaga commented Sep 19, 2021

It looks like Coverlet misses the inline directive on F# functions and reports zero coverage for such functions and methods:

image

In this example above, many of the functions that I test use unwrapHandler but, zero coverage is returned.

Another example:
image

Here we see that valueCurry is used within the same method, but it is still reported as untested.

If I remove inline then it looks like:

image

So, even in my simple case, Coverlet misses 6K+ passes and reports the code as untested.

@daveMueller daveMueller added the bug Something isn't working label Sep 19, 2021
@daveMueller
Copy link
Collaborator

Thanks for reporting.

@MarcoRossignoli MarcoRossignoli added the fsharp F# source code issue label Sep 29, 2021
@daveMueller daveMueller self-assigned this Oct 4, 2021
@daveMueller
Copy link
Collaborator

daveMueller commented Oct 5, 2021

OK this is really complicated. I looked into the IL and saw that the compiler is generating a second function for inline functions with the exact same sequence point but with $W suffixed. This is described here https://github.com/fsharp/fslang-design/blob/main/FSharp-5.0/FS-1071-witness-passing-quotations.md#compiled-form-of-srtp-constrained-generic-code.

Coverlet is properly injecting the tracker in both functions but nevertheless they both never get called. Or at least for me it seems like this.

When I look into the IL without inline there is only one function without the $W. The function is also properly instrumented and the function is beeing call and thus hit.

image

Now when I look into the same IL with inline none of the two instrumented functions is called. Seems like the compiler optimized it in a way that the corresponding IL operation is used instead of a function call.

image

Maybe this is just because of the simple code example I'm using but I try to analyze this a bit more. Any information on this is highly welcome.

@AlexeyRaga
Copy link
Author

Coverlet is properly injecting the tracker in both functions but nevertheless they both never get called.

I think that this is what inline means: the compiler inlines (copies) the function body instead of the function call.

From F# documentation:

Inline functions are functions that are integrated directly into the calling code.

I also see that inline in F# works across compilation units:

F# can also inline between compiled assemblies because inline is conveyed via .NET metadata.

So based on that I would expect that there'd be no calls to the inline functions and that they are only compiled into assemblies as functions (with the flag mentioned above) to be inlined later in assemblies that use them...

But I don't know what happens at the edge cases, like:

  • What if the inline function is used by C#? Does C# understand the flag and inlines the function, or does it make a function call?
  • What happens when a method is marked with inline and not just a function? I would guess that it'd be inlined too (otherwise why enabling the keyword there), but I haven't written much OOP in F# myself...

@daveMueller
Copy link
Collaborator

Thanks for the explanation. I currently don't have any idea how we could work around this. I can't find a way to map the inline function body to the executed IL.
The only thing that comes to my mind is excluding inline functions from the instrumentation to at least get rid of the false not covered report of inline functions.
@MarcoRossignoli @petli what do you think? Any other ideas?

@daveMueller
Copy link
Collaborator

OK I just got a response from the fsharp team that they currently don't emit this information (dotnet/fsharp#12263). Now I really don't have any other idea how to deal with this except for excluding inline functions.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working fsharp F# source code issue
Projects
None yet
Development

No branches or pull requests

3 participants