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 Rider'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 Rider 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 Rider to ensure better code analysis. Some features (for example, ASP.NET MVC 5.2 support) are 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.
Creating external annotations
You can create external annotations to specify contracts for any compiled assembly. To make sure JetBrains Rider recognizes the XML file with external annotations, this file should be named [AssemblyName].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 *.xml file in the same folder where the annotated *.dll file is located.
If you want to annotate some binaries included in your project, place the annotations *.xml files with the corresponding name for each assembly in the ExternalAnnotations folder next to the project or solution file.
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 Rider is not aware of how
ReverseString works, and therefore it finds no problems in the following code:
To 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>
Restart JetBrains Rider and open your solution. Now JetBrains Rider detects incorrect usage of
ReverseStringand highlights the problem with the null parameter: ... and the unnecessary check of the return value:
To see, which external annotations are applied to a library symbol, you can use the Quick Documentation feature. Just press Ctrl+Shift+F1 over the symbol to study its attributes. In our case, we can see that
ReverseStringis annotated with
[Pure]attributes and the parameter is annotated with
[NotNull]: The grey color of the attributes means that they come not from assembly code, but from external annotations.