Book Image

Instant StyleCop Code Analysis How-to

By : Franck Leveque
Book Image

Instant StyleCop Code Analysis How-to

By: Franck Leveque

Overview of this book

In medium-sized and big projects, coding conventions are defined in order to improve readability and maintainability for all the developers of the team. Stylecop analyzes your code and detects coding rule violations during all the phases of your project lifecycle. Instant StyleCop Code Analysis How-to allows you to take advantage of the features of Stylecop by guiding you through how to configure it, how to integrate it in your project environment, and finally how to personalize it in order to fit your needs. Instant StyleCop Code Analysis How-to teaches you how to configure and integrate Stylecop in your programming environment. The book will do this by showing you how to configure Stylecop on the developers IDE to the continuous integration server. You will also learn how to customize Stylecop to fit your coding style by creating new rules as well as learning how to personalize your headers file. You will also see how to embed it in your own tools, using as an example the creation of a real time analysis add-on for Monodevelop. With Instant StyleCop Code Analysis How-to, you will have an overview of all the required steps to successfully integrate your programming team and enforce your own coding rules.
Table of Contents (7 chapters)

Creating custom rules (Intermediate)


In this recipe, we will see how to create our own custom rules for the StyleCop engine. We will also see how to add parameters to this rule.

Getting ready

For this recipe, you will need to have:

  • StyleCop 4.7 installed

  • Visual Studio 2008

How to do it...

In the early days of StyleCop, lots of the rules chosen by Microsoft were criticized. One of them was the fact that the developer couldn't add an underscore at the beginning of their private instance fields. In this recipe, we will take this rule as an example and try to implement it at the beginning of non public instance fields.

Note

This rule directly conflicts with the following StyleCop rules:

SA1306: Variable names and private field names must start with a lower-case letter

SA1309: Field names must not start with an underscore.

You will have to disable them, if you want to use the rule.

  1. To create our custom rule, the first thing we must do is create a new Class Library project in Visual Studio. Then, we need to add the following reference to our project:

    • Stylecop

    • Stylecop.CSharp

    Both libraries are located in the installation directory of StyleCop.

  2. The rules need to be implemented in a code analyzer. Each analyzer is composed of two files:

    • A file containing the class that will discover your rules violations

    • An XML file containing the rules description

  3. Let's begin with the XML file. This file should have the same name as your analyzer class. Its purpose is to describe the analyzer category, describe the rules it contains, and prepare the parameters you might need in your rules. Let's have look at the file contained in our custom rule:

    <?xml version="1.0" encoding="utf-8" ?>
    <SourceAnalyzer Name="NamingExtension">
      <Description>
        Naming rules extending Stylecop.
      </Description>
      <Properties>
        <BooleanProperty
          Name="UseTokenAnalysis"
          DefaultValue="true"
          FriendlyName="Token Analysis"
          Description="Indicates whether the analyzer of document will the token analysis or the visitor pattern analysis."
          DisplaySettings="true"/>
      </Properties>
      <Rules>
        <Rule Name="NonPublicFieldsMustBeginBy" CheckId="NE1001">
          <Context>Instance fields should be prefixed.</Context>
          <Description>Instance fields should be prefixed to allow a better visibility of non public fields.</Description>
        </Rule>
      </Rules>
    </SourceAnalyzer>

    The file is composed of three important elements:

    • The Description element is used to define the description that will be displayed to the user for the category.

    • The Properties section is optional, and allows you to define parameters you want to use in the different rules that your analyzer manages. There are four available kinds of properties: BooleanProperty, StringProperty, IntegerProperty, and CollectionProperty. They can be accessed in your code by the analyzer function GetSetting(Settings, String).

    • The Rules section is used to describe all the rules your analyzer will manage.

  4. Next we need to create our analyzer class, which inherits SourceAnalyzer and defines SourceAnalizerAttribute, specifying which parser this analyzer is for:

    using System;
    using System.Linq;
    
    using StyleCop;
    using StyleCop.CSharp;
    
    namespace StylecopCustomRule
    {
        /// <summary>
        /// Description of custom rule for stylecop
        /// </summary>
        [SourceAnalyzer(typeof (CsParser))]
        public class NonPublicFieldsMustBeginBy : SourceAnalyzer
        {
            public override void AnalyzeDocument(CodeDocument document)
            {
                var csDocument = (CsDocument) document;
                
                // General settings
                var generalSettings = (from parser in document.Settings.ParserSettings
                             where parser.AddIn.GetType().Equals(typeof(CsParser))
                             select parser).Single();
                BooleanProperty analyseGeneratedFiles = (BooleanProperty)generalSettings[«AnalyzeGeneratedFiles»];
    
    
                if (csDocument.RootElement != null &&
                   (analyseGeneratedFiles.Value || !csDocument.RootElement.Generated))
                {
                    csDocument.WalkDocument(new CodeWalkerElementVisitor<object>(this.VisitElement), null, null);
                }
            }
    
            private bool VisitElement(CsElement element, CsElement parentElement, object context)
            {
                if (element.ElementType == ElementType.Field && 
                    element.ActualAccess != AccessModifierType.Public &&
                    element.ActualAccess != AccessModifierType.Internal && 
                    !element.Declaration.Name.StartsWith("_"))
                {
                    this.AddViolation(element, "NonPublicFieldsMustBeginBy", new object[0]);
                }
    
                return true;
            }
        }
    }

How it works...

The main entry point is the AnalyzeDocument function; this is where the document will be analyzed to see if it contains any broken rules. We have two options. Either we use the visitor pattern provided by StyleCop, and in this case we have to define code walkers for the type of construction we want to check (there are four walkers available: CodeWalkerElementVisitor, CodeWalkerStatementVisitor, CodeWalkerExpressionVisitor, and CodeWalkerQueryClauseVisitor), or you can directly access the token list and check them directly. The second approach is a little trickier as upper constructions are made of one or more tokens. To use it in our example, we just have to replace the call to the visitor function by a LINQ request selecting the tokens in violation of your rule. For our sample, it will look as follows:

if (csDocument.RootElement != null && (analyseGeneratedFiles.Value || !csDocument.RootElement.Generated))
{
   Array.ForEach(
       (from token in csDocument.Tokens
        let element = token.FindParentElement()
        where token.CsTokenClass == CsTokenClass.Token &&
        token.CsTokenType == CsTokenType.Other &&
        element.ElementType == ElementType.Field &&
        element.ActualAccess != AccessModifierType.Public &&
        element.ActualAccess != AccessModifierType.Internal &&
        !token.Text.StartsWith("_")
        select element).ToArray(),
        a => this.AddViolation(
        a, 
        "NonPublicFieldsMustBeginByUnderscore", 
        new object[0]));                
}

As you can see, both ways of enforcing our rule look quite similar as we need the parent element of tokens to check easily if the token is a field and if it respects the rule. To exclude tokens of the element construct I had to add further restrictions based on the token class and token type.

When you report your violations you have to be careful of the name of the violation as any reference to an unknown rule in the XML file will just discard the violation.

In this recipe, we have seen how to implement a rule. However, you have to keep in mind that the analyzer is designed to allow you to create a set of rules, not just one. We have also seen that the central method of the analyzer is the AnalyzeDocument function; this is where you have to analyze the rule violations and report them. We also quickly see how to set some properties and use them.

There's more...

However, custom tasks is a huge topic. In addition, you can customize the StyleCop setting, unit test your rules, and much more.

Customize your StyleCop settings dialog

Defining your properties in the XML file of the analyzer doesn't display them in the StyleCop settings UI. Only BooleanProperty can be shown directly using the DisplaySettings element as shown in the following screenshot:

All other properties require a custom UI. This is achieved by providing UserControl implementing Stylecop.IPropertyControlPage.

A really great tutorial is provided in the Adding a Custom StyleCop Settings Page section of the StyleCop SDK.

Unit testing your rules

Unit testing your rules is really important and can be achieved quite easily. To do so, we have to rely to the integration API provided by the StyleCop team. In this recipe code, I have made a project to unit test my rule using NUnit 2.6.2.

As it was only one rule, I didn't abstract the StyleCop integration in a base class, but this should be done as all your rules will depend on the same code implementation.

I also use test files that I placed in the TestFiles directory.