Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to indicate to R# that a function checks a variable for null

In our codebase, we have a battery of custom error checking functions (like those listed here) to check arguments less verbosely. For example, to check an argument for null I use:

Throw.IfNull(theArgument, "theArgument");

The one disadvantage of this approach is that R# gives the warning "possible NullReferenceException" on future uses of the value because it's not smart enough to detect this as a null check (or at least something that would fail if theArgument were null). Is there any way to indicate that this method checks against the argument being null? For example, when I try to run a static extension like Select() on such a value, R# warns me of 'possible null assignment to entity marked with NotNull attribute', but I can't find any documentation of such an attribute nor do I see it in the reference source for Enumerable.Select().

like image 863
ChaseMedallion Avatar asked Mar 12 '14 18:03

ChaseMedallion


1 Answers

What you're asking can definitely be solved by applying ReSharper Annotations! Those are attributes that provide additional hints for ReSharper's analysis, allowing you to add ReSharper "goodness" onto your own methods and classes. I have recently recorded a webinar with JetBrains called ReSharper Secrets where I talk about and demonstrate annotations, you're welcome to watch it!

As to your question, there are 3 annotation attributes you can apply to solve your issues (and add more cool features).

Supposing the definition for IfNull is something like:

public static class Throw
{
    public static void IfNull<T>(T parameter, string parameterName) where T : class
    {
        if (parameter == null) 
            throw ArgumentNullException(string.Format("Parameter {0} is null", parameterName));
    }
}

You can decorate it with 3 ReSharper attributes, ContractAnnotation, NotNull and InvokerParameterName like this:

[ContractAnnotation("parameter: null => halt")]
public static void IfNull<T>([NotNull] T parameter,
                             [InvokerParameterName] string parameterName) 
    where T : class
{
    ...
}

Here is what those attributes do:

The first, [ContractAnnotation], tells ReSharper that if parameter is heuristically null, then this method halts the program execution, i.e. throws exception (at runtime). This is what prevents the "possible NullReferenceException" warning. The language used for defining Contract annotations is explained here.

The second is [NotNull], tells ReSharper that parameter must not be heuristically null. This gives the "Possible null assignment to entity marked with [NotNull] attribute" warning.

The third, [InvokerParameterName] is telling ReSharper that the parameterName argument is the name of one of the parameters from the calling (invoking) method, so it will provide code completion that lists all the calling method parameters. This will give a warning in ReSharper if the name is not a parameter, for example, a local variable name.

Here's a short video of these attributes in action (applied to another set of APIs, but the idea is exactly the same): http://screencast.com/t/NhGVaUr7GO3b

like image 123
Igal Tabachnik Avatar answered Oct 14 '22 09:10

Igal Tabachnik