Custom FxCop rules

by ingvar 3. november 2010 09:41

FxCop is a good way of ensuring the health and quality of your code. FxCop ships with a bunch predefined rules. But sometimes these rules are not enough. Fortunately there is a SDK for creating you own rules for FxCop. It's pretty simple to create your own rules and in the following I will describe a step-by-step guide to create custom FxCop rules.

Step 1: Creating a Visual Studio project

You start by creating a new class library project (call it MyFxCopRules) and then add the three following references:

  • FxCopSdk.dll
  • FxCopCommon.dll
  • Microsoft.Cci.dll

These can be found in the FxCop install directory (c:\Program Files\Microsoft FxCop 1.36).

 

Step 3: Adding a new cs class file

Add a new cs class file and here I will name this class DoNotCallXDocumentLoadWithPath.

 

Step 4: Implementing the rule

Here is the implementation of the rule. There is one important detail to the code (See below).

namespace MyFxCopRules
{
    public class DoNotUseMethodWithStringBaseRule : BaseIntrospectionRule
    {
        protected string ClassName { get; set; }

        public DoNotUseMethodWithStringBaseRule(string name, string className)
            : base(name,
                   "MyFxCopRules.FxCopRules",
                   typeof(DotNotUseStreamWriterClass).Assembly)
        {
            this.ClassName = className;
            this.MethodToExclude = new List<string>();
        }


        public List<string> MethodToExclude { get; set; }


        public override ProblemCollection Check(Member member)
        {
            Method method = member as Method;
            if (method == null) return this.Problems;

            for (int i = 0; i < method.Instructions.Count; i++)
            {
                Instruction instruction = method.Instructions[i];

                if (instruction.Value == null) continue;

                string value = instruction.Value.ToString();

                if (!value.Contains(this.ClassName)) continue;

                if (this.MethodToExclude.Where(f => value.Contains(f)).Any()) continue;

                Method calledMethod = instruction.Value as Method;
                if (calledMethod == null) continue;

                if (calledMethod.Parameters.Count == 0) continue;

                Parameter parameter = calledMethod.Parameters[0];

                if (parameter.Type.FullName != typeof(string).FullName) continue;

                Resolution resolution = GetResolution(new string[] { method.ToString() });
                this.Problems.Add(new Problem(resolution));
            }

            return this.Problems;
        }
    }
}


DoNotCallXDocumentLoadWithPath inherits the class BaseIntrospectionRule. BaseIntrospectionRule constructor takes three arguments. Getting one of these wrong and the rule will not show up in the FxCop UI or FxCop will refuse to load the assembly.

  • name: This must be unique among all your rules and it's used in the XML rule description file. See step 6.
  • resourceName: This string is used by the SDK to locate an embedded xml resource file, by calling GetManifestResourceStream on the assembly given as the third argument. The string should have the following composition: (default)namespace.filename. In this example the string is “MyNamespace.FxCopRules”. It's optional to append the string with ".xml" like this "MyNamespace.FxCopRules.xml".
  • resourceAssembly: This should point to the assembly where the embedded xml resource file is located (named in the second argument - resourceName).


Step 5: Adding a XML rule description file

Each rule needs to have a XML description in order to work. If a rule is missing its description or there is an error in the description, the rule wont turn up in the FxCop UI. So for step 5 you need to do the following:

  • Add a new empty XML file to your project. Here I name this file FxCopRules.xml
  • Change the build action to: Embedded Resource. Important!


Step 6: Writing the rule description

Here is an example of how to write the rule description:


<?xml version="1.0" encoding="utf-8" ?>
<Rules FriendlyName="My Custom IO Rules">
  <Rule TypeName="DoNotCallXDocumentLoadWithPath"
        Category="MyCategory"
        CheckId="MyCategory.DoNotCallXDocumentLoadWithPath">
    <Name>Dot not call System.Linq.Xml.XDocument.Load(string uri, ...)</Name>
    <Description>Dot not call System.Linq.Xml.XDocument.Load(string uri, ...)</Description>
    <Owner></Owner>
    <Url></Url>
    <Resolution>Use the stream version of System.Linq.Xml.XDocument.Load</Resolution>
    <Email></Email>
    <MessageLevel Certainty="100">Warning</MessageLevel>
    <FixCategories>Breaking</FixCategories>
  </Rule>
</Rules>

A few notes on some of the values in this description:

  • TypeName: This should be the same as the first argument to the BaseIntrospectionRule constructor. See step 4.
  • CheckId: This should be unique among all your rules in the same category

 

Step 7: All done!

Now you can compile your assembly and load it in the FxCop UI. Enjoy!

Tags:

.NET | C#

About the author

Martin Ingvar Kofoed Jensen

Architect and Senior Developer at Composite on the open source project Composite C1 - C#/4.0, LINQ, Azure, Parallel and much more!

Follow me on Twitter

Read more about me here.

Read press and buzz about my work and me here.

Stack Overflow

Month List