FxCop & Cyclomatic Complexity

[The static location of this piece can be found at this address]

FxCop As a Code Quality Tool

For the past while I’ve been using VisualStudio Team Edition for Software Developers, one of itsbenefits over the Professional Edition being the inclusion ofstatic code analysis functionality right in the IDE.

This functionality comes via the FxCop codeset, which isan excellent — albeit unpolished — freely availabletool for analyzing the probable code quality of IntermediateLanguage assemblies, testing code to ensure compliance withnaming standards, best practices, and highlighting areas of codethat are suspect. While it’s less than pleasant starting FxCopanalysis from scratch on long existing project — to be metwith hundreds upon hundreds of error messages — it’s apainless process if you add it to your qualitychecks early on.

The standalone FxCop is largely the same as the VSTE version,and in some ways is superior. For instance that it retains theability to actually pass configuration settings to rules, ratherthan accepting whatever the defaults for the rule are.

Cyclomatic Complexity

One of the few differences between the standalone applicationand the VSTE-included version are the addition of several newmaintenance checks in the Team Edition code, one of the most usefulbeing the cyclomaticcomplexity checks. Cyclomatic complexity, for those who haven’tcome across it before, is often used to roughly gauge thecomplexity of a piece of code, to determine likely candidates forrefactoring, and to identify what will likely become a maintenanceproblem in the future. Finding the most complex pieces of codeoften brings you to the buggiest code as well.

Given that I still use FxCop, both the .NET 1.1 and .NET 2.0versions (not least because the integrated version offers noability to configure settings for rules, instead only allowing youto wholesale enable or disable. This eliminates the ability to setthresholds for tests such as the cyclomatic complexityrules), the lack of consistency between the twoversions was an annoying gap.

Introducing Cyclomatic Complexity Analysis For FxCop

So I implemented a simple cyclomatic counting rule for thestandalone FxCop. While in there, I added checks for statementcount (the number of intermediate language “statements”, which canbe indicative of overly complex methods), and callout count (e.g.callouts to other methods, again which can be an indicator ofoverly complex/convoluted methods).

As one added benefit, I added the ability to log all of thesemetrics to an SQL-capable OleDB destination (e.g. SQL Server,Access, etc). If you configured an OLEDB connection string,as detailed below, you can do data analysis after a run tocreate pretty reports of the complexity distributions of yourprojects, and so on. 

Download Links

yafla FxCopRules for .NET 1.1 (e.g. FxCop 1.32)
yafla FxCopRules for .NET 2.0 (e.g. FxCop 1.35)

Caveats

Like any tool of this type, there is only a moderate correlationbetween the metrics measured and actual code quality ormaintainability: It is entirely possible that the optimalimplementation is a highly-complex, lengthy method. This tool onlyprovides guidance, helping to determine which code should get acomplexity analysis, however from there experience and goodjudgement have to be applied to determine if it’s really a fault.If you’re using the .NET 2.0 version of FxCop, make use of theSuppressMessage attribute on methods that are necessarily highlycomplex.

Instructions

Drop yaflaRules.dll in your FxCop Rules subdirectory (e.g.C:\\program files\\Microsoft FxCop 1.32\\Rules).

If you want more advanced settings, configure FxCop with yourtargets and selected rules and then save the project file. Open thenewly created .FxCop file in an editor (for instance notepad) andfind the <Settings /> element. Expand it to anopening and closing tag (e.g.<Settings></Settings>), and between it add

<RuleTypeName=”MethodComplexity”></Rule>

Between the Rule element add any of the following entries asName attributes of an Entry element (as exampled following) –

Connection String – an OleDb connection stringdetermining where it will log metrics. e.g.Provider=SQLNCLI;Server=(local);Database=Analysis;Trusted_Connection=yes;
Target Table – The target table for metriclogging. Default –MethodComplexity
Cyclomatic CriticalError – Level at which a critical error istriggered. Default – 60
Cyclomatic Error – Level at which an error istriggered. Default – 50
Cyclomatic Critical Warning – Level at which acritical warning is triggered. Default – 45
Cyclomatic Warning – Level at which a warning istriggered. Default – 40
Cyclomatic Information – Level at which aninfromation event is triggered. Default – 20
Cyclomatic Recommended – Recommended level.Default – 20
Statements Critical Error – Statement count atwhich a critical error is triggered. Default – 500
Statements Error – Statement count at which anerror is triggered. Default – 350
Statements Critical Warning – Statement count atwhich a critical warning is triggered. Default – 250
Statements Warning – Statement count at which awarning is triggered. Default – 200
Statements Information – Statement count at whichan information event is triggered. Default – 150
Statements Recommended – Recommended maximumstatement count per method. Default – 100
Callouts Critical Error – Callout count at which acritical error is triggered. Default – 100
Callouts Error – Callout count at which an erroris triggered. Default – 75
Callouts Critical Warning – Callout count at whicha critical warning is triggered. Default – 50
Callouts Warning – Callout count at which awarning is triggered. Default – 40
Callouts Information – Callout count at which aninformation event is triggered. Default – 30
Callouts Recommended – Recommended maximum calloutcount per method. Default – 30

For instance, you might end up with a<Settings> element that looks like thefollowing:

<Settings><RuleTypeName=”MethodComplexity”><Entry Name=”ConnectionString”>Provider=SQLNCLI;Server=(local);Database=Analysis;Trusted_Connection=yes;</Entry><EntryName=”Callouts Warning”>100</Entry><EntryName=”Cyclomatic CriticalWarning”>500</Entry></Rule></Settings>

If you opt to take advantage of metrics logging, the destinationtable (which will be default will be MethodComplexity,unless overridden with the Target Table name entry)requires the following columns:

ContainingType – text (e.g.nvarchar(255))
MethodName – text (e.g. nvarchar(255))
Cyclomatic – int
Statements – int
Callouts – int

e.g.
CREATE TABLE [dbo].[MethodComplexity](
 [ContainingType] [nvarchar](255) COLLATESQL_Latin1_General_CP1_CI_AS NOT NULL,
 [MethodName] [nvarchar](255) COLLATESQL_Latin1_General_CP1_CI_AS NOT NULL,
 [Cyclomatic] [int] NOT NULL,
 [Statements] [int] NOT NULL,
 [Callouts] [int] NOT NULL
) ON [PRIMARY]

Hopefully someone finds this interesting. It scratched my itch.