Microsoft FxCop Sdk, Coding Standard and Custom RuleSet.

Microsoft FxCop Sdk is a library to help write programmers own ruleset i.e. we can write our own code analysis rulesets using FxCop sdk. Now the question is do we need to create our own custom ruleset? The answer depends on the environment of our work. If our work enforce about the coding standard, naming convention strictly then it is handy to have tool set which automatically check the standard etc.

In this article I am going to show how we can create custom ruleset and how to use those in Visual Studio. I use Microsoft Visual Studio 2010 as dev environment, Microsoft FxCop Sdk which comes along with VS 2010 installation. And coding rules, in this case I have two rules (1) All class names should end with 'Test' (2)
All variable names should not start 'm_' or '_'.

I created a solution named CodingStandard, in the solution I created AnalyzerEngine class library, set few dlls as reference. We need FxCopSdk and Microsoft.Cci.dll to reference in the project. Dlls are reference from C:\Program Files\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop (this path is based on my VS 2010 installation in Windows 7 box might be different on yours).

Now the coding part, I created a xml file named RuleDefinition.xml with following contents,
<?xml version="1.0" encoding="utf-8" ?>
<Rules FriendlyName="Naming Standard Rules">
  <Rule
    TypeName="TypeNameAnalyzer"
    Category="AnalyzerEngine.CodingStandard"
    CheckId="AE0001">
    <Name>All class names should end with 'Test'</Name>
    <Description>TypeNameAnalyzer rule will check whether the class name ends with Test.</Description>
    <Url></Url>
    <Resolution Name="ClassNameResolution">The name of type {0} does not end with the suffix 'Test'. Add the suffix to the type name.</Resolution>
    <MessageLevel Certainty="95">Warning</MessageLevel>
    <FixCategories>Breaking</FixCategories>
    <Email />
    <Owner />
  </Rule>
 
  <Rule
    TypeName="VariableNameAnalyzer"
    Category="AnalyzerEngine.CodingStandard"
    CheckId="AE0002">
    <Name>All variable names should not start 'm_' or '_'</Name>
    <Description>VariableNameAnalyzer rule will check whether the variable name starts with m_ or _.</Description>
    <Url></Url>
    <Resolution Name="PrivateMemberNameResolution">The name of type {0} start with m_ or _</Resolution>
    <MessageLevel Certainty="95">Warning</MessageLevel>
    <FixCategories>Breaking</FixCategories>
    <Email />
    <Owner />
  </Rule>
</Rules>

base class named AnalyzerBase.cs with the following contents,

namespace AnalyzerEngine
{
    using Microsoft.FxCop.Sdk;
    public abstract class AnalyzerBase : BaseIntrospectionRule    {
        protected AnalyzerBase(string rule)
            : base(rule, "AnalyzerEngine.RuleDefinition.xml"typeof(AnalyzerBase).Assembly)
        { }
    }
}
in the above code BaseIntrospectionRule is from FxCop.Sdk and xml file name will be Namespace + "." + XML file name so in the case namespace is AnalyzerEngine and xml file name RuleDefinition.xml => AnalyzerEngine.RuleDefinition.xml. Also xml file has to be Embedded Resource of this assembly, so right click on this xml file select Properties -> then set the Build Action as Embedded Resource.

Two other rule classes TypeNameAnalyzer.cs and VariableNameAnalyzer.cs

TypeNameAnalyzer.cs
namespace AnalyzerEngine
{
    using System;
    using Microsoft.FxCop.Sdk;
    public class TypeNameAnalyzer : AnalyzerBase    {
        private readonly string classNameEndsWith = "Test";
        private readonly string resolutionName = "ClassNameResolution";
        public TypeNameAnalyzer()
            : base("TypeNameAnalyzer")
        { }
        public override ProblemCollection Check(TypeNode type)
        {
            if (!type.Name.Name.EndsWith(classNameEndsWith, StringComparison.Ordinal))
            {
                var classNameResolutionDetails = GetNamedResolution(resolutionName, type.Name.Name);
                var classNameProblem = new Problem(classNameResolutionDetails, type);
                Problems.Add(classNameProblem);
            }
            return Problems;
        }
    }
}
VariableNameAnalyzer.cs
namespace AnalyzerEngine
{
    using Microsoft.FxCop.Sdk;
    public class VariableNameAnalyzer : AnalyzerBase    {
        private readonly string vairableNameStartsWith_m = "m_";
        private readonly string vairableNameStartsWith_underscore = "_";
        private readonly string resolutionName = "PrivateMemberNameResolution";
        public VariableNameAnalyzer()
            : base("VariableNameAnalyzer")
        { }
        public override ProblemCollection Check(Member member)
        {
            Field variable = member as Field;
            if (variable == nullreturn Problems;
            if (variable.Name.Name.StartsWith(vairableNameStartsWith_m, System.StringComparison.Ordinal) ||
                variable.Name.Name.StartsWith(vairableNameStartsWith_underscore, System.StringComparison.Ordinal))
            {
                var resolutionDetails = GetNamedResolution(resolutionName, variable.Name.Name);
                var problem = new Problem(resolutionDetails, variable);
                Problems.Add(problem);
            }
            return Problems;
        }
    }
}

the Last bit is to create a ruleset file, in this case I created a ruleset named CodingStandardRules.ruleset with following contents
<?xml version="1.0" encoding="utf-8"?>
<RuleSet Name="Coding Standard Rules" Description=" " ToolsVersion="10.0">
  <RuleHintPaths>
    <Path>C:\Common\CodingStandardRules</Path>
  </RuleHintPaths>
  <Rules AnalyzerId="Microsoft.Analyzers.ManagedCodeAnalysis" RuleNamespace="Microsoft.Rules.Managed">
    <Rule Id="AE0001" Action="Error" />
    <Rule Id="AE0002" Action="Error" />
  </Rules>
</RuleSet>

Now build this library. If we see the ruleset file from above then we can see a tag named <Path> this tag refers to the place in where we going to place our newly created assembly and the ruleset file so from where everyone(programmer in the team) can use to run the code analysis. After compiling the project we will get assembly AnalyzerEngine.dll, copy this dll along with CodingStandardRules.ruleset into the C:\Common\CodingStandardRules\ folder on my dev box. In the work environment should the folder should be somewhere in the network so then everyone can use the same information.

To test the rules I am going the create another project named AnalyzerEngine-TestHarness in the CodingStandard solution. The project has only simple Program.cs file with following contents,
namespace AnalyzerEngine_TestHarness
{
    class Program    {
        static void Main(string[] args)
        {
        }
    }
    public class CodingStandardClassname    {
        private string name = "I am string";
        private string _name = "I am string";
    }
}

Now we need to set our ruleset with this project, So right click on the project -> select Propertites -> from the new window click on the Code Analysisi tab from the left.From this window under Run this rule set  combo bex select the <Browse> option then it will pop up file dialog from there select the CodingStandardRules.ruleset file which is in this case in C:\Common\CodingStandardRules here, see the image below


Fig: Set options in Code analysis window

So all done build the  AnalyzerEngine-TestHarness project and we will get following errors,


Fig: Code analysis output.

I found following resources are really helpful for extra information in regards to FxCop,


I will try to upload the source code sometimes later. Thanks mohammad.

 Few C# and Application Design books from Amazon,

No comments:

Post a Comment