Introduction
An automated code analyzer can come in handy when maintaining and refactoring an existing project. For this goal I have written a 'static code analyzer', that's a code analyzer that operates by analyzing compiled assemblies. It thereby analyzes the method calls and builds a graph of which method calls which method. The result of the analyzer is a graph in memory that looks like what's represented in the following picture:
In fact, the analyzer has no notion of classes or types, it only looks to the methods and their calls. So from the picture, the analyzer only sees the green dots and their connecting lines.
Still, with this limited view, and with some 'smart algorithms', this code analyzer can be used for a lot of applications, including:
Providing a simplified view on the application logic
Asserting certain coding conventions
Detecting 'dead code'
Checking test code coverage (on method level)
Checking test code quality (i.e. whether Assert methods are called)
...
To ease you in writing those several applications, the analyzer comes with an extensible set of 'processors' including a rule based processor!
Getting started
To run the analyzer, you first need to write an XML file that describes the analysis process. The code analyzer can then be started with the following code (where "SomeProfile.xml" is the process description file):
// Create an AnalyzerSession and process it through an Analyzer:
StaticCodeAnalyzerSession session = new StaticCodeAnalyzerSession(@"SomeProfile.xml");
StaticCodeAnalyzer analyzer = new StaticCodeAnalyzer();
MethodList methods = analyzer.Process(session);
The MethodList object that is returned by the Process method is a list of all methods (ma1, ma2, ma3, mb1, mb2, mb3, mb4, ..., me3, me4) of the assemblies that were analyzed. The list contains objects of type "Arebis.CodeAnalysis.Static.Method", which provides properties as CallsMethods (which methods are directly called by this one), CalledByMethods (which methods call this method directly), but also GetAllCallsMethods() and GetAllCalledByMethods() methods which retrieve both directly and indirectly called methods.
For instance, the GetAllCalledByMethods() on method mc2 will return: ma2, ma3, mb2, mb3, mb4 and mb5.
The MethodBase property provides access to the "System.Reflection.MethodBase" object matching this method:
The analysis process description is an XML file that has the following structure:
<?xml version="1.0" encoding="utf-8" ?>
<analyzer>
<assemblies>
...
</assemblies>
<language code="C#">
...
</language>
<processors>
...
</processors>
</analyzer>
The <assemblies> section lists, using wildcards, the assemblies to include in the analysis.
The <language> section describes the programming language to use. This is not used during the analysis process, but is used afterwards, when you ask string representations of method signatures etc. (the language settings will for instance replace "System.Int32" into "int" and remove known namespaces).
The <processors> section contains custom processors to run during the analysis process. You can easily implement your own processors by implementing the IProcessor interface, but for the most common tasks, predefined processors are already provided, including a rule based one.
namespace Arebis.CodeAnalysis.Static
{
public interface IProcessor
{
void Initialize(XmlNode processorInstance);
void Process(MethodList methods);
}
}
In next posts on this blog, I will explain how to use the rule based processor and other provided processors to write some useful applications.
In the mean time, take a look at the following sample, including all the code of the static code analyzer. The sample already enviels how to detect dead code and perform other analysis tasks:
Static Code Analyzer code & sample
StaticCodeAnalyzerSample.zip (42.79 kb)
Note that the code assumes .NET 3.5 and also the Microsoft Parallel Extensions (System.Threading.dll). If you don't have .NET 3.5, you'll have to change the sample code (which yses LINQ), but on the code analyzer, you'll only have to change the reference to the HashSet<T> class to use the one provided in the Arebis.Common project. If you don't have the Microsoft Parallel Extensions, you can use the "Parallel" class provided in the Arebis.Common project, or even replace the Parallel.ForEach call into a regular foreach.
For this project, I must thank Sorin Serban, which did most of the work for the core of the analyzer engine by publish his method body parser on CodeProject (http://www.codeproject.com/KB/cs/sdilreader.aspx).