Improve ReSharper Static Analysis Using Annotation Attributes

August 18th 2014 ReSharper Static Analysis C#

Static code analysis is a very useful feature of ReSharper. The generated warnings can help you fix bugs which might otherwise remain unnoticed. Look at the following code:

Loop control variable is never changed inside loop

Do you see the issue? You would definitely have a more difficult time noticing it, if ReSharper didn't warn you about it: Loop control variable is never changed inside loop. It might even remain unnoticed when surrounded by all the other code.

Unfortunately static analysis is not perfect and it might detect false positives - warning you about potential issues in code, which in reality aren't issues at all. Let's take a look at another example:

Access to disposed closure

ReSharper's warning in this case: Access to disposed closure. The reason being: this code could fail if ExceptionAssert.Throws would store the lambda and execute it after scope was already disposed. Looking at its code, we can see, this is not the case:

public static T Throws<T>(Action action)
   where T : Exception
{
   try
   {
      action();
   }
   catch (T exception)
   {
      return exception;
   }
   Assert.Fail("No {0} was thrown.", typeof(T).FullName);
   return null;
}

The lambda is not being stored and is executed before the method completes, i.e. this ReSharper's warning is a false positive.

There are a couple of ways to tell ReSharper about it, so that it doesn't warn us any more. The simplest one is being offered as a quick fix: the check can be disabled with a special comment:

Disabling a warning with a comment

Although this works, it's not a perfect solution since you need to add such a comment every time you call ExceptionAssert.Throws by passing it an IDisposable.

When we know that it's always safe to pass an IDisposable to our method, we can tell that to ReSharper using its annotation attributes:

public static T Throws<T>([InstantHandle]Action action)
   where T : Exception
{
   try
   {
      action();
   }
   catch (T exception)
   {
      return exception;
   }
   Assert.Fail("No {0} was thrown.", typeof(T).FullName);
   return null;
}

This way we permanently got rid of the warning and the comment is not needed any more:

Disabling a warning with code annotations

To use the annotations, you either need to reference ReSharper's JetBrains.Annotations.dll assembly or define the annotation attribute classes directly in your code. Unfortunately there's no official NuGet package for either, but there's plenty of unofficial options available. To my experience, it doesn't really matter which one you choose, they all seem to achieve their goal just fine: you don't need to reference an assembly from a proprietary location in your project or put it in source control.

The above of course only works when you have access to the source code. When you don't, but you'd still like to improve ReSharper's static code analysis, you can use external annotations. That's how ReSharper's authors have annotated the framework assemblies for you.

Copyright
Creative Commons License