Quality

Querying Your Code Base Using NDepend’s CQLinq

Your code base has a lot to tell you. The question is: How can you listen to it? You can identify code smells when you’re reading code or extending it, but this doesn’t give you an overview. After you have been working for a while in a project, you can name some of its strengths and weaknesses. But this approach takes a long time, relies on experience and is subjective. It would be nice if you could query a code base in a structured way. Basic search functionality from an IDE like Visual Studio isn’t powerful enough. NDepend allows you to query .Net code using LINQ syntax through CQLinq – Code Query LINQ. In this blog post we’ll discuss how to query your code base using CQLinq.

An Example

Since I like to learn from examples, let’s first see a simple, yet very powerful query. The following code detects classes that are candidates to be turned into structures. This is one of the default CQLinq rules.

from t in JustMyCode.Types where
  t.IsClass &&
 !t.IsGeneratedByCompiler &&
 !t.IsStatic &&
  t.SizeOfInst > 0 &&

  // Structure instance must not be too big,
  // else it degrades performance.
  t.SizeOfInst <= 16 &&
  // Must not have children
  t.NbChildren == 0 &&

  // Must not implement interfaces to avoid boxing mismatch
  // when structures implements interfaces.
  t.InterfacesImplemented.Count() == 0 &&

  // Must derive directly from System.Object
  t.DepthOfDeriveFrom("System.Object".AllowNoMatch()) == 1 &&

  // Structures should be immutable type.
  t.IsImmutable
select new { t, t.SizeOfInst, t.InstanceFields }

As you can see, the query is quite readable. Even if you don’t know the CQLinq syntax, you understand what it does. And since it’s based on Linq, it already seems familiar.

Anatomy of a CQLinq Query

Let’s look at the basic elements of a CQLinq query.

Domain

The domain is what you are querying on:

from t in JustMyCode.Types

CQLinq allows you to query on a set of predefined domains: assemblies, namespaces, types, methods and fields. The root domain, context.CodeBase, is an entry point to all other domains. You can use the built in intellisense support from the Query and Rules Editor to explore it:

CQLinq Query Editor

But, since you’ll mainly use the subdomains, there are a set of shortcuts defined for these. For example, Types is just a shortcut for context.CodeBase.Types.

Since many times you’re not interested in querying third party code, there are two disjoint predefined domains:

  • Application – contains only application code
  • ThirdParty – contains only 3rd party code, like System.*, mscorblib, NHibernate

Apart from these two, there’s also the JustMyCode view, which excludes generated code. You can define code to exclude by adding the notmycode keyword in front of a query. Here is a simple query that excludes Specflow generated files:

notmycode from t in Types where
  t.SourceFileDeclAvailable && 
  t.SourceDecls.First().SourceFile
   .FileName.ToLower().EndsWith(".feature.cs")
select t

Note: This is not required, since Specflow adds the System.CodeDom.Compiler.GeneratedCodeAttribute on generated types. NDepend already excludes these from JustMyCode. You can see all predefined notmycode queries in the Defining JustMyCode queries and rules group:

Just My Code definition

You can also refine the domain by using the query operator syntax to filter and narrow down your search:

from t in JustMyCode.Namespaces.WithNameLike("Library")

Properties to Query On

NDepend’s API has a feature rich object model, that you can see in our Classes that are candidates to be turned into structures example. You can query on the following properties of a code element:

  • Name and file location
  • Interfaces and class hierarchy
  • Visibility
  • Mutability
  • Attributes
  • Code Dependencies
  • A set of predefined code metrics like lines of code, cyclomatic complexity, code coverage, coupling, cohesion.

Results

The result of a CQLinq query can either be a scalar value (e.g. a count) or an anonymous object. In the later case, the first property of the object must be an assembly, namespace, type, method or field:

from t in JustMyCode.Types
select new { t, t.SizeOfInst, t.InstanceFields }

Query Targets

There are many situations in which you would want to query the dependencies between code elements. For example, in our query, we want to see the inheritance depth from System.Object.

t.DepthOfDeriveFrom("System.Object".AllowNoMatch()) == 1

To make these queries simpler, NDepend allows you to query for the element and then for its usage in one go. This functionality is exposed through a set of extension methods. You can query if a code element is using, creating, assigning, returning or deriving from another code element.

Also, in order to be able to write generic queries that don’t break when you can’t find an element, you can use the AllowNoMatch() extension method. Without this, if the element is not found in the code base, the query will not compile. This makes sure that the query will always compile, even when it can’t match a code element.

Transforming a Query into a Rule

Transforming a query into a rule is straightforward. You just need to prefix the query with warnif count:

warnif count > 0 from t in JustMyCode.Types where

This will trigger a warning when you break the rule. If you want to break the build (i.e. return exit code 1 after running NDepend) you can mark the rule as Critical by clicking the Critical button in the Queries and Rules Editor.

CQLinq critical rule

In the Queries and Rules Explorer window, you can export rules to a rule file. This is useful when you want to share the same set of rules across the company:

CQLinq Rules file

Code Diff

NDepend allows you to query the code differences between two different versions of the code base. The OlderVersion() and NewerVersion() extension methods allow getting the older or newer version of a code element. For a full list of methods, check out the NDepend documentation.

This is useful when adopting NDepend on a legacy project. Usually, when you first run static code analysis (like FxCop/CodeAnalysis or StyleCop) on an existing code base, you are going to see two problem:

  • The first problem is that there is a lot of noise. Deciding what should be fixed now will require a thorough analysis. The team should agree on a set of rules and their severity.
  • The second, bigger problem, is that it doesn’t factor parts of the code that you don’t touch anymore. By analyzing just the the current version of the code, you lose a lot of historical information. SonarQube does store previous analysis results. This allows you to define delta metrics and track trends over time, but it’s still somewhat limited since I would like more granular control.

These two problems make it harder to decide on what to focus. NDepend goes one step further and allows you to define evolutionary rules. This makes it easy to apply the boy scout rule and only fix what you touch. Here is the default Avoid making large methods even larger NDepend rule:

warnif count > 0 
from m in JustMyCode.Methods where
 !m.IsAbstract &&
  m.IsPresentInBothBuilds() &&
  m.CodeWasChanged() &&
  // Eliminate constructors from match, since they get larger
  // as soons as some fields initialization are added.
 !m.IsConstructor &&
 !m.IsClassConstructor

let oldLoc = m.OlderVersion().NbLinesOfCode
where oldLoc > 15 && m.NbLinesOfCode > oldLoc

select new { m,
    oldLoc,
    newLoc = m.NbLinesOfCode
}

Examples

This section will contain a few practical examples of how to use CQLinq to query your code base.

Stability Metrics

Uncle Bob defined a set of principles and metrics that evaluate the package structure of an application. In a previous post we have already mentioned NDepend’s Abstractness vs Instability Graph. Now we’ll see how to check what assemblies in our code base don’t adhere to the stability principles. If you’re not familiar with the definition of stability, I would advise you to read Uncle Bob’s paper.

The Acyclic Dependencies Principle (ADP)

The dependency structure between packages must be a Directed Acylcic Graph (DAG). That is, there must be no cycles in the dependency structure.

Visual Studio doesn’t allow dependency cycles between projects. But, if you’re using namespaces as logical components, you can introduce circular dependencies between namespaces. NDepend has a default rule for avoiding namespace dependency cycles. As discussed in the previous post, you can also use the Dependency Structure Matrix to highlight these.

The Stable Dependencies Principle (SDP)

The dependencies between packages in a design should be in the direction of the stability of the packages. A package should only depend upon packages that are more stable that it is.

NDepend doesn’t have a default rule for SDP, but it’s easy to implement one since it already calculates the Instability metric for a given assembly.

from me in JustMyCode.Assemblies
  let deps = me.AssembliesUsed.Where(d => me.Instability < d.Instability)
  where deps.Any()
select new {me, deps}

The Stable Abstractions Principle (SAP)

Packages that are maximmally stable should be maximmaly abstract. Instable packages should be concrete. The abstraction of a package should be in proportion to its stability.

NDepend has a default rule for checking Assemblies that don’t satisfy the Abstractness/Instability principle. You can see how simple the query is, since NDepend already computes the Distance from main sequence (D) metric.

Detecting tests with complex setup

I’m not a fan of having long test setups. Some people say that you should not use test setups at all. With CQLinq it’s quite easy to write a query that detects long setups:

from m in JustMyCode.Methods.Where(method =>
  method.HasAttribute("NUnit.Framework.*SetUpAttribute") &&
  method.NbLinesOfCode > 6)
select m.ParentType

Ensuring temporally coupled methods are used together

Temporal coupling is a design smell. But you sometimes have to live with it, because of legacy code or the API of some framework. So how could you ensure you’re calling both methods?

Let’s say you have the following classes:

namespace App
{
    public class TemporallyCoupled
    {
        public void Init(string value) { }

        public void DoSomething() { }
    }

    public class Caller
    {
        public void Test()
        {
            var temp = new TemporallyCoupled();
            temp.DoSomething();
        }
    }
}

If you need to call Init before calling DoSomething, this call will fail at runtime. With CQLinq, you can define a rule to check methods that call DoSomething, but don’t call Init:

from m in JustMyCode.Methods.Where(caller =>
  caller.IsUsingMethod("App.TemporallyCoupled.DoSomething()") &&
 !caller.IsUsingMethod("App.TemporallyCoupled.Init*"))
select m

This query doesn’t catch all the issues. It doesn’t cater for calling Init after DoSomething or for calling Init from a different method. For the second case, you could switch the query to search for types that have a field of type TemporallyCoupled and call DoSomething without calling Init.

from t in JustMyCode.Types.Where(caller =>
  caller.Fields.Where(f => f.FieldTypeIs("App.TemporallyCoupled")).Any() &&
  caller.IsUsingMethod("App.TemporallyCoupled.DoSomething()") &&
 !caller.IsUsingMethod("App.TemporallyCoupled.Init*"))
  let methods = t.Methods.Where(m => m.IsUsingMethod("App.TemporallyCoupled.DoSomething()"))
select new {t, methods }

This query doesn’t pickup other false negatives. But the idea is that you can write this type of queries. Depending on your application and code, you can tailor the rule to pickup most of the issues.

Detecting API Breaking Changes

NDepend can also detect API Breaking Changes. In the next version you will also be able to get the Version of an Assembly. This will allow you to create a rule that breaks the build when you introduce API breaking changes, but you don’t increment the Major Version. This will ensure that you won’t make breaking changes by mistake.

Conclusion

CQLinq is a powerful tool. Although it might take some time to learn it well, you can benefit from it right from the start. There are over 250 default rules that you can read and learn from. You can tweak them and get instant feedback in the Queries and Rules Editor. You can use NDepend’s code search functionality which generates CQLinq queries on the fly.

After you start getting the hang of it, you should start tailoring rules for your own code base. The one size fits all rule set approach of many static code analyzers is both an advantage and a disadvantage. On one hand, when you want to compare different code bases in an objective way, you need a standardized rule set. On the other hand, every code base is unique. As Erik Dietrich says, custom code metrics allow you to define targeted goals. You can ensure that types in a certain namespace are immutable, namespace A doesn’t depend on namespace B, or that certain classes are at least 90% covered by unit tests. Being able to define custom metrics can help you improve the quality of your code base.