"Access to displosed closure" false positive with local function + yield return inside using

Completed

The following code returns a false positive, which caused my boss to yell at me for writing sloppy code (and I didn't even write this, it's from Stack Overflow)..

public static class BatchLinq
{
    public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
    {
        if (size <= 0)
            throw new ArgumentOutOfRangeException("size", "Must be greater than zero.");
        using (var enumerator = source.GetEnumerator())
            while (enumerator.MoveNext())
            {
                int i = 0;
                // Batch is a local function closing over `i` and `enumerator` that
                // executes the inner batch enumeration
                IEnumerable<T> Batch()
                {
                    do yield return enumerator.Current; // Access to disposed closure
                    while (++i < size && enumerator.MoveNext()); // Access to disposed closure
                }

                yield return Batch();
                while (++i < size && enumerator.MoveNext()); // discard skipped items
            }
    }
}

 

Related: https://resharper-support.jetbrains.com/hc/en-us/community/posts/206661285-Still-confused-about-Access-to-modified-closure-?input_string=%22Access%20to%20displosed%20closur - which discusses that JetBrains added three annotations to help with this. HOWEVER, this is buried and not searchable via Google when I type "Access to disposed closure" in the search engine, I see nothing about these attributes. Likewise, the ToolTip in Visual Studio 2017 says nothing about these annotaitons, either.

Past instances of false positives:

https://resharper-support.jetbrains.com/hc/en-us/community/posts/206669485--Access-to-disposed-closure-in-LINQ

https://resharper-support.jetbrains.com/hc/en-us/community/posts/206354999-Access-to-modified-closure-redux?input_string=%22Access%20to%20displosed%20closur

https://resharper-support.jetbrains.com/hc/en-us/community/posts/206705685-Erroneous-Access-to-disposed-closure-warning?input_string=%22Access%20to%20displosed%20closur

 

1
2 comments

Here is a screenshot demonstrating the issue.  Note: I can only ToolTip one of the two ToolTips in a single screenshot, but both blue underlined "enumerator" variables in the screenshot indicate the same ReSharper warning.  The "i" variable separately warns "Access to modified closure".

I believe this code is correct.  Moreover, I do not see how [InstantHandle] can be applied here, since the closure is actually a local function, and as best as I understand the C# language, you cannot apply an InstantHandle to a local function.  Moreover, since the C# language specifies that local functions may only be called from within their scope, I do not see a way for the closure created to be accessed from a disposed scope.

Ergo, I believe this is a test case JetBrains ReSharper has not accounted for: local functions with free variables and a yield statement generate false positives inside a using.  Note, if you play with my sample, and remove the yield statement (effectively writing non-sensical code for the purpose of Batching, but just to demonstrate the problem with ReSharper code analysis engine), the error goes away despite enumerator.MoveNext staying as a free variable reference inside the local function.

Variation 1: yield return new T() - enumerator.MoveNext() is underlined in blue

                    IEnumerable<T> Batch()
{
if (++i < size && enumerator.MoveNext()) // still says access to disposed closure
yield return new T(); // T must have a "where T : new()" constraint
yield return new T();
}

 

Variation 2: remove yield, just return List<T> -- no errors, despite i and enumerator still being free variables

                    IEnumerable<T> Batch()
{
if (++i < size && enumerator.MoveNext())
return new List<T>();
return new List<T>();
}
1

Hello!

Corresponding issue is reported here - https://youtrack.jetbrains.com/issue/RSRP-473776.

Thank you.

0

Please sign in to leave a comment.