ReSharper allows you to annotate the code for several conditions and it can validate your code against those annotations. These annotations are implemented in the .NET system as Attribute classes that you can attach to various items. For example, there is an annotation that represents that a value cannot be null any time. If try to assign a null value to a variable that is marked with the NotNullAttribute, you will receive a warning from the ReSharper tool that you are violating the contract. Of course, you can ignore this, but that is highly unpractical. By knowing which objects cannot be null any time, the tool also warns you, if you are checking a value against being null, when it is not going to be null ever. Using these annotations allows you to write effective code.
The ReSharper tool has a setting regarding to the nullability of the different elements of the code, which is defining how the tool should behave. In the Optimistic setting, it is going to only warn you when an element is marked with the CanBeNullAttribute and you don’t implement a null checking when you try to refer to different attributes of the object. These scenarios can easily lead to NullReferenceException. However, in this setting the tool does not do anything when there is no mark on the element. On the other hand, when the tool is set to Pessimistic, it only going be silent if you either check for null values or the element is marked with NotNullAttribute. Programming with this setting and not ignoring any warning, will lead to a stable application. At least in term of not having NullReferenceException. Some of the third-party libraries are also using the same annotation system as JetBrains made it available in the form of a NuGet package named JetBrains.Annotations. If your third-party library, however, does not have support for the annotations, it is really annoying to always either mark the warning as safely ignorable or check for null values when there is no possibility for them. Enters External Annotations. With External Annotations, you can place an XML file near the DLL, named as <DLL name without extension>.ExternalAnnotations.xml, and develop the information in it about where and which annotations should be in the DLL itself. With this information, ReSharper is behaving exactly as if the developer of the third-party code would have added the annotations herself.
The fact that the XML annotation file needs to be near the DLL, however, creates a problem when using NuGet packages over a wide array of different projects. They need to be in the packages directory, which you generally do not add to the versioning control system, because NuGet.org has a policy not to remove package from the source – except in extreme conditions. This means, that you can generally restore the NuGet packages, but you will lose the annotations. ReSharper allows you to install annotations into its global directory, but manually copying there the annotations and maintaining them is also cumbersome.
An easy way to maintain those files is to build a ReSharper extension that is containing the annotations and deploy it the same way as JetBrains delivers updates to you on the same information.
Building the Extension Package
So, what is a ReSharper extension package? Well, believe or not, it is a NuGet package itself too that has some small set of specific requirements. It will be installed by ReSharper and not the standard NuGet tools, and you will need to follow some rules to have ReSharper recognize it as an extension. Other than this, nothing special, everything can be done with the standard NuGet building system as you already have. Or if you don’t, have a look at my previous articles on how to build a build server for yourself with NuGet package building capability. You might also want to read my other build server articles to have additional capabilities, like automatic build numbering, etc.
Let’s jump into creating the package
We are starting from the situation that we have already an ExternalAnnotations.xml file called ThirdPartyLibrary.ExternalAnnotations.xml. We would like to pack this into an extension. If you don’t have such a file, you can write one for yourself based on JetBrains resources, a good starting point for reading is the article announcing the open sourcing of the external annotations. Alternatively, if you just want to play with the system, you can use my annotations for which the source is available in my GitHub repository.
The NuGet Package Descriptor
The above file is how your nuspec file would look like for packaging the ThirdPartyLibrary.ExternalAnnotations.xml. The parts are:
<id>: This will be the NuGet package id, and it will be used by ReSharper to identify your extension package.
<version>: This is the version of the package, it will be shown as a major.minor.patch number, filling the empty parts with zero values. A practical value would be the date of the build in format of yyyy.mm.dd, because that would increase correctly.
<title>: This is the title of the package and will be shown as headline in the ReSharper extension installer.
<authors>, <owners>: These are both required by the NuGet packaging tool, but only the <authors> will be displayed in the ReSharper extension installer as copyright holder for the extension.
<requireLicenseAcceptance>: required by the NuGet packaging tool, but will be ignored by the ReSharper extension manager.
<projectUrl>: This is the URL of your project, and will be linked in the ReSharper extension manager as the “More Info…” link of the plugin description.
<licenseUrl>: This is the URL of your project license, and will be linked in the ReSharper extension manager as the “License…” link of the plugin description.
<description>: This will be used as additional information below the headline in your extension description in the ReSharper extension manager. Keep in mind that only one line is visually visible in the extension manager user interface here, so long description will be partially visible only.
<releaseNotes>, <copyright>: Required by the NuGet packaging tool.
<dependencies>: To recognize the NuGet package as a ReSharper extension, you need to have the Wave package referred here. The current version of the Wave system is 8. By using [8.0] as package version, you declare that the extension is compatible with version 8 only.
<files>: This is where you mark the external annotation files to be packaged. JetBrains in their packages for the annotations follows the path scheme of DotFiles/ExternalAnnotations/Package/NameSpace/AnnotationComponent.xml. You can follow that, or in the above example, I used only the DotFiles/ExternalAnnotations/Package part, because the annotations are not as large as for example for the .NET Framework.
Building the Extension
Once you have the nuspec file, place it in the same directory as the annotation file. The extension can be built by executing the following command:
Distributing the Extension
There are two ways to distribute the extension and both are based on the NuGet system:
Using possibly shared directory or URL: In Visual Studio, go to ReSharper/Options…/Extension Manager and click on the Add button. Add the name and the source. Then, when you start the Extension Manager next time (ReSharper/Extension Manager…), the location will be scanned for extensions.
Use the extension market at https://resharper-plugins.jetbrains.com. If you ever released a NuGet package over NuGet.org, the interface will look familiar to you, and it is probably not a coincidence. You can configure automatic upload in the same way into your build server, as I documented in my article for distributing the NuGet packages.
Happy packaging and sharing of useful annotations. In case you are interested in the packaged external annotation, my extension on ReSharper Gallery is here.