Code Aware Libraries are “libraries that provide guidance on correct use through embedded tooling and operates on the user’s code in real time.”
.NET Analyzers can be shipped via NuGet or VSIX. The NuGet version can “flow with your code through source control and the build server”. This will allow you to enforce best practices at all levels. VSIX deployments are more appropriate for interactive tools like suggestions and refactoring.
Consider this code:
var a1 = new int[];
var a2 = new int[] {1, 2, 3, 4, 5};
Console.WriteLine(a1.Length + a2.Length);
If you naively convert it into immutable array you’ll may end up with this:
var b1 = new ImmutableArray<int>();
var b2 = new ImmutableArray<int>() {1, 2, 3, 4, 5};
Console.WriteLine(b1.Length + b2.Length);
In cases b1 and b2, you’ll end up with a null reference exception. It isn’t easy to see, but ImmutableArray is just a struct so its default constructor can’t properly setup the internal array.
Furthermore, in the b2 case the syntax is transformed into a call to the Add method on the array, the results of which are then discarded. So even if the array was created, it won’t be populated.
With these two factors in mind, the code that should have been written is:
var c1 = ImmutableArray<int>.Empty;
var c2 = ImmutableArray.Create(1, 2, 3, 4, 5);
Using the CoreFxAnalyzers NuGet package, the compiler can detect these problems and warn you that the library isn’t being used correctly before you hit a runtime exception.
Building your own Analyzer
Using the new Roslyn SDK, you can create your own analyzer. Before you begin, you’ll need to learn how to use is the Roslyn visualizer tools. These show the abstract syntax tree for a selected block of code so that you can see what it is your analyzer will be looking for.
Initialize
When building an analyzer, you’ll need to implement the Initialize method. This is where tell the host which events and types of information you want to work with. The most common types of information are Symbol and Syntax.
Symbol information is similar to reflection and operates in a language agnostic fashion. It is used to tools with features like FxCop.
Syntax information looks at the language specific parse trees. This is used for source code tools that work like StyleCop.
You can also indicate what kind of code you wish to analyze. For example, if you are building an analyzer for the ImmutableArray constructor then you would only request constructor expressions.
Analyzer Object Creation
The AnalyzerObjectCreation method gives you information about a particular block of code. It is expressed in terms of a Node and a SemanticNode.
The Node contains the abstract syntax tree of the code being analyzed. Calling ToString on a Node will always show the original source code, even if that code has errors.
The SemanticNode allows you to ask questions about the code. For example, you can use it to determine what data type is being referred to by the expression stored in the Node.
A Cancellation token is provided at this time. Pay close attention to it, as the user may change the code in a way that will invalidate your analysis.
Code Fixes
Building a code fix is slightly more complex. You’ll need to create a CodeFixProvider which has to work at the syntax level. Since the code may need to be loaded from disk, everything happening in this provider needs to run asynchronously. This code is represented as a Document object.
To provide a fix, you return a function of type Task<Document> that returns the new version of the code.
Language Agnostic Syntax Generation
The new SyntaxGenerator API will allow you to create a new source code in a language agnostic fashion. For example, if you want an a call to “ImmutableArray.Empty” you can use generator.MemberAccessExpression to generate the correct C# or VB code depending on the context.
For the complete walkthrough for creating the ImmutableArray analyzer, watch the Channel 9 presentation .NET Compiler Platform ("Roslyn"): Analyzers and the Rise of Code-Aware Libraries.