Technology with opinion

Tuesday, November 25, 2008

Creating Custom StyleCop Rules in C#

Microsoft has released a new free static code analysis tool called StyleCop.  Named after the similar FxCop, this product is aimed to be more focused for cosmetic code consistency which can increase readability and maintainability.  StyleCop has built-in rules, which you can turn off, many of which are:
  • Ordering of members in a class from most visible to least visible
  • Order of Usings in alphabetical order, with System namespaces on top
  • Proper camel casing and pascal casing
  • Proper whitespace
I like about 3/4 of these rules so I think it's definitely worth considering using, even consider integrating into your automated build process using NAnt, CruiseControl or TFS.  That being said many will likely want to extend these rules to create your own.  Doing so is fairly simple but not documented very well at this time.  I created the following rule to make sure that all instance variables (private & protected class level variables) are prefixed with an underscore.  I realize not everybody follows this convention, but I like it because it's more concise than prefixing your instance variables with 'm_' or 'this' which is often needed to prevent name collisions.

First I created a C# Class assembly project and referenced "Microsoft.StyleCop" and "Microsoft.StyleCop.CSharp".  Next I added the following class, which implements my custom rule:

using Microsoft.StyleCop;
using Microsoft.StyleCop.CSharp;

namespace DotNetExtensions.StyleCop.Rules
{
    /// <summary>
    /// This StyleCop Rule makes sure that instance variables are prefixed with an underscore.
    /// </summary>
    [SourceAnalyzer(typeof(CsParser))]
    public class InstanceVariablesUnderscorePrefix : SourceAnalyzer
    {
        
        public override void AnalyzeDocument(CodeDocument document)
        {
            CsDocument csdocument  = (CsDocument) document;
            if (csdocument.RootElement != null && !csdocument.RootElement.Generated)
                csdocument.WalkDocument(new CodeWalkerElementVisitor<object>(this.VisitElement), null, null);
        }

        private bool VisitElement(CsElement element, CsElement parentElement, object context)
        {
            // Flag a violation if the instance variables are not prefixed with an underscore.
            if (!element.Generated && element.ElementType == ElementType.Field && element.ActualAccess != AccessModifierType.Public && 
                element.ActualAccess != AccessModifierType.Internal && element.Declaration.Name.ToCharArray()[0] != '_')
            {
                AddViolation(element, "InstanceVariablesUnderscorePrefix");
            }
            return true;
        }
    }
}

The VisitElement function handles the main logic for this rule.  First it makes sure that the member isn't Generated code, which you have no control over so no sense in applying rules to.  Secondly it's making sure that the member's visibility isn't Public or Internal.  Finally we make sure that the instance variable starts with an underscore prefix..

Next we need to provide metadata so that StyleCop can categorize and describe our rule.  Add an XML file to your project, then change it's Build Action to 'Embedded Resource'

<?xml version="1.0" encoding="utf-8" ?>
<SourceAnalyzer Name="Extensions">
  <Description>
    These custom rules provide extensions to the ones provided with StyleCop.
  </Description>
  <Rules>
    <Rule Name="InstanceVariablesUnderscorePrefix" CheckId="EX1001">
      <Context>Instance variables should be prefixed by underscore.</Context>
      <Description>Instance variables are easier to distinguish when prefixed with an underscore.</Description>
    </Rule>
  </Rules>
</SourceAnalyzer>

At the root level you are naming the main category for the rules in this file.  Next you describe your suite of rules.  Then in the Rules section you can add all the rules (note: you can use RuleGroup element to further categorize your rules).  Finally you name your rule and give it a unique CheckId.  The CheckId is what you can search for your rule by in the StyleCop Project Settings.  Important: for StyleCop to correlate your class to your metadata your class and xml file need to be named exactly the same.

Finally build your project and drop your new assembly's dll into the StyleCop directory (C:\Program Files\Microsoft StyleCop 4.3).  Now double-click your Settings.StyleCop to see your rule in the list.  It should look something like the following:



Now you can open your solutions/projects in Visual Studio and click Tools -> Run StyleCop and you should see warnings for your new rule.

StyleCop is a nice little accent to any C# project.  I'd like to see Microsoft make the following changes:
  • StyleCop should be OSS (open source software) - why not Microsoft, there really can't be any proprietary business technology in an application that is basically is performing string  parsing
  • StyleCop's documentation needs to be improved - Microsoft provides to help documents for StyleCop, a user manual and an SDK manual.  Both of them lack details and some of the examples simply do not even compile.  For instance the CodeWalkerElementVisitor delegate in a boolean, in their samples they do not even return a boolean value which will not allow their samples to compile.  They do not even document the purpose of this boolean.  Through trial and error I think that if you return a false that it will stop walking (parsing) the document for this rule.
  • Custom rules should be able to be categorized within the existing main ones: Naming Rules, Ordering Rules, etc.
  • Easier to create custom rules by using regex within an XML file.  This would eliminate having to create custom assemblies and classes.
  • Command-line interface should be available for testing or scripting purposes.

13 comments:

samuraix said...

I need the rule to you implement for using underscore prefix for private or protected instance level variables for SyleCop.

More thanks for help me.

My email es: walkeraguilar@gmail.com

Research Writer said...

Many institutions limit access to their online information. Making this information available will be an asset to all.
Custom Paper Writing

Neil said...

Hi,

I have a problem.

When I open the settings file via visual studio (right-click a project and click StyleCop Settings) my custom rules appear. All good there. However if I open the same file by double-clicking it in windows explorer my custom rules don't show up. Any idea why?

Thanks
Neil

Asp.net said...

In your Article there is mentioned that "Proper camel casing and pascal casing" in build function in StyleCop, but i am unable to find that rule. Can you help me


Satish
Email:- satishbhargav4u@gmail.com

Scott White said...

@samuraix

I posted the code to Google Code.

@neil

If you modify the stylecop settings file in the Program Files location then it will make the settings global to Visual Studio unless a projects settings override the default.

Ivan said...

I followed your guide (actually I just copy and pasted your code), but I can't get Microsoft StyleCop to pick the rule up. I'm using .NET framework 2.0 and Visual Studio 2005. The dll is getting picked up because I can't edit or delete it. I've made sure that the file names match. Any help would be appreciated

Jackson...!!! said...

Hi,
My issue is same as what Ivan had told in his comment, i also but created same project, which got compiled without errors, but I can't get Microsoft StyleCop to pick the rule. Is there any other setting to integrate our custom rule in StypeCop?

Thanks,
Jackson C.

Zeor said...

If anyone should come by this; The project is now opensource and by looking it up I found out that the assembly should be build with .NET 3.5. If using another version, then the assembly will not be picked up and you can not see your own rules.

EnThu said...

I need to count lines of code in a method in c#. Can i add my custom rule for the same. Please help.
Thanks in advance for help.

EnThu said...

Hwy i am not able to see my custom created rule in the setting.stylecop. Can anyone help my assembly is 3.5 built my name of the XML file is same as name of class.
Please help.

D. Maymone said...

I have exactly the same problem as En Thu.
I am not able to see my custom created rule in the setting.stylecop. My assembly is 3.5. Same name to XML and class file. But I don't see the "Extensions" entry in the setting file.
Please help.

graydo64 said...

I had the same problem as EnThu and D. Maymone. I'd followed your post to the letter, checked it against the SDK but couldn't get my custom rule to load. I had used the nuget package in my project to get the references for StyleCop and StyleCop.CSharp. I found that removing the nuget package and referenced the dlls in %programfiles%\StyleCop 4.7 instead fixed the problem.

D. Maymone said...

I am not able yet to see my custom created rule in the setting.stylecop. My assembly is 3.5. Same name to XML and class file. But I don't see the "Extensions" entry in the setting file.
When I double click the Settings file this error is displayed:
---------------------------
StyleCop
---------------------------
An exception occurred while loading one of the StyleCop add-ins: System.IO.FileLoadException,
Could not load file or assembly 'StyleCop, Version=4.7.6.0, Culture=neutral,
PublicKeyToken=f904653c63bc2738' or one of its dependencies.
The located assembly's manifest definition does not match the assembly reference. (
Exception from HRESULT: 0x80131040)
Thanks