Null checking improvements in ReSharper 2017.2

Null checking is a very common task that ReSharper has always been helping to automate. Since the very first versions, ReSharper users had a whole bunch of context actions and quick-fixes at their disposal, such as Check parameter for null, Assert expression is not null and Check variable for null.

As we’re constantly improving the coding experience, we made a number of changes in previous releases: we extended the Check parameter for null action to check all parameters at once and also added an option to insert null checks when generating a constructor. ReSharper 2017.2 advances null checking even further. Let’s see!

Null checking preferences

There are plenty of ways to perform a null check in C#: classic “if-then-throw”, “if-then-throw” with ReferenceEquals call, using different kinds of assertions — including ones from BCL, from Code Contracts library or user-defined helpers, … Not to mention that C# 7.0 adds two more with throw expressions and pattern matching.

Until now ReSharper supported only the classic pattern. The new release removes this limitation by introducing a new options page, where you can configure null checking preferences!
ReSharper preferences for null checking

You can find the Null checking page in ReSharper | Options | Code Editing | C# section, or directly from the Check parameter for null context action. It has a new menu entry, Configure null-check pattern, which helps you access this new page without digging through the dialog.

The options page lists all predefined null checking patterns in the priority order — with higher priority patterns shown at the top. The order can be changed using Move up (Alt+U) and Move down (Alt+D) buttons. When ReSharper generates a null check, it will take the highest-priority pattern which semantically suits the context, taking into account the current C# version.

Consider the following example:
Check parameter for null - null checking priorities

In the first case, ReSharper uses the Throw expression pattern because it has the highest priority by default. But in the second case, there is no assignment from a parameter in question, so ReSharper tries to apply the next pattern, Classic, which always succeeds.

Mind that only a subset of null checking patterns can be used by the Assert expression is not null quick-fix. For instance, Debug assert pattern is suitable for assertion while the Classic one isn’t. The Null checking options page displays whether the pattern can be used for assertion and in case of custom patterns allows to change this setting.

Using custom null check helper methods

Now let’s see how we can configure ReSharper to use custom helper methods. Suppose we have the following class:

The AssertionMethod and AssertionCondition attributes mark the methods and parameters. They provide a hint to ReSharper’s code analysis engine, that if the method call completes without exception, the value parameter cannot be null. You can learn more about JetBrains Annotations here.

We’d like to use the AssertNotNull method in a statement context and NotNull in expression context (since it is an extension method, invocation can be inserted right in place.)

In order to do so, first we should go to Null checking options page, and edit Custom (statement) and Custom (expression) patterns in the following way:

ReSharper understands three special placeholders:

  • $EXPR$ denotes the expression we’re checking for null.
  • $NAME$ gives the textual representation of expression (e.g. nameof(arg)).
  • $MESSAGE$ is the $NAME$ concatenated with "!= null" (e.g. nameof(arg) + "!= null").

This may look similar to another great ReSharper feature, Structural Search and Replace.

Notice that patterns use the fully qualified class name Utils.Assertion, not simply Assertion. That’s because we want ReSharper to correctly resolve the method and insert required using directives when needed.

As patterns are customized we should increase their priority using the Move up button (expression-based pattern should be on top). After that, we are ready to use them!
Using a custom null check pattern

Typing assistance with ! and ?

ReSharper 2017.2 comes with another feature that makes it easier to check parameters for null values. Inspired by languages like Kotlin and Swift, where nullable and non-nullable reference types have a clear distinction, we made typing ! or ? after a type or name in parameter declaration (or any other member declaration) insert a [NotNull] or [CanBeNull] annotation.

More than that, if parameter is already marked with [NotNull], typing ! will automatically add a null check. Note that this feature requires the presence of JetBrains Annotations in project.

Using ReSharper null assist

Download ReSharper Ultimate 2017.2! We’d love to hear your feedback on these null checking improvements.

This entry was posted in How-To's and tagged , , , , , , . Bookmark the permalink.

7 Responses to Null checking improvements in ReSharper 2017.2

  1. Repka pro says:

    C#8 now has a pretty elegant nullable syntax. I wonder if R# will be able to help out converting from [NotNull]/[CanBeNull] annotations to question-mark syntax when the day comes.

    • Andrew Karpov says:

      It’s a little too early to talk about, as design of nullable reference types is only getting shaped, but yes, we’re definitely thinking about providing feature like this. People who use JetBrains Annotations will benefit when it comes to migration.

  2. Zach says:

    There seems to be a lot of bogus warnings with the new version particularly for intptrs. These can never be null but R# now underlines every single one and said it can be null. You can’t even compare them with null so how do you get rid of the warnings?

  3. Zach says:

    Oh not to mention that it is a lot more buggy than one version ago. Auto parentheses termination cuts off at the wrong index. Very amateurish update to be honest. Haven’t seen am update so bad since when R# first started.

  4. Typo: distincition -> distinction

Leave a Reply

Your email address will not be published. Required fields are marked *