Last time (three months ago, jeez) we talked about building our first analyzer and what we get out of the box with the default analyzer template. Today we’ll talk about the second half of the analyzer project: The Code Fix Provider.
The first thing to notice is that our class inherits from
CodeFixProvider. If you take a quick look at
CodeFixProvider, you’ll see that it expects you to provide at least two things:
FixableDiagnosticsIds – A list of diagnostic IDs that we would like our code fix to deal with. We would have defined these IDs in our original analyzer.
RegisterCodeFixesAsync – Registers our code fix within Visual Studio to handle our diagnostic(s).
Let’s take a look at the first half of this file:
First, we can see that we’re exporting a code fix provider for C# with the name “Analyzer1CodeFixProvider”. We can also specify additional languages such as VB if you’re writing a multi-language code fix. Note that we have to specify the name explicitly here. (
Name is a property in
ExportCodeFixProvider. I’d actually never come across this attribute-specific syntax before.)
To start, we’ve got the title of the analyzer which is self explanatory. We’ll expose this title to Visual Studio when we register our code fix action.
Next, we’ve got to expose a list of diagnostics for which we’d like to provide our code fix. In this case, we expose the analyzer we created in the introduction to analyzers.
Finally, the default codefix template overrides the optional
GetFixAllProvider. In this case they provide a
BatchFixer computes all the required changes in parallel and then applies them to the solution at one time.
Now we’ll take a look at the last two methods given to us in
The first is
RegisterCodeFixesAsync and it accepts a CodeFixContext. The CodeFixContext has information about where we can apply our code fix, and what diagnostics are available for us to register our code fix against. CodFixContext provides a list of diagnostics for us to choose from based on what we exposed in
Based on my experiments,
RegisterCodeFixesAsync is run every time the Visual Studio light bulb appears due to a diagnostic we’ve declared interest in. At this point we can register a action to run that we’d like to apply if the user selects our code fix. We do this with
context.RegisterCodeFix(). We pass in a title, a function that returns a solution with our change and an optional equivalence key. The title is simply what will be displayed to the user when they see our fix as an option. In the default template it’s “Make uppercase” which you can see below:
Clicking on the code fix runs
MakeUppercaseAsync. There’s admittedly a lot of overhead here for what seems like a trivial change. The real work occurs in
Renamer.RenameSymbolAsync() an API that quickly and easily renames symbols for us across an entire solution. Remember that Roslyn objects are immutable, so we are given an entirely new solution (
newSolution) which we return from our method. Now Visual Studio will replace the previous solution with our updated copy.
One final note to make is regarding
equivalenceKey. The equivalence key is used to match our code fix against other code fixes and see whether or not they’re the same. To my knowledge, there’s no commonly agreed upon format for these keys. However it looks like projects such as StyleCopAnalyzers are using a similar approach to Microsoft and name theirs with a two letter code followed by a number (eg. SA1510CodeFixProvider).
And there you have it. That’s the base case analyzer that ships with Visual Studio. Obviously we can build much more powerful analyzers and code fixes, but this project should serve as a nice starting point for most people. For more advanced analyzers check out StyleCopAnalyzers, Code Cracker or the Roslyn Analyzers.