Recently I was involved in a project for a bank which has a software factory that manages hundreds of applications, developed internally or by third-parties. One of the things they needed was to enforce that every code that's checked-in to the repository followed the bank's guidelines, which involve coding standards, best practices, etc.
We found that Team Foundation Server's check-in policies can be an excellent solution for this kind of scenario. One of the policies TFS ships with out of the box is the "Code Analysis Check-In policy", which runs your Code Analysis rules every time someone attempts to check-in code. If the rules fail, the check-in does not succeed. (For those of you that haven't heard of Code Analysis, it is basically FxCop integrated into Visual Studio, version 2005 onwards).
So one of the things we had to do to enforce the bank's guidelines was to develop the rules not already covered by FxCop/Code Analysis. It turns out that this is a relatively easy task with FxCop 1.36, which provides a new API called Introspection to perform the code analysis. If you have developed custom rules for prior versions of FxCop using Reflection, you will find the new API much more powerful and easy to use.
Jason Kresowaty's guide on writing custom fxcop rules is a great place to start learning the subject, and he has also developed a great tool for aiding in the use of Introspection.
One important detail is that FxCop works against compiled Intermediate Language. This means that a huge advantage is that the rules are not dependant on the language of your code, as long that it targets the .NET Framework. On the other side, a disadvantage is that many aspects of your code are not included in the IL so you can't check against them, i.e. positioning of curly braces, parenthesis, etc. For this kind of rules we had to use other tools, like StyleCop, which I will cover in another post.
Enough for the introduction, let's go to the coding and create a simple rule: "Enum's names should not end with 'Enum'". The steps involved to implement such a rule are:
- Create a new C# class library project and add references to Microsoft.FxCop.Sdk.dll and Microsoft.CCi.dll. Both come with FxCop.
- Add an xml file, which will contain the description about your rules; the name of the file is not important. This information will be used by FxCop/Code Analysis to show your rule on the UI. It will have the following structure:
Most of the information here is pretty self-explanatory, with exception of the Resolution field, wich I will cover later.
- Add a class to your project, and make it inherit from BaseIntrospectionRule. You should add a parameterless contstructor to your class, but that calls a base constructor and provides three parameters:
- Name: name of the rule, must match the TypeName specified in the rule metadata XML.
- Xml config: the name of the metadata XML resource. It is the name of the assembly plus the name of the xml created on step 2 but without the extension.
- Xml assembly: the assembly containing the metadata XML resource.
Override the method check, and do the magic here.
When the analysis is run, this method gets called by the runtime every time a type is found (such as a class, delegate, enum, struct or interface). If you are interested in checking other elements, such as methods, there are other overloads of the Check method that you can override.
As we are only interested in Enums here, we cast the TypeNode received to an EnumType to see if we are dealing with one. In case we are, then we just have to check the type's name to see if it has the incorrect suffix.
When a rule violation is found, we must add a Problem to the Problem collection inherited from the base rule class. We must supply a Resolution object to the problem constructor. This object just builds the description that will be shown to the developer about how to fix the rule violation. Remember that in the config xml we supplied the description and placed a positional placeholder such as the one's used in String.Format?
Well, in the Resolution constructor we are supplying the parameters to fill the placeholders, in our rule we are passing-in the Enum's name.
Multiple resolutions can be defined which are assigned with a Name attribute in the xml and then accessed by code with the this.GetResolutionByName() method.
The last step is to build the assembly and copy it to c:\Program Files\Microsoft Visual Studio 9.0\Team Tools\Static Analysis Tools\FxCop\Rules. In case that you are using FxCop standalone exe, copy the dll to C:\Program Files\Microsoft FxCop 1.36\Rules.
And that's it. Open the tool (Visual Studio Code Analysis or FxCop), and you will see your newly defined rule.
Write some code that breaks it, and you will see your rule in action.
Hope you have found this post useful to get started writing custom FxCop rules. In future posts I will talk about other things that I have learned about this subject.