Copyright 2008-2009, Paul Jackson, all rights reserved
Howard van Rooijen is apparently working on a combination of Microsoft Source Analysis and Resharper to get real-time style checking.
Copyright 2008-2009, Paul Jackson, all rights reserved
Howard van Rooijen is apparently working on a combination of Microsoft Source Analysis and Resharper to get real-time style checking.
Copyright 2008-2009, Paul Jackson, all rights reserved
Regardless of whether Microsoft expected it or not, it appears that the user-community is actively interested in writing custom rules for Microsoft Source Analysis for C# (StyleCop). Sergey Shishkin has been blogging about StyleCop and has worked out the details of test-driven development and unit testing for StyleCop custom rules on his blog.
Copyright 2008-2009, Paul Jackson, all rights reserved
So now it's time to create a real, useful custom rule for StyleCop (Source Analyzer).
<?xml version="1.0" encoding="utf-8" ?>
<SourceAnalyzer Name="Demo Custom Rules">
<Description>
Demonstration of a working, useful custom rule.
</Description>
<Rules>
<RuleGroup Name="Naming Rules">
<Rule Name="PrivateFieldNameMustStartWithUnderscoreFollowedByLowerCase" CheckId="DM1001">
<Context>Private field names must start with an underscore followed by a lower-case letter.</Context>
<Description>Private field names must start with an underscore character.</Description>
</Rule>
</RuleGroup>
</Rules>
</SourceAnalyzer>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SourceAnalysis.CSharp;
using Microsoft.SourceAnalysis;
namespace CustomRuleDemo
{
[SourceAnalyzer(typeof(CsParser))]
public class NamingRules : SourceAnalyzer
{
public override void AnalyzeDocument(CodeDocument document)
{
Param.RequireNotNull(document, "document");
CsDocument document2 = (CsDocument)document;
if ((document2.RootElement != null) && !document2.RootElement.Generated)
{
}
}
}
}
Documentusingusingusingnamespaceclassfieldfieldmethodclassfieldmethod... and so on.
<
private bool processElement(CsElement element)
{
if (base.Cancel)
{
return false;
}
foreach (CsElement child in element.ChildElements)
{
if (!this.processElement(child))
{
return false;
}
}
return true;
}
public override void AnalyzeDocument(CodeDocument document)
{
Param.RequireNotNull(document, "document");
CsDocument document2 = (CsDocument)document;
if ((document2.RootElement != null) && !document2.RootElement.Generated)
{
processElement(document2.RootElement);
}
}
if (element.ElementType == ElementType.Field &&
!element.Generated)
{
if (!(element.Declaration.Name.StartsWith("_", StringComparison.Ordinal))
element.Declaration.Name.Substring(1, 1).ToLower() != element.Declaration.Name.Substring(1, 1))
{
}
}
base.AddViolation(
element,
"PrivateFieldNameMustStartWithUnderscoreFollowedByLowerCase",
new object[0]);
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ClassLibrary3
{
public class Class1
{
private int _temp1;
private int _Temp2;
private int temp3;
private int Temp4;
}
}
Copyright 2008-2009, Paul Jackson, all rights reserved

xcopy "$(TargetDir)SourceAnalyzerSample*.*" "C:\Program Files\Microsoft Source Analysis Tool for C#" /y
You could set your project's target directory to build directly into the Source Analyzer install directory, but I prefer to have it as a separate step.
Copyright 2008-2009, Paul Jackson, all rights reserved
In Part I we set up a simple custom rule for the Microsoft Source Analyzer (StyleCop) that displays a rule violation for every source file in the project. Now in Part II, I'll explain the elements of the XML file and source code that went into that.
<?xml version="1.0" encoding="utf-8" ?>
<SourceAnalyzer Name="Custom Rules">
<Description>
Custom rules added to analyzer.
</Description>
<Rules>
<RuleGroup Name="Custom Rules Group">
<Rule Name="MyCustomRule" CheckId="CR0001">
<Context>This is a custom rule.</Context>
<Description>This is a custom rule description.</Description>
</Rule>
</RuleGroup>
</Rules>
</SourceAnalyzer>
In Settings, StyleCop create a hierarchy of rules based on the SourceAnalyzer Name-attribute, the RuleGroups and the Rules in the XML file. So the XML above becomes:

when loaded into settings by the Source Analyzer. The SourceAnalzyer element's Name attribute becomes a node under C#; each RuleGroup becomes a node under that; and each Rule is contained in its RuleGroup.
The CheckID attribute of a Rule must consist of two capital letters and four digits.
The Context element of a Rule is what displays in Visual Studio analysis results and can contain {0} string formatting placeholders (which we'll see in Part III).
The Description element of a Rule is what displays to the user in Source Analysis Settings when they're choosing which Rules to enforce.
You can use Reflector (one of the top five utilities a .Net developer must have, in my opinion) to examine the Rules included with StyleCop and the associated XML files:
Now on to the code:
namespace SourceAnalyzerSample
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SourceAnalysis;
using Microsoft.SourceAnalysis.CSharp;
using System.Windows.Forms;
[SourceAnalyzer(typeof(CsParser))]
public class SampleAddInRules : SourceAnalyzer
{
public override void AnalyzeDocument(CodeDocument document)
{
Param.RequireNotNull(document, "document");
CsDocument document2 = (CsDocument)document;
if ((document2.RootElement != null) && !document2.RootElement.Generated)
{
AddViolation(document2.RootElement, "MyCustomRule", new object[0]);
}
}
}
}
Our simple example from Part I violates every file it analyzes without actually checking anything -- this was done to demonstrate the minimum code necessary to create a rule and generate a violation. I used Reflector on the included Rules to determine what the minimal code should look like.
First, we need the references and using directives for Microsoft.SourceAnalysis and Microsoft.SourceAnalysis.CSharp.
Then we create a class inherited from SourceAnalyzer and add a SourceAnalyzer attribute on the class, giving it a parameter of typeof(CsParser). StyleCop uses Reflection to find classes inherited from SourceAnalyzer to add to its rules. The CsParser type tells StyleCop that this class analyzes C# source files. Although I didn't find a VB parser or rules in my download, maybe someone at Microsoft is working on one?
We next need to override the AnalyzeDocument method from the SourceAnalyzer base-class. This is the entry point StyleCop will use to run our rule and pass it each source file in the project. Each source file is passed in as a parameter of type CodeDocument.
As part of the Microsoft.SourceAnalysis assembly, they've included a Param class that has a number of methods on it to validate parameters passed to methods. We use this to require that the CodeDocument parameter passed isn't null. As an aside, I've seen similiar functionality in a class called Guard included in a lot of patterns & practices code -- it seems like there's a lot of duplicate code going into validating method parameters ... sounds like framework to me.
Anyway, after ensuring that we were passed a CodeDocument, we want to cast it to a CsDocument. CodeDocument is a base-class and, presumably, there'll be VbDocument and FsDocument coming at some point in the future.
The next step is to check some things on the document. In this case, we're checking to ensure that the document has a RootElement and that it isn't generated-code. The source code is treated a hierarchy of elements containing other elements, which we'll see more of in Part III. We want to avoid analyzing generated-code, because it doesn't make sense to create a bunch of style warnings for code that, theoretically, a human will never have to read. Of course, this presumes that the code generater followed the rules for marking its generated code as such.
Finally, we're going to create the violation. The AddViolation method has a number of overloads:
You also have the option of passing in a line number identifying the line of code that caused the violation (the Int32 parameters above).
And that's it for the code.
You can use Reflector against the included Rules to learn more about the different types of CodeElements and how to check specific things, which is what we'll be doing in Part III when we create a rule to ensure that private fields begin with an underscore, followed by a lower-case character and have no other underscores in the name.
Copyright 2008-2009, Paul Jackson, all rights reserved
http://code.msdn.microsoft.com/sourceanalysis
http://blogs.msdn.com/sourceanalysis/

Then add a new class and an XML file with the same name. Set the Properties of the XML file to be an Embedded Resource and not copy to the output directory:
<?xml version="1.0" encoding="utf-8" ?>
<SourceAnalyzer Name="Custom Rules">
<Description>
Custom rules added to analyzer.
</Description>
<Rules>
<RuleGroup Name="Custom Rules Group">
<Rule Name="MyCustomRule" CheckId="CR0001">
<Context>This is a custom rule.</Context>
<Description>This is a custom rule description.</Description>
</Rule>
</RuleGroup>
</Rules>
</SourceAnalyzer>
namespace SourceAnalyzerSample
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SourceAnalysis;
using Microsoft.SourceAnalysis.CSharp;
using System.Windows.Forms;
[SourceAnalyzer(typeof(CsParser))]
public class SampleAddInRules : SourceAnalyzer
{
public override void AnalyzeDocument(CodeDocument document)
{
Param.RequireNotNull(document, "document");
CsDocument document2 = (CsDocument)document;
if ((document2.RootElement != null) && !document2.RootElement.Generated)
{
AddViolation(document2.RootElement, "MyCustomRule", new object[0]);
}
}
}
}
Build the project and copy its DLL to the Source Analysis install directory. Now, when you run Source Analysis on a project, you should get a warning about the custom rule for every file in the project:
In Part II, I'll explain the elements of the XML file and the code; then, in Part III, I'll demonstrate a useful example.
One of the differences between the standards where I work and Microsoft's is that we require private fields to start with an underscore ("_"), while the Microsoft rules provided by StyleCop require they start with a lower-case letter and contain no underscores. So I'll be turning off the two Microsoft rules (SA1306 and SA1309) and creating a custom rule to enforce our standards.