Automate Your Development: Build Code Diagnostics and Code Fixes with the .NET Compiler Platform APIs
Important Points to Remember
I've given you a whirlwind tour of how the new Diagnostic and Code Fix APIs work with C# 6 and Visual Studio 2015. The more you work with these APIs, the easier they are to use. But let's go over a few items that you should remember when you work with these APIs to create your own functionality.
First, I didn't use the NormalizeWhitespace() extension method (it extends SyntaxNode) to handle the leading and trailing trivia. NormalizeWhitespace() modifies the portion of the tree handed to it, and it applies the default Visual Studio C# formatting. NormalizeWhitespace() is tough to use on spans that are smaller than a document, because it will start the first line of the span in the first column. In the example in this article, it would have moved the if statement all the way to the left margin in the document. That's not what we needed. You may think to apply NormalizeWhitespace() to the root document node. That approach works, but only if the developer hasn't made any formatting changes in the document. For these reasons, I do the extra work to handle the whitespace formatting myself. It's a bit harder, but the results are worthwhile.
Second, you may have noticed that I did a number of idiomatic casts and conversions in both the Diagnostic and the Code Fix code. Remember that you register what kind of nodes your diagnostic covers, and the location of the code where the fix operates. That gives you complete control over which node types can call your code. The casts are safe as long as you register the correct syntax type when you create the diagnostic. I prefer not to validate the casts and have the exceptions get thrown immediately when I make a mistake.
Third, the design decision that syntax trees and semantic trees are immutable has implications on the code you write. You'll often see code following this style:
// Create the statements that go in the block: var statements = new SyntaxList<StatementSyntax>(); statements = statements.Add(trueStatement.WithLeadingTrivia(blockLeadingTrivia));
It looks different, but the SyntaxList.Add() method doesn't modify the collection. It returns a new collection that contains the previous contents and the new added item. If you find that modifications to trees are not being seen, you probably followed examples from mutable APIs instead of immutable APIs.
The .NET Compiler APIs provide rich tools that enable you to diagnose and manipulate code. The APIs give you all the tools you need to find and fix errors and enforce coding rules in your organization. You'll gain new understanding for the structure and form of C# programs, and you'll have tools that make your code better.