External annotations for C#
How it works
If you are using an external library, whose sources are not available to you, it does not seem feasible to use the attributes there for specifying code annotations.
In this case, you can use External Annotations, which allow you to complement the already compiled entities with attributes recognized by JetBrains Fleet's code analysis engine. External annotations let you 'cheat' the engine, by making it see the attributes (for methods, parameters, and other declarations), which were not declared at the time the library was compiled.
External annotations are specified in XML files. When loading solution, JetBrains Fleet looks for specifically named XML files in specific locations and reads annotations from there. These XML files have a structure similar to XmlDoc. For example, to annotate the method XmlReader.Create(Stream input)
from the assembly System.Xml
of .NET Framework 4.0, with the NotNull
contracts, the XML file would look as follows:
JetBrains external annotations
The approach described above was used to annotate a huge amount of symbols in the .NET Framework Class Library, as well as in other frequently used libraries. External annotations for these libraries are installed with JetBrains Fleet to ensure better code analysis. Some features (for example, ASP.NET MVC support) also rely on external annotations.
The source code of JetBrains external annotations is available under the MIT license on GitHub: https://github.com/JetBrains/ExternalAnnotations. You are welcome to contribute to this repository or use it as an example for your own external annotations.
Create external annotations
You can create external annotations to specify contracts for any compiled assembly. To make sure JetBrains Fleet recognizes the XML file with external annotations, this file should be named [Assembly.Name].ExternalAnnotations.xml. Depending on how you are going to use the annotated binaries, you can create annotations files in the following places:
If you are going to publish some binaries (for example, via NuGet), place the annotations [Assembly.Name].ExternalAnnotations.xml file in the same folder where the annotated [Assembly.Name].dll file is located.
If you want to annotate some binaries included in your project, place the annotations files for each assembly in the ExternalAnnotations folder next to the project or solution file.
When you put an annotations file in this folder, the file should be named after the assembly name, that is [Assembly.Name].xml. It can also be in a subfolder of ExternalAnnotations, but that folder must have the assembly name, that is ExternalAnnotations/[Assembly.Name]/[AnyName].xml
This way you can also keep the annotations under a VCS so that your team could benefit from them.
To illustrate creating external annotations, imagine that you use a TestLib assembly that has MyTestClass
class with static string ReverseString(String inputString)
method. Suppose, that from the assembly documentation, you know that the method is pure and never returns null, and its parameter should never be null.
Initially, JetBrains Fleet is not aware of how ReverseString
works, and therefore it finds no problems in the following code:
Create external annotations for a compiled assembly
Locate the TestLib.dll on your file system and create a file named TestLib.ExternalAnnotations.xml either in the same folder or in the ExternalAnnotations folder next to the project or solution file where TestLib.dll is referenced.
Open TestLib.ExternalAnnotations.xml for editing and type in the following:
<assembly name="TestLib"> <member name="M:TestLib.MyTestClass.ReverseString(System.String)"> <attribute ctor="M:JetBrains.Annotations.NotNullAttribute.#ctor" /> <attribute ctor="M:JetBrains.Annotations.PureAttribute.#ctor" /> <parameter name="inputString"> <attribute ctor="M:JetBrains.Annotations.NotNullAttribute.#ctor" /> </parameter> </member> </assembly>Reload your solution or disable and then enable the Smart Mode. Now JetBrains Fleet detects incorrect usage of
ReverseString
and highlights the problem with the null parameter:... and the unnecessary check of the return value:
Distribute external annotations
Another case of using external annotations is publishing external annotations for a library that you distribute, or for any library you like. In this case, the users of your library who also use ReSharper, Rider, or Fleet will get more suggestions and fixes.
External annotations for libraries can be published as and installed from NuGet packages. For more information about packaging external annotations, refer to ReSharper plugin development guide.