Sergey Shishkin

on agile software development

StyleCop Contrib: Simple Extensibility Framework

After my recent experiments with StyleCop I received several questions on how to write and test custom StyleCop rules. To provide a general answer and maybe foresee some further questions I decided to publish the source code of the rules and the small framework that I’ve built so far. Please note, that it was a quick and dirty solution, yet designed for extensibility. Anyway, any feedback: questions, ideas, and critics is welcome.

The custom rules framework consists of mainly two classes: DocumentAnalyzer and SourceAnalysisTest. The former extends the StyleCop’s SourceAnalyzer abstract class and provides a much simpler custom rules API. The latter is a base class for unit tests (I prefer to calling them integration tests since they are not enough autonomous).

The DocumentAnalyzer basically walks through the source code document (on both token and code element levels) and calls registered delegates implementing the rules. This is an implementation of the Visitor design pattern based on delegates. The rules then look something like this:

internal static void CheckTabsMustBeUsed(CodeElement element,

    Node<CsToken> node,

    DocumentAnalyzer context)

{

    var token = node.Value;

    if (token.CsTokenType == CsTokenType.WhiteSpace)

    {

        var whitespace = (Whitespace)token;

        if (node.Previous == null ||

            node.Previous.Value.CsTokenType == CsTokenType.EndOfLine)

        {

            if (whitespace.SpaceCount > 0)

            {

                context.AddViolation(element, token.LineNumber, RuleNames.TabsMustBeUsed);

            }

        }

    }

}

The rule must be introduced to the DocumentAnalyzer in the AddRules method:

private void AddRules()

{

    _NodeHandlers.Add(CsTokenType.WhiteSpace, SpacingRules.CheckTabsMustBeUsed);

    _ElementHandlers.Add(ElementType.UsingDirective, OrderingRules.CheckUsingDirectivesMustBeginDocument);

    _ElementHandlers.Add(ElementType.Field, NamingRules.CheckPrivateFieldUnderscorePrefix);

    _ElementHandlers.Add(ElementType.Field, NamingRules.CheckPrivateFieldCasing);

}

I don’t like the idea of DocumentAnalyzer knowing the rules, but I didn’t come up with something clean and simple yet. Anyway, StyleCop already tightly couples the analyzer implementation with its rules by requiring a single embedded XML resource (named after the analyzer class) to describe all the rules.

If I was designing StyleCop, I would make a rule the first class citizen with a single analyzer implemented by StyleCop itself. Rules then should own their metadata and should be registered by the analyzer somehow. But this is another story…

NodeHandlers delegate collection (PowerCollections‘ MultiDictionary actually) contains delegates operating on the token level (white spaces, keywords, operators etc.). ElementHandlers analyze code document elements (classes, methods, fields etc.) and expressions (for example, variable declarations).

The SourceAnalysisTest class configures the environment for StyleCop and makes all the required analysis heavy lifting, so that end tests can look like this:

[TestClass]

public class SpacingRulesTest : SourceAnalysisTest

{

    [TestMethod]

    public void TabsMustBeUsed_Invalid()

    {

        AddSourceCode("Spacing_Invalid.cs");

 

        StartAnalysis();

 

        AssertViolated(RuleNames.TabsMustBeUsed, new[] { 1, 2, 6 });

    }

}

When running tests ensure that the Resources folder is getting deployed to the test output folder (see the TestConfiguration.testrunconfig file). This folder contains required StyleCop configuration and sample source code to test rules against.

Please, let me know if you are interested in further development of this samples. I called it StyleCopContrib but maybe it should be driven by Microsoft in some way. So, it’s just a proposal. What do you think?

And here is the source code (92 KB, .zip): http://cid-9f19e53ba9c1d63f.skydrive.live.com/self.aspx/Public/Code/StyleCopContrib.zip

 

UPDATE: see update for version 4.3.

 

Technorati Tags:
Advertisements

Written by Sergey Shishkin

25.06.2008 at 11:26

Posted in Uncategorized

%d bloggers like this: