Code
Query Language 1.8 Specification
CQL
1.8
CQL
1.8 is supported by NDepend 2.9 and higher, including NDepend 3.x versions
Copyright
SMACCHIA.COM S.A.R.L 2006/2007/2008/2009
All
right reserved
The CQL language and real-world
needs
Storing CQL queries and constraints
in your C# or VB.NET source code
Some examples of queries and
constraints written in CQL
Examples of code Quality constraints
Examples of naming constraints
Examples of design constraints
Examples of encapsulation
constraints
Examples of queries on the graph of
dependencies
Examples of queries on the
inheritance tree
Examples of queries to get extremum
WARN IF xxx IN: Query vs. Constraint
TOP xxx: Restrict the number of rows
in the result
FROM / OUT OF xxx: Domain of search
WHERE xxx: Define a set of
conditions
ORDER BY xxx: Order rows of the
result
Kind of Code Elements’ Names
Prefixes
The OPTIONAL: Code Elements’ Names
Prefix
ABT
T (Association Between Types)
LCOM
T (Lack Of Cohesion Of Methods)
LCOMHS T (Lack Of Cohesion Of Methods
Henderson-Sellers)
CQL test coverage metrics conditions
ContainsNamespaceDependencyCycle A
ContainsMethodDependencyCycle M
CQL boolean conditions dedicated to
Build Comparison
CQL boolean conditions dedicated to
Optimal Encapsulation
CQL boolean conditions dedicated to
Purity / Side-Effects / Mutability
CQL is a
language which allows writing queries on the code structure of any.NET
application, independently of the .NET language used (C#, VB.NET, C++/CLI …).
For example, the following CQL query returns all the methods of your
application with more than 200 IL instructions, ordered from the biggest to the
smallest:
SELECT METHODS WHERE NbILInstructions > 200 ORDER BY NbILInstructions DESC
You might
wish to avoid methods with more than 200 IL instructions since they are hard to
maintain. After having shrunk all your methods, you certainly want to be
notified when during development a method exceeds this threshold. The CQL
language addresses this need by allowing the transformation of queries into
constraints. For example, here is our previous query rewritten as a constraint:
WARN IF Count >
With almost
a hundred keywords, the CQL language allows you to deal with various conditions
on your code structure. It allows to write code quality constraints, naming constraints, design constraints, encapsulation constraints, queries on the graph of
dependencies, queries on the
inheritance tree, queries to get the biggest or
smallest code elements according to almost 30 metrics and much more.
The tool VisualNDepend
allows the editing and execution of CQL queries and constraints. A GUI allows
you to have a unique understanding of your application. VisualNDepend
can also be used to generate reports during each build of your application.

Writing CQL
queries and constraints is straightforward thanks to the three following
features:


The CQL
language has been conceived to understand and control real-world application. In
a real-world environment, there are often exceptions (like automatically
generated methods which are often very big) and you need to allow a few
particular methods to exceed this threshold without being bothered by our
previous constraint. The CQL language offers numerous features allowing you to
deal with such exceptions. For example, all generated methods might contain the
word “Generated” in their names:
WARN IF Count >
Or maybe,
all generated methods are in dedicated assemblies, namespaces or types:
WARN IF Count >
Or maybe,
you prefer to mention each one explicitly:
WARN IF Count >
You can also
mix all these features in the same constraint:
WARN IF Count >
NDepend
stores your CQL queries and constraints in the project file. As the source code is the design you might prefer storing your CQL queries and
constraints directly in your source code (C#, VB.NET...). To do so, you must
reference the assembly ./Lib/NDepend.CQL.dll and use the attribute NDepend.CQL.CodeRuleAttribute
in your code.
WARN IF Count >
// METHODS WHERE NbILInstructions > 200 are extremely complex and
// should be split in smaller methods
// (except if they are
automatically generated by a tool).
WARN IF Count >
// METHODS WHERE ILCyclomaticComplexity > 20 are hard to understand
and maintain.
// METHODS WHERE ILCyclomaticComplexity > 40 are extremely complex
and should be split
// in smaller methods (except if
they are automatically generated by a tool).
WARN IF Count >
// METHODS WHERE NbParameters > 5 might be painful to call and might
degrade performance.
// You should prefer using additional properties/fields to the declaring
type to handle
// numerous states. Another alternative is to provide a class or
structure dedicated to
// handle arguments passing (for example see the class
System.Diagnostics.ProcessStartInfo
// and the method
System.Diagnostics.Process.Start(ProcessStartInfo))
WARN IF Count >
// METHODS WHERE NbVariables > 8 are hard to understand and maintain.
// METHODS WHERE NbVariables > 15 are extremely complex and should be
split in
// smaller methods (except if they
are automatically generated by a tool).
WARN IF Count >
// TYPES WHERE NbMethods > 20 might be hard to understand and
maintain
// but there might be cases where it is relevant to have a high value
for NbMethods.
// For example, the
System.Windows.Forms.DataGridView standard class has more than 1000 methods.
WARN IF Count >
// TYPES WHERE NbFields > 20 AND !IsEnumeration might be hard to
understand and maintain
// but there might be cases where it is relevant to have a high value
for NbFields.
// For example, the System.Windows.Forms.Control standard class has more
than 200 fields.
// The value of the metric
SizeOfInst might be a better indicator of complex type.
WARN IF Count >
// TYPES WHERE SizeOfInst > 64 might degrade performance (depending
on the number of
// instances created at runtime) and might be hard to maintain.
// However it is not a rule since sometime there is no alternative
// (the size of instances of the
System.Net.NetworkInformation.SystemIcmpV6Statistics
// standard class is 2064 bytes).
WARN IF Count >
// TYPES WHERE LCOM > 0.8 AND NbFields > 10 AND NbMethods >10
might be problematic.
// However, it is very hard to avoid such non-cohesive types. The LCOMHS
metric
// is often considered as more
efficient to detect non-cohesive types.
WARN IF Count >
// TYPES WHERE LCOMHS > 1.0 AND NbFields > 10 AND NbMethods >10
should be avoided.
// Note that this constraint is stronger than the constraint
// TYPES WHERE LCOM > 0.8 AND
NbFields > 10 AND NbMethods >10.
SELECT TYPES WHERE DepthOfInheritance > 6 ORDER BY DepthOfInheritance DESC
// TYPES WHERE DepthOfInheritance > 6 might be hard to maintain.
However it is not
// a rule since sometime your classes might inherit from tier classes
which have a
// high value for depth of inheritance. For example, the average depth
of inheritance
// for framework classes which
derive from System.Windows.Forms.Control is 5.3.
WARN IF Count >
// Type with big instances can be problematic
// (Obviously the SizeOfInst metric does not do a deep traverse.
// Any instance reference field
will count for 4 bytes.
//
It is also unable to cop with generic types properly)
WARN IF Percentage >
// With generics, boxing and
unboxing should be very rare.
WARN IF Percentage >
WARN IF Count >
// As classes inside an assembly should be strongly related,
// the cohesion should be high. On the other hand, a value which is too
high may
// indicate over-coupling. A good
range for RelationalCohesion is 1.5 to 4.0.
WARN IF Count >
// A static field should not be
named 'm_XXX'
WARN IF Count >
// A non-static field should not
be named 's_XXX'
WARN IF Count >
// The name of an interface should
begin with a 'I'
WARN IF Count >
// The name of an exception class
should end with 'Exception'
WARN IF Count >
// The name of an attribute class
should end with 'Attribute'
WARN IF Count >
!NameLike "^[A-Z]" AND // The name of a
type should begin with an Upper letter.
!NameLike "__StaticArrayInit" AND // Except __StaticArrayInit generated type
!NameLike "<" // Except C# compiler generated
type
WARN IF Count >
!NameLike "^[A-Z]" AND
!(IsClassConstructor OR IsConstructor OR
IsPropertyGetter OR IsPropertySetter OR
IsIndexerGetter OR IsIndexerSetter OR
IsEventAdder OR IsEventRemover OR
IsOperator)
// The name of a regular method
should begin with an Upper letter.
WARN IF Count >
// It indicate stateless types that
might eventually be turned into static classes.
WARN IF Count >
// A field should not be public or
internal, except for performance reasons.
WARN IF Count >
// A nested type should not be
public or internal.
WARN IF Count >
// Restrict the possibility of
using the type "System.Xml.XmlChildNodes" only to certain namespace.
WARN IF Count >
DepthOfIsUsing "System.Xml.XmlNamedNodeMap.InsertNodeAt(Int32,XmlNode)"== 1
// Restrict the possibility of calling the method
"System.Xml.XmlNamedNodeMap.InsertNodeAt(Int32,XmlNode)"
// only to certain namespace.
WARN IF Count >
"System.Xml.XmlDocumentFragment",
"System.Xml.XmlEntityReference",
"System.Xml.XmlDocumentType",
"System.Xml.XmlEntity",
"System.Xml.XmlDocument",
"System.Xml.XmlAttribute",
"System.Xml.XmlElement" WHERE DepthOfCreateA "System.Xml.XmlLoader" ==1 ORDER BY DepthOfCreateA
// Restrict the possibility of
creating an instance of "System.Xml.XmlLoader" only to certain type.
WARN IF Count >
WHERE DepthOfCreateA "System.Xml.XmlLoader" == 1 ORDER BY DepthOfCreateA
// Restrict the possibility of
creating an instance of "System.Xml.XmlLoader" only to certain
namespaces.
WARN IF Count >
WHERE DepthOfIsUsing "System.Windows.Forms.Internal" == 1
// Restrict the possibility of
using a namespace only to one assembly.
WARN IF Count >
DepthOfIsUsing "System.Windows.Forms.DataGridView+HitTestInfo.typeInternal" == 1
// Restrict the possibility of using the field
//
"System.Windows.Forms.DataGridView+HitTestInfo.typeInternal"
// only to certain namespace.
SELECT METHODS WHERE IsUsing "System.Xml.XmlWriter..ctor()" ORDER BY DepthOfIsUsing
// 'IsUsing/IsUsedBy/DepthOfIsUsing/DepthOfIsUsedBy' conditions are
useful
// to understand who calls statically who for example to anticipate
future impacts
// of some refactoring or to
provide some customized encapsulation constraints.
SELECT METHODS WHERE DepthOfIsUsing "System.Xml.XmlWriter..ctor()" <=3
// 'IsUsing/IsUsedBy/DepthOfIsUsing/DepthOfIsUsedBy' conditions are
useful
// to understand who calls statically who for example to anticipate
future impacts
// of some refactoring or to
provide some customized encapsulation constraints.
SELECT METHODS WHERE
IsUsedBy "System.Xml.Serialization.SoapSchemaImporter.ImportMembersMapping(String,String,SoapSchemaMember[])"
ORDER BY DepthOfIsUsedBy
// 'IsUsing/IsUsedBy/DepthOfIsUsing/DepthOfIsUsedBy' conditions are
useful
// to understand who calls statically who for example to anticipate
future impacts
// of some refactoring or to
provide some customized encapsulation constraints.
SELECT METHODS WHERE CreateA "System.Xml.XmlWriter" ORDER BY DepthOfCreateA
// 'CreateA'/'DepthOfCreateA' conditions can be useful to enforce some
constraints on design patterns such as factory
// where the ctors must be called
only from certain methods or certain type.
SELECT METHODS WHERE DepthOfCreateA "System.Xml.XmlDocument" < 10 ORDER BY DepthOfCreateA
// 'CreateA'/'DepthOfCreateA' conditions can be useful to enforce some
constraints on design patterns such as factory
// where the ctors must be called
only from certain methods or certain type.
SELECT METHODS WHERE IsUsing "System.Xml.XmlWriter.WriteEndElement()"
SELECT METHODS WHERE IsUsing "System.Web.Security.PassportIdentity.LoginUser()"
OR IsUsedBy "System.Web.Security.PassportIdentity.LoginUser()"
// 'IsUsing/IsUsedBy/DepthOfIsUsing/DepthOfIsUsedBy' conditions are
useful
// to understand who calls statically who for example to anticipate
future impacts
// of some refactoring or to provide
some customized encapsulation constraints.
SELECT TYPES WHERE DepthOfIsUsedBy "System.Net.Sockets.Socket" > 2 ORDER BY DepthOfIsUsedBy
// Try to play with the numeric intellisense by selecting the 2 and
moving the trackbar.
// A 'IsUsing/IsUsed' relation between two types A and B is created by
NDepend as soon as
// there is a 'IsCalled/IsUsedBy'
relation between a method of A and a method of B.
SELECT NAMESPACES WHERE DepthOfIsUsedBy "System.Net.Sockets" <=2
// Try to play with the numeric intellisense by selecting the 2 and
moving the trackbar.
// A 'IsUsing/IsUsed' relation between two namespaces A and B is created
by NDepend as soon as
// there is a 'IsUsing/IsUsed'
relation between a type of A and a type of B.
SELECT TYPES WHERE DeriveFrom "System.Windows.Forms.Control" ORDER BY DepthOfDeriveFrom DESC
SELECT TYPES WHERE DeriveFrom "System.Web.UI.Control" ORDER BY DepthOfDeriveFrom DESC
SELECT TYPES WHERE DepthOfDeriveFrom "System.Windows.Forms.Control" == 1
// Select classes which derive
directly from control.
SELECT TYPES WHERE Implement "System.Web.UI.IDataSource"
SELECT TYPES WHERE Implement "System.Web.IHttpHandler" ORDER BY NbILInstructions DESC
SELECT TYPES WHERE NbChildren > 5 ORDER BY NbChildren DESC
SELECT TOP 10 METHODS WHERE !IsConstructor AND !IsClassConstructor ORDER BY NbILInstructions DESC
// Try to play with the numeric intellisense
by selecting the 2 and moving the trackbar.
SELECT TOP 10 TYPES FROM ASSEMBLIES "System.Web" ORDER BY NbMethods DESC
// Illustrate the 'FROM' domain of
search definition.
SELECT TOP 10 METHODS OUT OF NAMESPACES "System.Windows.Forms" ORDER BY NbVariables DESC
// Illustrate the 'OUT OF' domain
of search definition.
SELECT TOP 10 METHODS ORDER BY NbParameters ,NbVariables ASC, NbILInstructions DESC
// Illustrate the fact that several 'ORDER BY' clause can be specified.
// By default the 'ASC' option is chosen.
// Check in the Query Result panel
that 4 columns are displayed.
SELECT TOP 10 TYPES ORDER BY TypeCa DESC
// TypeCa: Afferent Coupling
// The Afferent Coupling for a particular
type is the number of types that depends directly on it.
SELECT TOP 10 TYPES ORDER BY TypeCe DESC
// TypeCe: Efferent Coupling
// The Efferent Coupling for a
particular type is the number of types it directly depends on..
SELECT TOP 10 METHODS WHERE !IsConstructor AND !IsClassConstructor ORDER BY NbParameters DESC
SELECT TOP 10 METHODS WHERE IsPropertyGetter OR IsPropertySetter ORDER BY NbILInstructions DESC
SELECT TOP 10 TYPES WHERE IsClass ORDER BY NbILInstructions DESC
SELECT TOP 10 TYPES WHERE IsStructure ORDER BY NbILInstructions DESC
SELECT TOP 10 TYPES WHERE IsClass ORDER BY NbFields DESC
SELECT TOP 10 TYPES WHERE IsStructure ORDER BY NbFields DESC
SELECT TOP 10 TYPES WHERE IsClass ORDER BY SizeOfInst DESC
SELECT TOP 10 TYPES WHERE IsStructure ORDER BY SizeOfInst DESC
SELECT TOP 10 NAMESPACES ORDER BY NbILInstructions DESC
SELECT TOP 10 ASSEMBLIES ORDER BY NbILInstructions DESC
SELECT ASSEMBLIES ORDER BY NbILInstructions DESC
The SELECT METHODS expression allows to select all kind
of methods. You can use a combination of following conditions to select some
particular methods:
NbILInstructions NbMethods
NbParameters NbVariables
ILCyclomaticComplexity
IsUsing DepthOfIsUsing IsDirectlyUsing
IsUsedBy DepthOfIsUsedBy
IsDirectlyUsedBy CreateA DepthOfCreateA
IsPublic IsInternal IsProtected IsProtectedOrInternal
IsProtectedAndInternal IsPrivate IsConstructor
IsPropertySetter IsPropertyGetter IsStatic
IsVirtual IsAbstract
IsUsingBoxing IsUsingUnboxing
IsGeneric IsUsingPointers
IsOperator IsIndexerSetter
IsIndexerGetter IsEventAdder
IsEventRemover IsClassConstructor
The SELECT FIELDS
expression allows to select all kind of fields. You can use a combination of
following conditions to select some particular field:
IsPublic IsInternal IsProtected IsProtectedOrInternal
IsProtectedAndInternal IsPrivate IsEnumValue
IsStatic IsLiteral IsInitOnly IsEventDelegateObject
IsUsedBy DepthOfIsUsedBy
IsDirectlyUsedBy
The SELECT TYPES
expression allows to select all kind of types. You can use a combination of
following conditions to select some particular types:
NbILInstructions NbMethods
NbFields NbTypes ILCyclomaticComplexity SizeOfInst DepthOfInheritance
NbChildren TypeCe TypeCa ABT LCOM LCOMHS
IsUsing DepthOfIsUsing IsDirectlyUsing
IsUsedBy DepthOfIsUsedBy
IsDirectlyUsedBy DeriveFrom
DepthOfDeriveFrom Implement
IsPublic IsInternal IsProtected IsProtectedOrInternal
IsProtectedAndInternal IsPrivate IsStatic IsAbstract IsUsingBoxing
IsUsingUnboxing IsGeneric
IsUsingPointers IsClass
IsStructure IsEnumeration
IsInterface IsSealed
IsNested IsDelegate IsAttributeClass IsExceptionClass
The SELECT NAMESPACES expression allows to select
some namespaces. You can use a combination of following conditions to select
some particular namespaces:
NbILInstructions NbMethods
NbFields NbTypes NbNamespaces
IsUsing DepthOfIsUsing IsDirectlyUsing
IsUsedBy DepthOfIsUsedBy
IsDirectlyUsedBy
The SELECT ASSEMBLIES expression allows to select
some assemblies. You can use a combination of following conditions to select
some particular assemblies:
NbILInstructions NbMethods
NbFields NbTypes NbNamespaces Abstracness
Instability NormDistFromMainSeq DistFromMainSeq RelationalCohesion
AsmCa AsmCe
IsUsing DepthOfIsUsing IsDirectlyUsing
IsUsedBy DepthOfIsUsedBy
IsDirectlyUsedBy
You can
transform every CQL query into a CQL constraint by adding a WARN IF xxx IN expression at
the beginning. There are two kinds of CQL constraint.
WARN IF Count >
WARN IF Count <=
WARN IF Percentage >
WARN IF Percentage <=
WARN IF xxx IN expressions are
optional.
You can
restrict the number of code elements selected thanks to a TOP xxx expression. For
example:
SELECT TOP 10 METHODS ORDER BY NbILInstructions DESC
SELECT TOP 10 TYPES WHERE IsStructure ORDER BY SizeOfInst DESC
It is
likely that queries which contain a TOP xxx expression also take
advantage of an ORDER BY xxx
expression.
TOP xxx expressions are
optional.
You can
restrict the domain of search for code elements by using a FROM / OUT OF xxx expression.
By default, the domain of search is the whole set of application assemblies.
A FROM / OUT OF xxx expression
can be defined as a set of assemblies, namespaces or types when the kind of
code elements requested is METHODS or FIELDS. For example:
SELECT METHODS FROM ASSEMBLIES "System" WHERE IsAbstract
SELECT FIELDS OUT OF NAMESPACES "System.Net","System.XML" WHERE IsInitOnly
SELECT METHODS OUT OF TYPES "System.Xml.XmlWriter" WHERE CreateA "System.Xml.XmlWriter"
A FROM / OUT OF xxx expression
can be defined as a set of assemblies or namespaces when the kind of code
elements requested is TYPES. For example:
SELECT TYPES FROM ASSEMBLIES "System","System.XML" WHERE IsDelegate
SELECT TYPES OUT OF NAMESPACES "System.Windows.Forms" WHERE IsUsing "System.Windows.Forms.Control"
A FROM / OUT OF xxx expression
can be defined as a set of assemblies when the kind of code elements requested
is NAMESPACES. For example:
SELECT NAMESPACES OUT OF ASSEMBLIES "System.Windows.Forms" WHERE DepthOfIsUsing "System.Windows.Forms" == 2
The FROM / OUT OF xxx feature is
not available when the kind of code elements requested is ASSEMBLIES.
FROM / OUT OF xxx expressions
are optional.
You can
define a set of condition thanks to:
For
example:
SELECT METHODS WHERE
( NbILInstructions > 200 OR
ILCyclomaticComplexity > 50 OR
NbParameters > 5 OR
NbVariables > 8 )
AND
!( NameLike "InitializeComponent" OR NameLike "Generated")
WHERE xxx expressions are optional
but each query must have either a WHERE xxx expression or an ORDER BY xxx expression or
both.
You can
communicate to the CQL runtime engine how to sort code elements selected thanks
to the ORDER BY xxx
expression. Several metrics can be mentioned. Each metric mentioned can be
followed by one of the keyword ASC
or DESC to precise the
order of sort. The first metric mentioned will be the last one used for
sorting. For example:
SELECT TYPES ORDER BY NbILInstructions
SELECT METHODS ORDER BY NbILInstructions, NbParameters DESC, NbVariables ASC
ORDER BY xxx expressions are
optional but each query must have either a WHERE xxx expression or an ORDER BY xxx expression or
both.
You can use
following metrics to sort your result:
NbILInstructions NbMethods
NbFields NbTypes NbNamespaces NbParameters
NbVariables ILCyclomaticComplexity SizeOfInst DepthOfInheritance
NbChildren TypeCe TypeCa ABT LCOM LCOMHS Abstracness Instability
NormDistFromMainSeq DistFromMainSeq RelationalCohesion
AsmCa AsmCe DepthOfIsUsing DepthOfIsUsedBy DepthOfCreateA
DepthOfDeriveFrom
You can
insert comments ŕ la C++/C#/Java in
your CQL queries, for example:
SELECT METHODS /*Comment 1*/ ORDER
BY NbILInstructions // Comment 2
New in CQL version 1.4
You can
name your queries using the <Name> </Name> tags inside a line of a
comment. Query name are then displayed in VisualNDepend
in lieu of the whole query and can be more meaningful than the whole query
itself:
// <Name>Methods too big (NbLinesOfCode)</Name>
WARN IF Count > 0 IN SELECT TOP 10 METHODS WHERE NbLinesOfCode > 30 ORDER BY NbLinesOfCode DESC
First of
all, here are some C# code extracted from regression tests of NDepend that
illustrates how code elements are named in NDepend:
using
System.Collections.Generic;
using NDepend.CQL;
// Test the anonymous
namespace name : ""
[assembly: CQLConstraint(@"WARN IF Count !=
[CQLConstraint(@"WARN IF Count !=
class TypeInAnonymousNamespace {
[CQLConstraint(@"WARN IF Count !=
internal static
int MethodWithArrayParam(string[] args) { return
5; }
[CQLConstraint(@"WARN IF Count !=
unsafe internal
static void
MethodWithPointerParam(int* arg) { }
[CQLConstraint(@"WARN IF Count !=
internal static
void MethodWithOutParam(out int arg) { arg = 8; }
[CQLConstraint(@"WARN IF Count !=
internal static
void MethodWithRefParam(ref string arg) { }
[CQLConstraint(@"WARN IF Count !=
internal static
void MethodWithGenericParam(Dictionary<int, Dictionary<int, string>> arg) { }
}
[CQLConstraint(@"WARN IF Count !=
interface IInterfaceInAnonymousNamespace {
[CQLConstraint(@"WARN IF Count !=
int NonGenericMethod(short i);
}
namespace Test {
[CQLConstraint(@"WARN IF Count !=
class GenericClass<K, V> {
[CQLConstraint(@"WARN IF Count !=
int GenericMethod<U>(U u) { return 7; }
}
[CQLConstraint(@"WARN IF Count !=
struct GenericStructure<X> {
[CQLConstraint(@"WARN IF Count !=
int
GenericMethod<U>(U u) { return 8; }
}
[CQLConstraint(@"WARN IF Count !=
interface IGenericInterface<S, T>
{
[CQLConstraint(@"WARN IF Count !=
int GenericMethod<U>(U u);
[CQLConstraint(@"WARN IF Count !=
int NonGenericMethod(S s);
}
[CQLConstraint(@"WARN IF Count !=
interface INonGenericInterface {
[CQLConstraint(@"WARN IF Count !=
int GenericMethod<P>(P p);
[CQLConstraint(@"WARN IF Count !=
int NonGenericMethod(int i);
}
[CQLConstraint(@"WARN IF Count !=
class C1<I, J> :
IGenericInterface<I, J>, INonGenericInterface,
IInterfaceInAnonymousNamespace {
[CQLConstraint(@"WARN IF Count !=
int
IInterfaceInAnonymousNamespace.NonGenericMethod(short
i) {
return
i;
}
[CQLConstraint(@"WARN IF Count !=
int
IGenericInterface<I,J>.GenericMethod<U>(U u) {
return
6;
}
[CQLConstraint(@"WARN IF Count !=
int IGenericInterface<I,
J>.NonGenericMethod(I i) {
return
6;
}
[CQLConstraint(@"WARN IF Count !=
int
INonGenericInterface.GenericMethod<P>(P p) {
return
6;
}
[CQLConstraint(@"WARN IF Count !=
int INonGenericInterface.NonGenericMethod(int i) {
return
6;
}
[CQLConstraint(@"WARN IF Count !=
public int
GenericMethod<P>(P p) {
return
6;
}
[CQLConstraint(@"WARN IF Count !=
public int
NonGenericMethod(int i) {
return
6;
}
[CQLConstraint(@"WARN IF Count !=
class C2<Q, R, S> {
[CQLConstraint(@"WARN IF Count !=
public
int GenericMethod<P>(P p) {
return
6;
}
}
[CQLConstraint(@"WARN IF Count !=
class C3 { }
}
}
Sometime, 2
different kinds of code elements can have the same name. For example, you can
have one assembly named Foo, one
namespace named Foo and one class
named Foo. The IsUsing-like
conditions takes a code element as parameter and need to resolve if it is an
assembly, a namespace, a type, a method or a field.
SELECT METHODS WHERE IsDirectlyUsing "Foo"
Such a
query cannot compile because it cannot infer which code element Foo it is referencing. This is why we
need ASSEMBLY: NAMESPACE: TYPE: METHOD: and FIELD: prefixes.
SELECT METHODS WHERE IsDirectlyUsing "NAMESPACE:Foo"
The
OPTIONAL: prefix lets write some generic constraint. Suppose you want to be
advised when a method is using the .NET Framework method: System.Threading.Thread.Sleep(Int32). You just have to write the
following constraint:
WARN IF Count > 0 IN SELECT METHODS WHERE IsDirectlyUsing "System.Threading.Thread.Sleep(Int32)"
However, if
you want to forbid the use of the Thread.Sleep()
method, this method won’t be referenced anymore by the NDepend analysis because
it just keep code elements from tier assemblies that are used from your
application code. As a consequence, the constraint above won’t compile because
the method referenced can’t be found.
The
OPTIONAL: prefix lets remedy this problem and indicate to the CQL compiler that
if the code element is not found, an error should not be emitted:
WARN IF Count > 0 IN SELECT METHODS WHERE IsDirectlyUsing "OPTIONAL:System.Threading.Thread.Sleep(Int32)"
The
OPTIONAL: prefix must always appear before an ASSEMBLY: NAMESPACE: TYPE:
METHOD: or FIELD prefix.
Applicable on: ASSEMBLIES NAMESPACES TYPES METHODS FIELDS
Remarks: The
keyword NameLike is used to select code elements
whose names match a given regular expression. Regular expression accepts the
suffix \i to ignore case.
Example:
Select all
fields of application where the name begin with m_
SELECT FIELDS WHERE NameLike "^m_"
Select all
fields of application where the name begin with m_ or M_
SELECT FIELDS WHERE NameLike "^m_\i"
Applicable on: ASSEMBLIES NAMESPACES TYPES METHODS FIELDS
Remarks: The keyword NameIs is
used to select code elements whose names are equal to a given string.
Example: Select all methods of application where the
name is .ctor(String)
SELECT METHODS WHERE NameIs ".ctor(String)"
Applicable on: ASSEMBLIES NAMESPACES TYPES METHODS FIELDS
Remarks: The keyword FullNameLike is used to select
code elements whose full names match a given regular expression. Regular
expression accepts the suffix \i to ignore case.
Example: Select all methods of application where the
full name (i.e namespace.type.method) contains Database.
SELECT METHODS WHERE FullNameLike "Database"
Applicable on: ASSEMBLIES NAMESPACES TYPES METHODS FIELDS
Remarks: The keyword FullNameIs
is used to select code elements whose full names are equal to a given string.
Note that there can’t be more than one code element which matches a FullNameIs condition.
Example: Select all methods of application where the
full name is System.Windows.Forms.ToolStripMenuItem..ctor(String)
SELECT METHODS WHERE FullNameIs "System.Windows.Forms.ToolStripMenuItem..ctor(String)"
New in CQL version 1.1
Applicable on: ASSEMBLIES NAMESPACES TYPES METHODS
Remarks: This
metric (known as LOC) can be computed only if PDB files are present. NDepend
computes this metric directly from the info provided in PDB files. The LOC for
a method is equals to the number of sequence point found for this method in the
PDB file. A sequence point is used to mark a spot in the IL code that
corresponds to a specific location in the original source. More info about
sequence points here.Notice that sequence points which
correspond to C# braces‘{‘ and ‘}’ are not taken account.
Computing the number of lines of code from PDB’s sequence points allows to
obtain a logical LOC of code instead of a physical LOC (i.e
directly computed from source files). 2
significant advantages of logical LOC over physical LOC are:
Notice that the LOC for a type is the sum
of its methods’ LOC, the LOC for a namespace is the sum of its types’ LOC, the
LOC for an assembly is the sum of its namespaces’ LOC and the LOC for an
application is the sum of its assemblies LOC. Here are some observations:
Related Link:
Why
is it useful to count the number of Lines Of Code (LOC) ?
How
do you count your number of Lines Of Code (LOC) ?
Example:
SELECT METHODS WHERE NbLinesOfCode > 20
You can
also use the NbILInstructions keyword to
order the rows in the result list.
SELECT TOP 10 TYPES ORDER BY NbLinesOfCode DESC
Recommendations:
METHODS WHERE NbLinesOfCode > 20 are hard to understand and maintain. METHODS WHERE NbILInstructions > 40 are extremely complex and should be
split in smaller methods (except if they are automatically generated by a
tool).
There is no
particular recommendation for NbLinesOfCode
values on ASSEMBLIES, NAMESPACES and TYPES.
Applicable on: ASSEMBLIES NAMESPACES TYPES METHODS
Remarks: The number of IL instruction of some code
elements might be 0, for example for abstract method, interface or enumeration.
Example:
SELECT METHODS WHERE NbILInstructions > 200
You can
also use the NbILInstructions keyword to
order the rows in the result list.
SELECT TOP 10 TYPES ORDER BY NbILInstructions DESC
Recommendations:
METHODS WHERE NbILInstructions > 100 are hard to understand and maintain. METHODS WHERE NbILInstructions > 200 are extremely complex and should be
split in smaller methods (except if they are automatically generated by a
tool).
There is no
particular recommendation for NbILInstructions
values on ASSEMBLIES, NAMESPACES and TYPES.
New in CQL version 1.2
Applicable on: ASSEMBLIES NAMESPACES TYPES METHODS
Remarks: This metric can be computed only if PDB
files are present and if corresponding source files can be found. The number of
lines of comment is computed as follow:
Notice that
this metric is not an additive metric (i.e for example, the number of lines of
comment of a namespace can be greater than the number of lines of comment over
all its types).
Example:
SELECT METHODS WHERE NbLinesOfComment > 10
You can
also use the NbLinesOfComment keyword to
order the rows in the result list.
SELECT TOP 10 TYPES ORDER BY NbLinesOfComment DESC
Recommendations:
This metric is
not helpful to asses the quality of source code. We prefer to use the metric PercentageComment.
New in CQL version 1.2
Applicable on: ASSEMBLIES NAMESPACES TYPES METHODS
Remarks: This
metric is computed with the following formula:
PercentageComment = 100* NbLinesOfComment / (NbLinesOfComment + NbLinesOfCode)
Example:
SELECT METHODS WHERE PercentageComment < 10
You can
also use the PercentageComment keyword
to order the rows in the result list.
SELECT TOP 10 TYPES ORDER BY PercentageComment DESC
Recommendations:
Code where the percentage of comment
is lower than 20% should be more commented. However overly commented code (>40%)
is not necessarily a blessing as it can be considered as an insult to the
intelligence of the reader.
Applicable on: ASSEMBLIES NAMESPACES TYPES METHODS
Remarks: The value of the NbMethods
metric is equal to 1 for all methods, no matter it is static or not, it is abstract,
it is a constructor, it is a property accessor ….
Example:
SELECT TYPES WHERE NbMethods > 10
You can also use the NbMethods keyword to order the rows in the result list.
SELECT TOP 10 NAMESPACES ORDER BY NbMethods ASC
Recommendations:
TYPES WHERE NbMethods > 20 might be hard to understand and
maintain but there might be cases where it is relevant to have a high value for
NbMethods. For example, the System.Windows.Forms.DataGridView
framework class has more than 1000 methods.
There is no
particular recommendation for NbMethods values
on ASSEMBLIES and NAMESPACES.
Applicable on: ASSEMBLIES NAMESPACES TYPES FIELDS
Remarks: The value of the NbFields
metric is equal to 1 for all fields, no matter it is static or not, it is an
enumeration value, it is literal ….
Example:
SELECT TYPES WHERE NbFields < 10
You can also use the NbFields keyword to order the rows in the result list.
SELECT TOP 10 NAMESPACES ORDER BY NbFields ASC
Recommendations:
TYPES WHERE NbFields > 20 AND !IsEnumeration might be hard to understand and maintain but there
might be cases where it is relevant to have a high value for NbFields. For example, the System.Windows.Forms.Control framework class has more than 200
fields. The value of the metric SizeOfInst might
be a better indicator to pinpoint complex types.
There is no
particular recommendation for NbFields values on
ASSEMBLIES and NAMESPACES.
Applicable on: ASSEMBLIES NAMESPACES TYPES
Remarks: The value of the NbTypes
metric is equal to 1 for all types, no matter it is a class, an interface, a
structure, a delegate, a nested type….
Examples:
SELECT ASSEMBLIES WHERE NbTypes > 10
You can also use the NbTypes keyword to order the rows in the result list.
SELECT TOP 10 NAMESPACES ORDER BY NbTypes ASC
Recommendations:
There is no
particular recommendation for NbTypes values on ASSEMBLIES and NAMESPACES.
Applicable on: ASSEMBLIES NAMESPACES
Remarks: The value of the NbNamespaces
metric is equal to 1 for all namespaces.
Example:
SELECT ASSEMBLIES WHERE NbNamespaces == 10