ReSharper NullReferenceException Analysis and Its Contracts

If you use ReSharper, you are likely familiar with its Possible ‘NullReferenceException’ inspection. I want to talk about the analysis engine that displays this type of warnings and how you can, with just a bit of work, use it for best results.

Consider this sample code:

public string Bar(bool condition)
{
 string iAmNullSometimes = condition ? "Not null value" : null;
 return iAmNullSometimes.ToUpper();
}

ReSharper will reasonably highlight iAmNullSometimes in the second line of the method with the ‘NullReferenceException’ warning. Now let’s extract the method:

public string Bar(bool condition)
{
 string iAmNullSometimes = GetNullWhenFalse(condition);
 return iAmNullSometimes.ToUpper();
}

public string GetNullWhenFalse(bool condition)
{
 return condition ? "Not null value" : null;
}

The warning no longer pops up. Why?

The Analysis Engine

The analysis engine tries to determine what values variables could have. Let’s see what level of abstraction its knowledge of variable values is reduced to. From its point of view, a variable can be in one or more of the following states:

  • NULL, NOT_NULL — either null or non-null value.
  • TRUE, FALSE — the same for bool type.
  • UNKNOWN — this value is introduced for optimistic analysis, which is used to minimize the risk of incorrect analysis.

In the end, the analysis engine determines a set of possible states for a variable’s every use point.

In the first code sample above, after iAmNullSometimes is initialized it will have two possible states: NULL or NOT_NULL. Therefore, the Possible NullReferenceException highlighting tells us that there exists at least one execution path in which iAmNullSometimes will be null (in this case, when condition is FALSE).

The second code sample is more complex. The analysis engine doesn’t know what kind of values GetNullWhenFalse returns. Of course, we could analyze it and make sure it could also return a null. But this would increase the number of methods being analyzed (which also call something in their turn), and thus greatly increase analysis time. Unfortunately we cannot afford that kind of on-the-fly analysis on today’s PCs (to have ReSharper highlight all possible errors). Note also that the method being called could potentially belong to a library referenced in our project. It would be unwise to decompile and analyze that library on the fly too.

There is another possible option: to assume that the external methods, which we know nothing about, return either NULL or NOT_NULL. That’s how pessimistic analysis works.

By default, ReSharper uses optimistic analysis. In this kind of analysis, if we don’t know anything about a method, its return value will be in the special state called UNKNOWN. If a variable is in the UNKNOWN state at the time of use, it isn’t highlighted (unless of course there are other ways in which it can be assigned the null value, either explicitly or from a CanBeNull type of method). That’s what makes the analysis engine ‘let its guard down’ in the second code sample.

Whether we are doing optimistic or pessimistic analysis, we want to somehow find out what the method being called is capable of doing, so that ReSharper can find more potential errors. This is where contracts come in.

Contracts

ReSharper analysis engine can use additional knowledge of the called methods, which it obtains through contracts such as “method never returns null,” “method can return null” or “method doesn’t take null parameters.” In the basic case these contracts are defined through the attributes JetBrains.Annotations.CanBeNullAttribute and JetBrains.Annotations.NotNullAttribute. Applying one of these to a method specifies whether it can return a null. Applying one to a parameter specifies whether it can be null. You can also apply these attributes to properties and fields. They are defined in the library JetBrains.Annotations.dll which is located in <ReSharper install directory>Bin.

Our second code sample above can be improved by marking GetNullWhenFalse with the CanBeNull attribute:

public string Bar(bool condition)
{
 string iAmNullSometimes = GetNullWhenFalse(condition);
 return iAmNullSometimes.ToUpper();
}

[CanBeNull]
public string GetNullWhenFalse(bool condition)
{
 return condition ? "Not null value" : null;
}

Now, when we use a method of iAmNullSometimes, the Possible ‘NullReferenceException’ highlighting appears.

Unless you want to maintain an extra assembly with your project, which doesn’t do much for your application’s functionality during runtime, you can declare these attributes right in your project. But basically, the analysis engine will make use of any attributes from any assemblies, as long as their names match those in JetBrains.Annotations.dll. The definitions of these attributes can be easily accessed via the Copy default implementation to clipboard button located on this ReSharper Options page:

External Annotations

If you’re using an external library (e.g. mscorlib.dll), it doesn’t seem feasible to specify contracts for its entities using attributes. Enter External Annotations. This ReSharper feature allows you to complement the already compiled entities with attributes used by ReSharper’s analysis engine. External Annotations let you ‘cheat’ the engine, by making it see the attributes (for methods, parameters and other declarations) which weren’t declared at the time the library was compiled. To do this, the attributes must be specified in an XML file located in <ReSharper install directory>BinExternalAnnotations.

Such is the definition of contracts for standard libraries stored in this folder when ReSharper is installed. These contracts are obtained based on source code analysis as well as Microsoft Contracts. The contracts obtained using the former technique are stored in files of the type *.Generated.xml, while those obtained using the latter technique are stored in files of the type *.Contracts.xml.

The files that describe additional attributes have a structure similar to XmlDoc. For example, for the method XmlReader.Create(Stream input) from the assembly System.Xml of .NET Framework 4.0, NotNull contracts are defined as follows:

<assembly name="System.Xml, Version=4.0.0.0"> <!—The attribute name contains the assembly name. If you don’t specify the version, this file's attributes will be applied to all versions of the assemblies of that name -->
<member name="M:System.Xml.XmlReader.Create(System.IO.Stream)"> <!—This shows the name of the member whose attributes are complemented; the notation is the same as XmlDoc -->
 <attribute ctor="M:JetBrains.Annotations.NotNullAttribute.#ctor" /> <!—attribute constructor names are also specified using XmlDoc notation -->
 <parameter name="input">
  <attribute ctor="M:JetBrains.Annotations.NotNullAttribute.#ctor" />
 </parameter>
</member>
</assembly>

To make sure ReSharper recognizes the file, you should store it either as <ReSharper install directory>BinExternalAnnotations<Assembly name>.xml OR as <ReSharper install directory>BinExternalAnnotations<Assembly name><Any name>.xml, where <Assembly name> is the assembly name without the version. If you use the latter kind of location structure, you will be able to specify multiple sets of contracts for the same assembly. This may be useful if you need to use different contracts for different assembly versions.

Right now, editing these files isn’t as easy as we’d like — it involves a lot of manual work plus requires administrator-level privileges. But we’re planning to release a handy tool to simplify this pretty soon. Most likely it will come in the form of a ReSharper plug-in.

Use Cases

Here are some External Annotations use cases that will help you get more from ReSharper.

XmlDocument.SelectNodes(string xpath)

The CanBeNull annotation for this method gives rise to many bug reports. The thing is that SelectNodes is a method of XmlNode and can generally return a null (for example, for XmlDeclaration). But most of the time we use this method in such a way that it cannot return a null — from XmlDocument. One solution would be to delete this annotation from External Annotations or replace it with NotNull. But there is a better way: to write an extension method for XmlDocument:

public static class XmlUtil
{
 [NotNull]
 public static XmlNodeList SelectNodesEx([NotNull] this XmlDocument xmlDocument, [NotNull] string xpath)
 {
  // ReSharper disable AssignNullToNotNullAttribute
  return xmlDocument.SelectNodes(xpath);
  // ReSharper restore AssignNullToNotNullAttribute
 }
}

In this case it would also make sense to create additional methods such as SelectElements and SelectAttributes, to avoid typecasting every time, but that’s another story.

Assertion

If you use your own Assert methods or those from an external framework, they can be marked with the attributes AssertionMethodAttribute or AssertionConditionAttribute. Top of the list candidates for this would be Contracts.Assert methods, as long as you use Microsoft Contracts:

<assembly name="Microsoft.Contracts">
<member name="M:System.Diagnostics.Contracts.Contract.Assert(System.Boolean)">
 <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
 <parameter name="condition">
  <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
  <argument>0</argument>
  </attribute>
 </parameter>
</member>
<member name="M:System.Diagnostics.Contracts.Contract.Assert(System.Boolean,System.String)">
 <attribute ctor="M:JetBrains.Annotations.AssertionMethodAttribute.#ctor"/>
 <parameter name="condition">
  <attribute ctor="M:JetBrains.Annotations.AssertionConditionAttribute.#ctor(JetBrains.Annotations.AssertionConditionType)">
  <argument>0</argument>
  </attribute>
 </parameter>
</member>
</assembly>


If you have methods that always throw exceptions, you can also consider TerminatesProgramAttribute.

Author: Alexey Kuptsov, ReSharper developer. Translated from original article (in Russian)

Source code in this post is highlighted with Source Code Highlighter.

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

19 Responses to ReSharper NullReferenceException Analysis and Its Contracts

  1. Sven says:

    Thank you very much for this article. Very helpfull. :)

  2. Dave Van den Eynde says:

    Is it possible to add an analysis rule that gives a warning if a method returns null in some path and does not have the CanBeNull attribute applied?

  3. andrew says:

    Regarding null reference, this bug http://youtrack.jetbrains.net/issue/RSRP-111853 still exists.

    It seems that there is something missing in an external annotations file.

  4. David in Chicago says:

    Will the next version of Re# recognize .NET 4.0 code contracts natively?

    Take this code:

    1 public void DoSomething(MyClass thing)
    2 {
    3 Contract.Requires(thing != null);
    4
    5 _privateMember = thing.ToString();
    6 }

    There is no possibility of thing being null at line 5 if you have contracts set up properly in VS2010. (There is no possibility of the thing compiling if you don’t. :)

    ReSharper 5.1 warns about a possible null reference exception at line 5, however. But it doesn’t if line 3 is instead:

    if (null == thing) throw new ArgumentNullException(“thing”);

    Will a new version of R# that recognizes contracts come out in the near future?

  5. Joe White says:

    Isn’t there a way to put the external annotations into source control, the same way we can do with the .resharper file? We put third-party libraries in source control so that we don’t have to manually install them on every computer… and so that, if we check out an old version of our code, we get the correct version of the third-party libraries it depends on. Shouldn’t annotations get the same benefits?

  6. Eugene Pasynkov says:

    Joe White:

    You can place the external annotations file in the same location as dll.
    If the dll is named XXX.dll, then external annotation should be named XXX.ExternalAnnotations.xml

  7. Eugene Pasynkov says:

    David in Chicago:
    right now, ReSharper doesn’t handle Contracts in a native way :(

  8. Joe White says:

    @Eugene, cool — good to know that version control is supported for this. Thanks!

  9. Alexey Kuptsov says:

    Dave Van den Eynde:
    This analysis rule is planned as a part of ReSharper plug-in for simplifying the process of annotating.

  10. Peter says:

    For a moment there, I thought http://youtrack.jetbrains.net/issue/RSRP-108572 was finally addressed. I read thru a few times, but, no. And the example ‘solution’ I posted there doesn’t work quite as fine as we thought it did (I jumped the gun when my co-worker said it worked fine).

    To wit: is there any way to make the analysis figure out that I have extension methods that return boolean values based on the items being null or not. I prefer to use literate .IsNull and .IsNotNull methods in code, and well, they don’t work out too well with nullReferenceException analysis.

  11. Alexey Kuptsov says:

    Peter,
    At this moment there is no way to make annotations that mean “return value that depends on argument nullness”. The API that can customize NRE analyzer for complex preconditions and postconditions is planned to be developed in future.

  12. David says:

    Thank you Jura. This article was very helpful. As Andrew says, Regarding null reference, this bug http://youtrack.jetbrains.net/issue/RSRP-111853 still exists for me also.
    It looks like that there is something missing in an external annotations file. Please check this and solve my error totally. Thanks…

    http://godwinsblog.cdtech.in/2010/12/requested-page-cannot-be-accessed.html

  13. Hello
    Currently we have in an assembly an Ensure class which has contract methods of our own. This class resides in our Utilities.dll. The other projects do directly reference the utilities project by reference.

    If we define external anotations for Utilities.dll the code in the other projects does not honor our annotations. Any ideas? Does this only work with external libraries?

    Thanks for your help
    Daniel

  14. Alexey Kuptsov says:

    Daniel,
    Yes, external annotations work only with external libraies. If you can change your Utilities library code, annotate it with our attributes.
    External annotations are just a gear for adding attributes to compiled entities. It is supposed that it is hard to synchronize a source code and xml-files.

  15. Tom R says:

    Is there a way to specify external annotations at a project or solution level? If not is it planned? It would be nice to be able to commit annotations to source control and have them propagate to everyone that checks out the project.

  16. Markus Springweiler says:

    >> But we’re planning to release a handy tool to simplify this pretty soon.

    This “soon” was a year ago. Any news on this?

    >> Most likely it will come in the form of a ReSharper plug-in.

    On the plugins page only 1 of 16 plugins is compatible with R# 6 at all.

  17. @Markus
    I’ll inquire what’s the story with the tool that was so unfortunately pre-announced in this post.

    As to the plug-ins, actually more than one of them is compatible with v6 but feels like the page needs to be updated. In addition, some of the plug-ins listed here are no longer relevant: for example, ReSharper Settings Manager should not be anymore required with version 6.1 that introduces an easier way to manage/share ReSharper settings.

  18. @Markus
    Looks like we’ve had to push the tool down in favor of other features in 6.0.
    Hopefully we can return to the idea in future when we’re more focused on extending code annotations.

  19. Robert Slaney says:

    Is this still the case for v7 ?

Leave a Reply

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">