Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does FxCop not report CA2000 for this trivial case of not-disposed class instance?

The following code produces a CA2000 ("Dispose objects before losing scope") violation for the first line of Main, but not the second. I would really like the CA2000 violation for the second line, because this is a (obviously simplified) pattern often found in a large code base I work on.

Does anyone know why the violation is not produced for the second line?

public static void Main()
{
    new Disposable();
    MakeDisposable();
}

private static Disposable MakeDisposable() { return new Disposable(); }

private sealed class Disposable : IDisposable
{
    public void Dispose()
    {
    }
}
like image 315
nick.beer Avatar asked Apr 12 '15 20:04

nick.beer


1 Answers

The short answer is, CA2000 is broken and not likely to get fixed anytime soon. See this bug report which is almost exactly your question:

The warning CA2000 in particular is a rule that has known issues and that we won’t be able to fix it up in its current form.


The longer answer is that getting CA2000 right is hard. In past versions of Visual Studio, particularly 2010, false CA2000 warnings fired all over the place, and there was nothing you could do to make them go away. Search Stack Overflow for any of the dozens of questions about it.

Even in cases where you could eliminate the warning, the workaround was almost worse than just leaving it in place. The problem is, in cases like the one you have here, is that you don't want the object disposed before it leaves scope of the factory method. Except, you do -- if it throws an exception. In that case, the return value of the method is lost, and the caller has no way to dispose the object for itself.

Unfortunately, trying to figure that out means doing program flow analysis of the compiled IL, to determine if any code path (including exceptional ones) allow the object to leak. The end result was, almost any case where you tried to return an IDisposable from a method would produce the error.

Microsoft responded to this by doing two things:

  • Cranking down on the sensitivity of CA2000, so that it only fires on the most obvious of cases. This seems reasonable, since the obvious cases like your first line are more likely to be bugs than more obscure ones, and easier to fix.
  • Taking CA2000 out of their recommended code analysis rules; note that their "Extended Correctness" ruleset no longer includes CA2000.

Along with the Roslyn compiler rewrite comes the ability for tools like FxCop to do some source-level analysis, which may be easier to get correct in this case. In the mean time, the general consensus is, just turn CA2000 off.


In case you're curious, a bit of testing seems to confirm that the current (2013) CA rule only fires if a locally contained, locally constructed instance of IDisposable drops out of scope. IOW, the object can't leave the method in which you new it up, or CA ignores it. This leads me to believe that CA is simply not digging into method calls for it's analysis. Besides trying to silence false positives, it may also have been part of the overall attempt to speed up the CA process by cutting out some expensive checks, which I believe happened between 2010 and 2012?

If I add bit to your example, you can see the obvious pattern of which ones get the warning:

class Program
{
    public static void Main()
    {
        new Disposable(); // CA2000

        IDisposable x;
        MakeDisposable(out x);

        Test();
    }

    public static Disposable Test()
    {
        IDisposable z;
        var x = MakeDisposable(out z);
        var y = new Disposable(); // CA2000
        return x;
    }

    private static Disposable MakeDisposable(out IDisposable result)
    {
        result = new Disposable();

        new Disposable(); // CA2000
        return new Disposable(); 
    }
} 
like image 126
Michael Edenfield Avatar answered Sep 24 '22 07:09

Michael Edenfield