Create A Static Code Analyzer Using Dotnet For Your Special Scenario

With a real world use case.

Boris Donev
5 min readFeb 21, 2021

I believe you already know about static analyzers — they analyze your code while you type it, and are able to give you errors or warnings before you even build, along with quick fixes.
They can warn you about simple fixes, such as when using statement is unnecessary, up to ones that can logically analyze your code and warn you about possible bugs, such as using possible null object or when you can simplify some of your code.
Analyzers are usually added as NuGet packages to your projects, but could also be added as extensions to Visual Studio. Adding as extension is not recommended in team environments, since you would like all members of the team to have the same analyzers and type consistent code.

As there’s a whole array of static analyzers out there, written by Microsoft, Google, or other companies whose only concern is to produce analyzers and make you follow best practices, why would you want to build your own static analyzer?
For your own custom scenario of course.

This was a case with us some time ago. We decided that we wanted to use a static class which will give us the date and time, rather than using System.DateTime directly in code. It looked like this.

Simple stuff

We wanted to prevent developers from using local dates around the system (always use UTC) and to be able to mock the date and time for unit testing. We could have gone with a date service or Noda time instead— but we chose the static class. Whether that’s a good or bad decision is irrelevant now. The important thing is, now that we made this decision, how do we enforce it so the developers won’t just revert to using the good old DateTime as they are used to? Even though we had strict peer reviews in place, you cannot rely on people to be consistent. You cannot rely that they will spot every single usage of System.DateTime, however good they are, because there will be times when we are in a hurry, developers will move in and out of teams, they won’t spot it or will forget after some time. To mitigate this, we came up with the idea of writing our own static code analyzer. It was to prevent the developers from assigning System.DateTime, and make sure they always used MyProjectDate. DateTime could still be used as a return type, it just couldn’t be assigned.
Not a single pull request was blocked since then because of MyProjectDate not being used, or because of DateTime being assigned.
Our use case was with dates, but you can obviously adapt this to you own scenarios.

Now that you know at least one real world use case of why you would like to build your own static code analyzer, let’s see how easy it is to build one. In the rest of this blog post we will build our own static analyzer, provide our own quick fix, write tests for it and use it in a project. You can see the whole code here. Try it out and bend it to your needs.

First step is to create the analyzer project, which has it’s own template in Visual Studio.

There are some additional components that you need to have installed.

Once you create the template project, most of the boilerplate code is set up for you, so you just need to write the main method that analyzes the code and catches the errors.
In our analyzer, we raise an error if the node is a DateTime and if it is an assignment. If it is a return type, we don’t raise an error.

The way the analyzer works is that it sees your code as a tree with nodes, and iterates over each one.

Simple showcase of how the analyzer sees your code

It starts with the assignment, in this case DateTime.Now or 5 being one node each. Then it goes to the operator (=), and then the local variables, which are all separate nodes. It moves over to the input parameters, the method and its return type and accessor, over to the class, namespaces and so on. Each of these nodes has many properties that you can inspect in your analyzer and take action accordingly. There are many, many possibilities and options for which you can build your own analyzers.

Once you detect a faulty scenario, you should provide a quick fix for it, so the developers will know what is the fix for the problem.

Good thing is that tests are also provided with the template project, so you can immediately write some for your analyzer, thus ensuring that any future changes are less likely to break it.

You can then distribute it as a NuGet or a Visual Studio extension.

Now that you have written some analyzer code, how can you try it out and see if your analyzer works? Just set the Vsix project as starting project, and run the solution.

It will open another Visual Studio instance. Create new project in this new Visual Studio instance (can be console project) and start typing. This new Visual Studio instance already has your analyzer installed as an extension, so it will be in action as you type.

If you run the Vsix project from the demo project, create a new console project, and write the following code, it should raise an error as in the screenshot below.

If we assign a DateTime, an error is raised immediately with a nice description telling us what is wrong

If you try to fix the error with a quick fix suggested by Visual Studio, it should be your own static class.

The static class does not exist in this console project, but it will in your original solution

Conclusion

Having this kind of analyzer will make the new members of the team learn about decisions that were made even before they were part of the team, without needing to read documentation. They will know about the decision as soon as they need to use it.
The most important thing is that it is installed right there in the project, and no one needs to remember to let the new team members know that they must use MyProjectDate rather than DateTime. Also, this forces the decision, and no junior or senior developer, or even the CTO for that matter, can use DateTime instead of MyProjectDate.

--

--

Boris Donev

As a technical team lead of several startup projects, I've come across different issues which I'll try to document and maybe help someone else.