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

IDE0059 - Remove redundant assignment triggered for variable used from closure #32946

Closed
bartdesmet opened this issue Jan 30, 2019 · 2 comments
Closed
Assignees
Labels
4 - In Review A fix for the issue is submitted for review. Area-IDE Bug IDE-CodeStyle Built-in analyzers, fixes, and refactorings
Milestone

Comments

@bartdesmet
Copy link

Version Used: VS 16.0 Preview 2.0

Steps to Reproduce:

int i = 1;
Action a = () => Console.WriteLine(i);

Expected Behavior:

IDE0059 is not fired, and no suggested fix to remove a redundant assignment is made.

Actual Behavior:

IDE0059 fires and suggests to remove the redundant assignment, which results in:

int i;
Action a = () => Console.WriteLine(i);

which doesn't compile. Adding a use site outside the lambda/delegate body makes IDE0059 go away.

Context:

The bigger repro for this issue is a place where we cache delegates that have a reference to a loop variable, which is copied to a local inside the body of a for loop, like this:

Action[] GetCachedDelegates(int n)
{
    var res = new Action[n];

    for (var i = 0; i < n; i++)
    {
        var j = i;
        res[i] = () => DoSomething(j); // closure over `this` and `j`
    }

    return res;
}
@jinujoseph jinujoseph added this to the 16.0.P4 milestone Jan 31, 2019
@sharwell sharwell added the IDE-CodeStyle Built-in analyzers, fixes, and refactorings label Jan 31, 2019
@mavasani
Copy link
Contributor

Thanks for the repro cases. The original repro case is by design, hopefully the below explains why:

class C
{
    void M()
    {
        int i = 1;
        Action a = () => Console.WriteLine(i);
        // Commenting out the below code leads to assignments to 'a' and 'i' being unused.
        a();
    }
}

However, the repro case provided in Context is a false positive, our flow analysis has multiple bail out cases for lambdas/delegates escaping the method (such as assignment to fields/properties, return value, assignment to out/ref params, etc.), but it is not treating res[i] = () => DoSomething(j); as an escape, leading to the false result. I will fix the analyzer to handle such escapes.

@bartdesmet
Copy link
Author

The first case makes sense given that there are no observable side effects to constructing a delegate and its closure. However, removing i and not a as well in a quick fix seems unexpected to me. IIRC, that's exactly what I saw happen and the use site of i became invalid after taking away i.

mavasani added a commit to mavasani/roslyn that referenced this issue Feb 1, 2019
Prior implementation looked for specific unhandled operation tree shapes and was agressive, leading to false positives. New implementation handles only specific operation tree shapes and is much more conservative.

Fixes dotnet#32946
Fixes dotnet#32924
@mavasani mavasani added the 4 - In Review A fix for the issue is submitted for review. label Feb 1, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
4 - In Review A fix for the issue is submitted for review. Area-IDE Bug IDE-CodeStyle Built-in analyzers, fixes, and refactorings
Projects
None yet
Development

No branches or pull requests

4 participants