Home / .NET / Custom StyleCop Rules – Part II

Custom StyleCop Rules – Part II

In my first post (link) I’ve explained what StyleCop is and how you can start with your own StyleCop rules. We will now dig a little bit deeper into the jungle of StyleCop….

StyleCop’s Code Model

After some reverse engineering of StyleCop’s Code Model, I thought it is not worth to try finding out what the meaning of all the classes is. I’ll just show you, how I exploratively found solutions for the things I wanted to check in the source code.

Just a few things: The Document is one file of C# code. If you walk through the Document using AnalyzeDocument and after that you use StyleCop’s WalkDocument callback mechanism for example to find out which Expression is a magic number, you will see some items twice. Maybe you’re confused now; just try out the source code mentioned at the end of this article.

Custom Rules Made Easy

It’s pretty annoying writing a whole SourceAnalyzer class every time you want to add a new rule. So I’ve created a generic mechanism where you just have to chose the type of item you want to inspect and the write the rule against it. You can try and write a dummy rule for each item to find out, what will be provided by each type of item.

The different items are

  • Tokens
  • Elements
  • Expressions

Additionally you can decide which type of CsTokens (e.g. Attribute, Return, UsingDirective, …), Elements (e.g. Methods, Fields, Struct, …) or Expressions (e.g. Arithmetic, Lambda, Logical, Typeof, …) you like to inspect.

Test Driven Development TDD

It is one of my favorite to develop in TDD and I think it changed the whole thinking about development. To make that possible, you have to use StyleCop’s CodeProject class and the StyleCopConsole class and then some Code that violates your rules. The latter is easy, but the first one gave me some miracles until I solved it.

First of all, create a new CodeProject:

  var configuration = new Configuration(new string[0]);
  CodeProject = new CodeProject(Guid.NewGuid().GetHashCode(), null, configuration);

Then you can add your violating source code to the CodeProject instance:

public void AddSourceCode(string fileName)
{
fileName = Path.GetFullPath(fileName);
bool result = this.StyleCopConsole.Core.Environment.AddSourceCode(CodeProject, fileName, null);
if (result == false)
{
throw new ArgumentException("Source file could not be loaded.", fileName);
}
}

To start the analysis use the following code:

public void StartAnalysis()
{
var projects = new[] { CodeProject };
bool result = this.StyleCopConsole.Start(projects, true);
if (result == false)
{
throw new ArgumentException("StyleCopConsole.Start had a problem.");
}
}

I’ve put all the code above in an abstract base class and for the rules I’ve set up a new test class using Visual Studio’s integrated test runner:

namespace CleanCode.StyleCopCustomRules.UnitTests
{
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class CleanCodeTests : AbstractSourceAnalysisTest
{
[TestMethod]
public void TooManyParameters()
{
AnalyzeCodeWithOneAssertion("TooManyParameters.cs");
}

...

private void AnalyzeCodeWithOneAssertion(string codeFileName)
{
const int ExpectedViolations = 1;
this.AnalyzeCodeWithAssertion(codeFileName, ExpectedViolations);
}

private void AnalyzeCodeWithAssertion(string codeFileName, int expectedViolations)
{
AddSourceCode(codeFileName);
StartAnalysis();
WriteViolationsToConsole();
WriteOutputToConsole();
Assert.AreEqual(expectedViolations, StyleCopViolations.Count);
}

private void WriteOutputToConsole()
{
Console.WriteLine(string.Join(Environment.NewLine, StyleCopOutput.ToArray()));
}

private void WriteViolationsToConsole()
{
foreach (var violation in StyleCopViolations)
{
Console.WriteLine(violation.Message);
}
}
}
}

You would probably ask why I’ve used the Visual Studio test runner and not NUnit or xUnit. The reason is, that I haven’t found a way to copy the source code files that violates the rules to the directory where NUnit could find them. In VS.Net I could use the LocalTestRun.testrunconfig and say that the Resources folder should be deployed where the test is running. I’m curious if you find a solution with NUnit or xUnit.

Putting All Together

Clean Code CoverSo far I’ve implemented some rules from the book “Clean Code” written by Robert C. “Uncle Bob” Martin which can be found here or at amazon. I was astonished and excited about the contents of the book. This was exactly what I was looking for years. Now I know what was wrong with our code walk-through’s and reviews. We had no idea what we were looking for!

The code is available on googlecode here. You should be able to use it after checking out from the subversion repository. Otherwise let me know…

Have fun
Thomas