OSCADSharpDotnet7/OSCADSharp/OSCADSharp.ndproj
2016-03-20 20:38:27 -07:00

8101 lines
352 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<NDepend AppName="OSCADSharp" Platform="DotNet">
<OutputDir KeepXmlFiles="False">C:\src\OSCADSharp\OSCADSharp\NDependOut</OutputDir>
<Assemblies>
<Name>OSCADSharp</Name>
</Assemblies>
<FrameworkAssemblies>
<Name>mscorlib</Name>
<Name>System.Core</Name>
<Name>System</Name>
</FrameworkAssemblies>
<Dirs>
<Dir>C:\Windows\Microsoft.NET\Framework\v4.0.30319</Dir>
<Dir>C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF</Dir>
<Dir>C:\src\OSCADSharp\OSCADSharp\OSCADSharp\bin\Debug</Dir>
<Dir>C:\src\OSCADSharp\OSCADSharp\OSCADSharp.UnitTests\bin\Debug</Dir>
</Dirs>
<MergeCodeGeneratedByCompiler>True</MergeCodeGeneratedByCompiler>
<Report Kind="0" SectionsEnabled="45055" XslPath="" Flags="261120" />
<BuildComparisonSetting ProjectMode="DontCompare" BuildMode="MostRecentAnalysisResultAvailable" ProjectFileToCompareWith="" BuildFileToCompareWith="" NDaysAgo="1" FocusOnRecentRulesViolations="False" />
<BaselineInUISetting ProjectMode="DontCompare" BuildMode="MostRecentAnalysisResultAvailable" ProjectFileToCompareWith="" BuildFileToCompareWith="" NDaysAgo="1" FocusOnRecentRulesViolations="False" />
<CoverageFiles CoverageDir="" UncoverableAttribute="" />
<TrendMetrics UseCustomLog="False" LogRecurrence="3" LogLabel="2" UseCustomDir="False" CustomDir="">
<Chart Name="Lines of Code" ShowInReport="True">
<Serie MetricName="# Lines of Code" MetricUnit="Loc" Color="#FF00BFFF" ChartType="Line" ScaleExp="0" />
<Serie MetricName="# Lines of Code Covered" MetricUnit="Loc" Color="#FF32CD32" ChartType="Area" ScaleExp="0" />
<Serie MetricName="# Lines of Code (NotMyCode)" MetricUnit="Loc" Color="#FFA9A9A9" ChartType="Area" ScaleExp="0" />
<Serie MetricName="# Lines of Comments" MetricUnit="Lines" Color="#FF008000" ChartType="Line" ScaleExp="0" />
</Chart>
<Chart Name="Rules Violated" ShowInReport="True">
<Serie MetricName="# Rules" MetricUnit="Rules" Color="#FF66CDAA" ChartType="Line" ScaleExp="0" />
<Serie MetricName="# Rules Violated" MetricUnit="Rules" Color="#FFFF8C00" ChartType="Area" ScaleExp="0" />
<Serie MetricName="# Critical Rules Violated" MetricUnit="Rules" Color="#FFFF0000" ChartType="Area" ScaleExp="0" />
</Chart>
<Chart Name="Rules Violations" ShowInReport="True">
<Serie MetricName="# Rules Violations" MetricUnit="Violations" Color="#FFFF8C00" ChartType="Area" ScaleExp="0" />
<Serie MetricName="# Critical Rules Violations" MetricUnit="Violations" Color="#FFFF0000" ChartType="Area" ScaleExp="0" />
</Chart>
<Chart Name="Percentage Coverage by Tests" ShowInReport="True">
<Serie MetricName="Percentage Code Coverage" MetricUnit="%" Color="#FF32CD32" ChartType="Area" ScaleExp="0" />
</Chart>
<Chart Name="Max" ShowInReport="True">
<Serie MetricName="Max IL Cyclomatic Complexity for Methods" MetricUnit="Paths" Color="#FFFF0000" ChartType="Line" ScaleExp="0" />
<Serie MetricName="Max # Lines of Code for Methods (JustMyCode)" MetricUnit="LoC" Color="#FF0000FF" ChartType="Line" ScaleExp="0" />
<Serie MetricName="Max # of Methods for Types" MetricUnit="Methods" Color="#FF32CD32" ChartType="Line" ScaleExp="0" />
<Serie MetricName="Max IL Nesting Depth for Methods" MetricUnit="Scopes" Color="#FFFFD700" ChartType="Line" ScaleExp="0" />
</Chart>
<Chart Name="Average" ShowInReport="True">
<Serie MetricName="Average IL Cyclomatic Complexity for Methods" MetricUnit="Paths" Color="#FFFF0000" ChartType="Line" ScaleExp="0" />
<Serie MetricName="Average # Lines of Code for Methods" MetricUnit="LoC" Color="#FF0000FF" ChartType="Line" ScaleExp="0" />
<Serie MetricName="Average # Methods for Types" MetricUnit="Methods" Color="#FF32CD32" ChartType="Line" ScaleExp="0" />
<Serie MetricName="Average IL Nesting Depth for Methods" MetricUnit="Scopes" Color="#FFFFD700" ChartType="Line" ScaleExp="0" />
</Chart>
<Chart Name="Third-Party Usage" ShowInReport="True">
<Serie MetricName="# Third-Party Types Used" MetricUnit="Types" Color="#FF0000FF" ChartType="Line" ScaleExp="0" />
<Serie MetricName="# Third-Party Methods Used" MetricUnit="Methods" Color="#FFFF0000" ChartType="Line" ScaleExp="0" />
<Serie MetricName="# Third-Party Assemblies Used" MetricUnit="Assemblies" Color="#FF646464" ChartType="Line" ScaleExp="1" />
<Serie MetricName="# Third-Party Namespaces Used" MetricUnit="Namespaces" Color="#FF32CD32" ChartType="Line" ScaleExp="1" />
<Serie MetricName="# Third-Party Fields Used" MetricUnit="Fields" Color="#FFFFD700" ChartType="Line" ScaleExp="1" />
</Chart>
</TrendMetrics>
<HistoricAnalysisResult PersistRecurrence="2" UseCustomDir="False" CustomDir="" />
<SourceFileRebasing FromPath="" ToPath="" />
<PathVariables />
<RuleFiles />
<ProjectRules AreActive="True" />
<Queries>
<Group Name="Code Quality" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Types too big - critical</Name>
warnif count > 0 from t in JustMyCode.Types where
t.NbLinesOfCode > 500
// We've commented # IL Instructions, because with LINQ syntax, a few lines of code can compile to hundreds of IL instructions.
// || t.NbILInstructions > 3000
orderby t.NbLinesOfCode descending
select new { t, t.NbLinesOfCode, t.NbILInstructions,
t.Methods, t.Fields }
//<Description>
// This rule matches types with more than 500 lines of code.
//
// Types where *NbLinesOfCode > 500* are extremely complex
// to develop and maintain.
// See the definition of the NbLinesOfCode metric here
// http://www.ndepend.com/docs/code-metrics#NbLinesOfCode
//
// Maybe you are facing the **God Class** phenomenon:
// A **God Class** is a class that controls way too many other classes
// in the system and has grown beyond all logic to become
// *The Class That Does Everything*.
//
// In average, a line of code is compiled to around
// 6 IL instructions. This is why the code metric
// *NbILInstructions* is used here, in case the
// code metric *NbLinesOfCode* is un-available because
// of missing assemblies corresponding PDB files.
// See the definition of the *NbILInstructions* metric here
// http://www.ndepend.com/docs/code-metrics#NbILInstructions
//</Description>
//<HowToFix>
// Types with many lines of code
// should be split in a group of smaller types.
//
// To refactor a *God Class* you'll need patience,
// and you might even need to recreate everything from scratch.
// Here are a few advices:
//
// • Think before pulling out methods: on what data does this method operate?
// What responsibility does it have?
//
// • Try to maintain the interface of the god class at first
// and delegate calls to the new extracted classes.
// In the end the god class should be a pure facade without own logic.
// Then you can keep it for convenience
// or throw it away and start to use the new classes only.
//
// • Unit Tests can help: write tests for each method before extracting it
// to ensure you don't break functionality.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Methods too complex - critical</Name>
warnif count > 0 from m in JustMyCode.Methods where
m.CyclomaticComplexity > 30 ||
m.ILCyclomaticComplexity > 60 ||
m.ILNestingDepth > 6
orderby m.CyclomaticComplexity descending,
m.ILCyclomaticComplexity descending,
m.ILNestingDepth descending
select new { m, m.CyclomaticComplexity,
m.ILCyclomaticComplexity,
m.ILNestingDepth }
//<Description>
// This rule matches methods where *CyclomaticComplexity* > 30
// or *ILCyclomaticComplexity* > 60
// or *ILNestingDepth* > 6.
// Such method is typically hard to understand and maintain.
//
// Maybe you are facing the **God Method** phenomenon.
// A "God Method" is a method that does way too many processes in the system
// and has grown beyond all logic to become *The Method That Does Everything*.
// When need for new processes increases suddenly some programmers realize:
// why should I create a new method for each processe if I can only add an *if*.
//
// See the definition of the *CyclomaticComplexity* metric here:
// http://www.ndepend.com/docs/code-metrics#CC
//
// See the definition of the *ILCyclomaticComplexity* metric here:
// http://www.ndepend.com/docs/code-metrics#ILCC
//
// See the definition of the *ILNestingDepth* metric here:
// http://www.ndepend.com/docs/code-metrics#ILNestingDepth
//</Description>
//<HowToFix>
// A large and complex method should be split in smaller methods,
// or even one or several classes can be created for that.
//
// During this process it is important to question the scope of each
// variable local to the method. This can be an indication if
// such local variable will become an instance field of the newly created class(es).
//
// Large *switch…case* structures might be refactored through the help
// of a set of types that implement a common interface, the interface polymorphism
// playing the role of the *switch cases tests*.
//
// Unit Tests can help: write tests for each method before extracting it
// to ensure you don't break functionality.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Methods with too many parameters - critical</Name>
warnif count > 0 from m in JustMyCode.Methods where
m.NbParameters > 8
orderby m.NbParameters descending
select new { m, m.NbParameters }
//<Description>
// This rule matches methods with more than 8 parameters.
// Such method is painful to call and might degrade performance.
// See the definition of the *NbParameters* metric here:
// http://www.ndepend.com/docs/code-metrics#NbParameters
//</Description>
//<HowToFix>
// More properties/fields can be added to the declaring type to
// handle numerous states. An alternative is to provide
// a class or a structure dedicated to handle arguments passing.
// For example see the class *System.Diagnostics.ProcessStartInfo*
// and the method *System.Diagnostics.Process.Start(ProcessStartInfo)*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Quick summary of methods to refactor</Name>
warnif count > 0 from m in JustMyCode.Methods where
// Code Metrics' definitions
m.NbLinesOfCode > 30 || // http://www.ndepend.com/docs/code-metrics#NbLinesOfCode
// We've commented # IL Instructions, because with LINQ syntax, a few lines of code can compile to hundreds of IL instructions.
// m.NbILInstructions > 200 || // http://www.ndepend.com/docs/code-metrics#NbILInstructions
m.CyclomaticComplexity > 20 || // http://www.ndepend.com/docs/code-metrics#CC
m.ILCyclomaticComplexity > 50 || // http://www.ndepend.com/docs/code-metrics#ILCC
m.ILNestingDepth > 5 || // http://www.ndepend.com/docs/code-metrics#ILNestingDepth
m.NbParameters > 5 || // http://www.ndepend.com/docs/code-metrics#NbParameters
m.NbVariables > 8 || // http://www.ndepend.com/docs/code-metrics#NbVariables
m.NbOverloads > 6 // http://www.ndepend.com/docs/code-metrics#NbOverloads
select new { m, m.NbLinesOfCode, m.NbILInstructions, m.CyclomaticComplexity,
m.ILCyclomaticComplexity, m.ILNestingDepth,
m.NbParameters, m.NbVariables, m.NbOverloads }
//<Description>
// Methods matched by this rule somehow violate
// one or several basic quality principles,
// whether it is too large (too many *lines of code*),
// too complex (too many *if*, *switch case*, loops…)
// has too many variables, too many parameters
// or has too many overloads.
//</Description>
//<HowToFix>
// To refactor such method and increase code quality and maintainability,
// certainly you'll have to split the method into several smaller methods
// or even create one or several classes to implement the logic.
//
// During this process it is important to question the scope of each
// variable local to the method. This can be an indication if
// such local variable will become an instance field of the newly created class(es).
//
// Large *switch…case* structures might be refactored through the help
// of a set of types that implement a common interface, the interface polymorphism
// playing the role of the *switch cases tests*.
//
// Unit Tests can help: write tests for each method before extracting it
// to ensure you don't break functionality.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods too big</Name>
warnif count > 0 from m in JustMyCode.Methods where
m.NbLinesOfCode > 30
// We've commented # IL Instructions, because with LINQ syntax, a few lines of code can compile to hundreds of IL instructions.
// || m.NbILInstructions > 200
orderby m.NbLinesOfCode descending,
m.NbILInstructions descending
select new { m, m.NbLinesOfCode, m.NbILInstructions }
//<Description>
// This rule matches methods where *NbLinesOfCode > 30* or
// (commented per default) *NbILInstructions > 200*.
// Such method can be hard to understand and maintain.
//
// However rules like *Methods too complex* or *Methods with too many variables*
// might be more relevant to detect *painful to maintain* methods,
// because complexity is more related to numbers of *if*,
// *switch case*, loops… than to just number of lines.
//
// See the definition of the *NbLinesOfCode* metric here
// http://www.ndepend.com/docs/code-metrics#NbLinesOfCode
//</Description>
//<HowToFix>
// Usually too big methods should be split in smaller methods.
//
// But long methods with no branch conditions, that typically initialize some data,
// are not necessarily a problem to maintain nor to test, and might not need
// refactoring.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods too complex</Name>
warnif count > 0 from m in JustMyCode.Methods where
m.CyclomaticComplexity > 20 ||
m.ILCyclomaticComplexity > 40 ||
m.ILNestingDepth > 4
orderby m.CyclomaticComplexity descending,
m.ILCyclomaticComplexity descending,
m.ILNestingDepth descending
select new { m, m.CyclomaticComplexity,
m.ILCyclomaticComplexity,
m.ILNestingDepth }
//<Description>
// This rule matches methods where *CyclomaticComplexity > 20*
// or *ILCyclomaticComplexity > 40*
// or *ILNestingDepth > 4*.
// Such method is typically hard to understand and maintain.
//
// See the definition of the *CyclomaticComplexity* metric here:
// http://www.ndepend.com/docs/code-metrics#CC
//
// See the definition of the *ILCyclomaticComplexity* metric here:
// http://www.ndepend.com/docs/code-metrics#ILCC
//
// See the definition of the *ILNestingDepth* metric here:
// http://www.ndepend.com/docs/code-metrics#ILNestingDepth
//</Description>
//<HowToFix>
// A large and complex method should be split in smaller methods,
// or even one or several classes can be created for that.
//
// During this process it is important to question the scope of each
// variable local to the method. This can be an indication if
// such local variable will become an instance field of the newly created class(es).
//
// Large *switch…case* structures might be refactored through the help
// of a set of types that implement a common interface, the interface polymorphism
// playing the role of the *switch cases tests*.
//
// Unit Tests can help: write tests for each method before extracting it
// to ensure you don't break functionality.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods potentially poorly commented</Name>
warnif count > 0 from m in JustMyCode.Methods where
m.PercentageComment < 20 &&
m.NbLinesOfCode > 20
orderby m.PercentageComment ascending
select new { m, m.PercentageComment, m.NbLinesOfCode, m.NbLinesOfComment }
//<Description>
// This rule matches methods with less than 20% of comment lines and that have
// at least 20 lines of code. Such method might need to be more commented.
//
// See the definitions of the *Comments metric* here:
// http://www.ndepend.com/docs/code-metrics#PercentageComment
// http://www.ndepend.com/docs/code-metrics#NbLinesOfComment
//</Description>
//<HowToFix>
// Typically add more comment. But code commenting is subject to controversy.
// While poorly written and designed code would needs a lot of comment
// to be understood, clean code doesn't need that much comment, especially
// if variables and methods are properly named and convey enough information.
// Unit-Test code can also play the role of code commenting.
//
// However, even when writing clean and well-tested code, one will have
// to write **hacks** at a point, usually to circumvent some API limitations or bugs.
// A hack is a non-trivial piece of code, that doesn't make sense at first glance,
// and that took time and web research to be found.
// In such situation comments must absolutely be used to express the intention,
// the need for the hacks and the source where the solution has been found.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods with too many parameters</Name>
warnif count > 0 from m in JustMyCode.Methods where
m.NbParameters > 5
orderby m.NbParameters descending
select new { m, m.NbParameters }
//<Description>
// This rule matches methods with more than 5 parameters.
// Such method might be painful to call and might degrade performance.
// See the definition of the *NbParameters* metric here:
// http://www.ndepend.com/docs/code-metrics#NbParameters
//</Description>
//<HowToFix>
// More properties/fields can be added to the declaring type to
// handle numerous states. An alternative is to provide
// a class or a structure dedicated to handle arguments passing.
// For example see the class *System.Diagnostics.ProcessStartInfo*
// and the method *System.Diagnostics.Process.Start(ProcessStartInfo))*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods with too many local variables</Name>
warnif count > 0 from m in JustMyCode.Methods where
m.NbVariables > 15
orderby m.NbVariables descending
select new { m, m.NbVariables }
//<Description>
// This rule matches methods with more than 15 variables.
//
// Methods where *NbVariables > 8* are hard to understand and maintain.
// Methods where *NbVariables > 15* are extremely complex and must be refactored.
//
// See the definition of the *Nbvariables* metric here:
// http://www.ndepend.com/docs/code-metrics#Nbvariables
//</Description>
//<HowToFix>
// To refactor such method and increase code quality and maintainability,
// certainly you'll have to split the method into several smaller methods
// or even create one or several classes to implement the logic.
//
// During this process it is important to question the scope of each
// variable local to the method. This can be an indication if
// such local variable will become an instance field of the newly created class(es).
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods with too many overloads</Name>
warnif count > 0 from m in JustMyCode.Methods where
m.NbOverloads > 6 &&
!m.IsOperator // Don't report operator overload
orderby m.NbOverloads descending
let overloads =
m.IsConstructor ? m.ParentType.Constructors :
m.ParentType.Methods.Where(m1 => m1.SimpleName == m.SimpleName)
select new { m, overloads }
//<Description>
// Method overloading is the ability to create multiple methods of the same name
// with different implementations, and various set of parameters.
//
// This rule matches sets of method with more than 6 overloads.
//
// Such method set might be a problem to maintain
// and provokes higher coupling than necessary.
//
// See the definition of the *NbOverloads* metric here
// http://www.ndepend.com/docs/code-metrics#NbOverloads
//</Description>
//<HowToFix>
// Typically the *too many overloads* phenomenon appears when an algorithm
// takes a various set of in-parameters. Each overload is presented as
// a facility to provide a various set of in-parameters.
// In such situation, the C# and VB.NET language feature named
// *Named and Optional arguments* should be used.
//
// The *too many overloads* phenomenon can also be a consequence of the usage
// of the **visitor design pattern** http://en.wikipedia.org/wiki/Visitor_pattern
// since a method named *Visit()* must be provided for each sub type.
// In such situation there is no need for fix.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types with too many methods</Name>
warnif count > 0 from t in JustMyCode.Types
// Optimization: Fast discard of non-relevant types
where t.Methods.Count() > 20
// Don't match these methods
let methods = t.Methods.Where(
m => !(m.IsGeneratedByCompiler ||
m.IsConstructor || m.IsClassConstructor ||
m.IsPropertyGetter || m.IsPropertySetter ||
m.IsEventAdder || m.IsEventRemover))
where methods.Count() > 20
orderby methods.Count() descending
select new { t,
nbMethods = methods.Count(),
instanceMethods = methods.Where(m => !m.IsStatic),
staticMethods = methods.Where(m => m.IsStatic)}
//<Description>
// This rule matches types with more than 20 methods.
// Such type might be hard to understand and maintain.
//
// Notice that methods like constructors or property
// and event accessors are not taken account.
//
// Having many methods for a type might be a symptom
// of too many responsibilities implemented.
//
// Maybe you are facing the **God Class** phenomenon:
// A **God Class** is a class that controls way too many other classes
// in the system and has grown beyond all logic to become
// *The Class That Does Everything*.
//</Description>
//<HowToFix>
// To refactor such type and increase code quality and maintainability,
// certainly you'll have to split the type into several smaller types
// that together, implement the same logic.
//
// To refactor a *God Class* you'll need patience,
// and you might even need to recreate everything from scratch.
// Here are a few advices:
//
// • Think before pulling out methods:
// What responsibility does it have?
// Can you isolate some subsets of methods that operate on the same subsets of fields?
//
// • Try to maintain the interface of the god class at first
// and delegate calls to the new extracted classes.
// In the end the god class should be a pure facade without own logic.
// Then you can keep it for convenience
// or throw it away and start to use the new classes only.
//
// • Unit Tests can help: write tests for each method before extracting it
// to ensure you don't break functionality.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types with too many fields</Name>
warnif count > 0 from t in JustMyCode.Types
// Optimization: Fast discard of non-relevant types
where !t.IsEnumeration &&
t.Fields.Count() > 20
// Count instance fields and non-constant static fields
let fields = t.Fields.Where(f =>
!f.IsGeneratedByCompiler &&
!f.IsLiteral &&
!(f.IsStatic && f.IsInitOnly) &&
JustMyCode.Contains(f) )
where fields.Count() > 20
orderby fields.Count() descending
select new { t,
instanceFields = fields.Where(f => !f.IsStatic),
staticFields = fields.Where(f => f.IsStatic),
// See definition of Size of Instances metric here:
// http://www.ndepend.com/docs/code-metrics#SizeOfInst
t.SizeOfInst
}
//<Description>
// This rule matches types with more than 20 fields.
// Such type might be hard to understand and maintain.
//
// Notice that constant fields and static-readonly fields are not counted.
// Enumerations types are not counted also.
//
// Having many fields for a type might be a symptom
// of too many responsibilities implemented.
//</Description>
//<HowToFix>
// To refactor such type and increase code quality and maintainability,
// certainly you'll have to group subsets of fields into smaller types
// and dispatch the logic implemented into the methods
// into these smaller types.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types with poor cohesion</Name>
warnif count > 0 from t in JustMyCode.Types where
(t.LCOM > 0.8 || t.LCOMHS > 0.95) &&
t.NbFields > 10 &&
t.NbMethods >10
orderby t.LCOM descending, t.LCOMHS descending
select new { t, t.LCOM, t.LCOMHS,
t.NbMethods, t.NbFields }
//<Description>
// This rule is based on the *LCOM code metric*,
// LCOM stands for **Lack Of Cohesion of Methods**.
// See the definition of the LCOM metric here
// http://www.ndepend.com/docs/code-metrics#LCOM
//
// The LCOM metric measures the fact that most methods are using most fields.
// A class is considered utterly cohesive (which is good)
// if all its methods use all its instance fields.
//
// Only types with enough methods and fields are taken account to avoid bias.
// The LCOM takes its values in the range [0-1].
//
// This rule matches types with LCOM higher than 0.8.
// Such value generally pinpoints a **poorly cohesive class**.
//
// There are several LCOM metrics.
// The LCOM HS (HS stands for Henderson-Sellers) takes its values in the range [0-2].
// A LCOM HS value higher than 1 should be considered alarming.
//</Description>
//<HowToFix>
// To refactor a poorly cohesive type and increase code quality and maintainability,
// certainly you'll have to split the type into several smaller and more cohesive types
// that together, implement the same logic.
//</HowToFix>]]></Query>
</Group>
<Group Name="Code Quality Regression" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>From now, all methods added or refactored should respect basic quality principles</Name>
warnif count > 0 from m in JustMyCode.Methods where
// *** Only match new or refactored methods since Baseline for Comparison ***
(m.WasAdded() || m.CodeWasChanged()) &&
// Low Quality methods// Metrics' definitions
( m.NbLinesOfCode > 30 || // http://www.ndepend.com/docs/code-metrics#NbLinesOfCode
m.NbILInstructions > 200 || // http://www.ndepend.com/docs/code-metrics#NbILInstructions
m.CyclomaticComplexity > 20 || // http://www.ndepend.com/docs/code-metrics#CC
m.ILCyclomaticComplexity > 50 || // http://www.ndepend.com/docs/code-metrics#ILCC
m.ILNestingDepth > 4 || // http://www.ndepend.com/docs/code-metrics#ILNestingDepth
m.NbParameters > 5 || // http://www.ndepend.com/docs/code-metrics#NbParameters
m.NbVariables > 8 || // http://www.ndepend.com/docs/code-metrics#NbVariables
m.NbOverloads > 6 )
select new { m, m.NbLinesOfCode, m.NbILInstructions, m.CyclomaticComplexity,
m.ILCyclomaticComplexity, m.ILNestingDepth,
m.NbParameters, m.NbVariables, m.NbOverloads } // http://www.ndepend.com/docs/code-metrics#NbOverloads
//<Description>
// This rule is executed only if a *baseline for comparison* is defined (*diff mode*).
// This rule operates only on methods added or refactored since the baseline.
// The same effect can be obtained for any rules through:
// *Dashboard > Filter Recent Violations Only*.
// Doing so let's focus on code quality on recent changes only.
//
// Methods matched by this rule not only have been recently added or refactored,
// but also somehow violate one or several basic quality principles,
// whether it is too large (too many *lines of code*),
// too complex (too many *if*, *switch case*, loops…)
// has too many variables, too many parameters
// or has too many overloads.
//</Description>
//<HowToFix>
// To refactor such method and increase code quality and maintainability,
// certainly you'll have to split the method into several smaller methods
// or even create one or several classes to implement the logic.
//
// During this process it is important to question the scope of each
// variable local to the method. This can be an indication if
// such local variable will become an instance field of the newly created class(es).
//
// Large *switch…case* structures might be refactored through the help
// of a set of types that implement a common interface, the interface polymorphism
// playing the role of the *switch cases tests*.
//
// Unit Tests can help: write tests for each method before extracting it
// to ensure you don't break functionality.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>From now, all types added or refactored should respect basic quality principles</Name>
warnif count > 0 from t in JustMyCode.Types where
// *** Only match new or refactored types since Baseline for Comparison ***
(t.WasAdded() || t.CodeWasChanged()) &&
// Eliminate interfaces, enumerations or types only with constant fields
// by making sure we are matching type with code.
t.NbLinesOfCode > 10 &&
// Optimization: Fast discard of non-relevant types
(t.Fields.Count() > 20 || t.Methods.Count() > 20)
// Count instance fields and non-constant static fields
let fields = t.Fields.Where(f =>
!f.IsLiteral &&
!(f.IsStatic && f.IsInitOnly))
// Don't match these methods
let methods = t.Methods.Where(
m => !(m.IsConstructor || m.IsClassConstructor ||
m.IsGeneratedByCompiler ||
m.IsPropertyGetter || m.IsPropertySetter ||
m.IsEventAdder || m.IsEventRemover))
where
// Low Quality types Metrics' definitions are available here:
// http://www.ndepend.com/docs/code-metrics#MetricsOnTypes
( // Types with too many methods
fields.Count() > 20 ||
methods.Count() > 20 ||
// Complex Types that use more than 50 other types
t.NbTypesUsed > 50
)
select new { t, t.NbLinesOfCode,
instanceMethods = methods.Where(m => !m.IsStatic),
staticMethods = methods.Where(m => m.IsStatic),
instanceFields = fields.Where(f => !f.IsStatic),
staticFields = fields.Where(f => f.IsStatic),
t.TypesUsed }
//<Description>
// This rule is executed only if a *baseline for comparison* is defined (*diff mode*).
// This rule operates only on types added or refactored since the baseline.
// The same effect can be obtained for any rules through:
// *Dashboard > Filter Recent Violations Only*.
// Doing so let's focus on code quality on recent changes only.
//
// Types matched by this rule not only have been recently added or refactored,
// but also somehow violate one or several basic quality principles,
// whether it has too many methods,
// it has too many fields,
// or is using too many types.
// Any of these criterions is often a symptom of a type with too many responsibilities.
//
// Notice that to count methods and fields, methods like constructors
// or property and event accessors are not taken account.
// Notice that constants fields and static-readonly fields are not counted.
// Enumerations types are not counted also.
//</Description>
//<HowToFix>
// To refactor such type and increase code quality and maintainability,
// certainly you'll have to split the type into several smaller types
// that together, implement the same logic.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>From now, all types added or refactored should be 100% covered by tests</Name>
warnif count > 0 from t in JustMyCode.Types where
// *** Only match new or refactored types since Baseline for Comparison ***
(t.WasAdded() || t.CodeWasChanged()) &&
// …that are not 100% covered by tests
t.PercentageCoverage < 100
let methodsCulprit = t.Methods.Where(m => m.PercentageCoverage < 100)
select new { t, t.PercentageCoverage, methodsCulprit }
//<Description>
// This rule is executed only if a *baseline for comparison* is defined (*diff mode*).
// This rule operates only on types added or refactored since the baseline.
// The same effect can be obtained for any rules through:
// *Dashboard > Filter Recent Violations Only*.
// Doing so let's focus on code quality on recent changes only.
//
// This rule is executed only if some code coverage data is imported
// from some code coverage files.
//
// Often covering 10% of remaining uncovered code of a class,
// requires as much work as covering the first 90%.
// For this reason, typically teams estimate that 90% coverage is enough.
// However *untestable code* usually means *poorly written code*
// which usually leads to *error prone code*.
// So it might be worth refactoring and making sure to cover the 10% remaining code
// because **most tricky bugs might come from this small portion of hard-to-test code**.
//
// Not all classes should be 100% covered by tests (like UI code can be hard to test)
// but you should make sure that most of the logic of your application
// is defined in some *easy-to-test classes*, 100% covered by tests.
//
// In this context, this rule warns when a type added or refactored since the baseline,
// is not fully covered by tests.
//</Description>
//<HowToFix>
// Write more unit-tests dedicated to cover code not covered yet.
// If you find some *hard-to-test code*, it is certainly a sign that this code
// is not *well designed* and hence, needs refactoring.
//
// You'll find code impossible to cover by unit-tests, like calls to *MessageBox.Show()*.
// An infrastructure must be defined to be able to *mock* such code at test-time.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid decreasing code coverage by tests of types</Name>
warnif count > 0
from t in JustMyCode.Types where
t.IsPresentInBothBuilds() &&
t.PercentageCoverage < t.OlderVersion().PercentageCoverage
select new { t,
OldCov = t.OlderVersion().PercentageCoverage,
NewCov = t.PercentageCoverage,
OldLoc = t.OlderVersion().NbLinesOfCode,
NewLoc = t.NbLinesOfCode,
}
//<Description>
// This rule is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This rule is executed only if some code coverage data is imported
// from some code coverage files.
//
// This rule warns when a type code coverage ratio
// decreased since the baseline.
// This can mean that some tests have been removed
// but more often, this means that the type has been modified,
// and that changes haven't been covered by tests.
//
// To visualize changes in code, right-click a matched type and select:
//
// • Compare older and newer versions of source file
//
// • or Compare older and newer versions disassembled with Reflector
//</Description>
//<HowToFix>
// Write more unit-tests dedicated to cover changes in matched types
// not covered yet.
// If you find some *hard-to-test code*, it is certainly a sign that this code
// is not *well designed* and hence, needs refactoring.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types that used to be 100% covered but not anymore</Name>
warnif count > 0
from t in JustMyCode.Types where
t.IsPresentInBothBuilds() &&
t.OlderVersion().PercentageCoverage == 100 &&
t.PercentageCoverage < 100
let culpritMethods = t.Methods.Where(m => m.PercentageCoverage < 100)
select new {t, t.PercentageCoverage, culpritMethods }
//<Description>
// This rule is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This rule is executed only if some code coverage data is imported
// from some code coverage files.
//
// Often covering 10% of remaining uncovered code of a class,
// requires as much work as covering the first 90%.
// For this reason, typically teams estimate that 90% coverage is enough.
// However *untestable code* usually means *poorly written code*
// which usually leads to *error prone code*.
// So it might be worth refactoring and making sure to cover the 10% remaining code
// **because most tricky bugs might come from this small portion of hard-to-test code**.
//
// Not all classes should be 100% covered by tests (like UI code can be hard to test)
// but you should make sure that most of the logic of your application
// is defined in some *easy-to-test classes*, 100% covered by tests.
//
// In this context, this rule warns when a type fully covered by tests is now only partially covered.
//</Description>
//<HowToFix>
// Write more unit-tests dedicated to cover code not covered anymore.
// If you find some *hard-to-test code*, it is certainly a sign that this code
// is not *well designed* and hence, needs refactoring.
//
// You'll find code impossible to cover by unit-tests, like calls to *MessageBox.Show()*.
// An infrastructure must be defined to be able to *mock* such code at test-time.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid making complex methods even more complex (Source CC)</Name>
warnif count > 0
from m in JustMyCode.Methods where
!m.IsAbstract &&
m.IsPresentInBothBuilds() &&
m.CodeWasChanged()
let oldCC = m.OlderVersion().CyclomaticComplexity
where oldCC > 6 && m.CyclomaticComplexity > oldCC
select new { m,
oldCC ,
newCC = m.CyclomaticComplexity ,
oldLoc = m.OlderVersion().NbLinesOfCode,
newLoc = m.NbLinesOfCode,
}
//<Description>
// This rule is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// The method complexity is measured through the code metric
// *Cyclomatic Complexity* defined here:
// http://www.ndepend.com/docs/code-metrics#CC
//
// This rule warns when a method already complex
// (i.e with *Cyclomatic Complexity* higher than 6)
// become even more complex since the baseline.
//
// This rule needs assemblies PDB files and source code
// to be available at analysis time, because the *Cyclomatic Complexity*
// is inferred from the source code and source code location
// is inferred from PDB files. See:
// http://www.ndepend.com/docs/ndepend-analysis-inputs-explanation
//
// To visualize changes in code, right-click a matched method and select:
//
// • Compare older and newer versions of source file
//
// • or Compare older and newer versions disassembled with Reflector
//</Description>
//<HowToFix>
// A large and complex method should be split in smaller methods,
// or even one or several classes can be created for that.
//
// During this process it is important to question the scope of each
// variable local to the method. This can be an indication if
// such local variable will become an instance field of the newly created class(es).
//
// Large *switch…case* structures might be refactored through the help
// of a set of types that implement a common interface, the interface polymorphism
// playing the role of the *switch cases tests*.
//
// Unit Tests can help: write tests for each method before extracting it
// to ensure you don't break functionality.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid making complex methods even more complex (IL CC)</Name>
warnif count > 0
from m in JustMyCode.Methods where
!m.IsAbstract &&
m.IsPresentInBothBuilds() &&
m.CodeWasChanged()
let oldCC = m.OlderVersion().ILCyclomaticComplexity
where oldCC > 10 && m.ILCyclomaticComplexity > oldCC
select new { m,
oldCC ,
newCC = m.ILCyclomaticComplexity ,
oldLoc = m.OlderVersion().NbLinesOfCode,
newLoc = m.NbLinesOfCode,
}
//<Description>
// This rule is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// The method complexity is measured through the code metric
// *IL Cyclomatic Complexity* defined here:
// http://www.ndepend.com/docs/code-metrics#ILCC
//
// This rule warns when a method already complex
// (i.e with *IL Cyclomatic Complexity* higher than 10)
// become even more complex since the baseline.
//
// If assemblies PDB files and source code
// are available at analysis time,
// the *Cyclomatic Complexity* can be inferred from source code,
// and this is more precise than inferring it from IL code.
// Hence, prefer use the rule
// *Avoid making complex methods even more complex (Source CC)*
// that offers more precise result.
//
// To visualize changes in code, right-click a matched method and select:
//
// • Compare older and newer versions of source file
//
// • or Compare older and newer versions disassembled with Reflector
//</Description>
//<HowToFix>
// A large and complex method should be split in smaller methods,
// or even one or several classes can be created for that.
//
// During this process it is important to question the scope of each
// variable local to the method. This can be an indication if
// such local variable will become an instance field of the newly created class(es).
//
// Large *switch…case* structures might be refactored through the help
// of a set of types that implement a common interface, the interface polymorphism
// playing the role of the *switch cases tests*.
//
// Unit Tests can help: write tests for each method before extracting it
// to ensure you don't break functionality.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid making large methods even larger</Name>
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,
}
//<Description>
// This rule is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This rule warns when a method already large
// (i.e with more than 15 lines of code)
// become even larger since the baseline.
//
// The method size is measured through the code metric
// *# Lines of Code* defined here:
// http://www.ndepend.com/docs/code-metrics#NbLinesOfCode
//
// This rule needs assemblies PDB files
// to be available at analysis time, because the *# Lines of Code*
// is inferred from PDB files. See:
// http://www.ndepend.com/docs/ndepend-analysis-inputs-explanation
//
// To visualize changes in code, right-click a matched method and select:
//
// • Compare older and newer versions of source file
//
// • or Compare older and newer versions disassembled with Reflector
//</Description>
//<HowToFix>
// Usually too big methods should be split in smaller methods.
//
// But long methods with no branch conditions, that typically initialize some data,
// are not necessarily a problem to maintain, and might not need refactoring.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid adding methods to a type that already had many methods</Name>
warnif count > 0
// Don't count constructors and methods generated by the compiler!
let getMethodsProc = new Func<IType, IList<IMethod>>(
t => t.Methods.Where(m =>
!m.IsConstructor && !m.IsClassConstructor &&
!m.IsGeneratedByCompiler).ToArray())
from t in JustMyCode.Types where
t.IsPresentInBothBuilds()
// Optimization: fast discard of non-relevant types
where t.OlderVersion().NbMethods > 15
let oldMethods = getMethodsProc(t.OlderVersion())
where oldMethods.Count > 15
let newMethods = getMethodsProc(t)
where newMethods.Count > oldMethods.Count
let addedMethods = newMethods.Where(m => m.WasAdded())
let removedMethods = oldMethods.Where(m => m.WasRemoved())
select new {
t,
nbOldMethods = oldMethods.Count,
nbNewMethods = newMethods.Count,
addedMethods ,
removedMethods
}
//<Description>
// This rule is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// Types where number of methods is greater than 15
// might be hard to understand and maintain.
//
// This rule lists types that already had more than 15 methods
// at the baseline time, and for which new methods have been added.
//
// Having many methods for a type might be a symptom
// of too many responsibilities implemented.
//
// Notice that constructors and methods generated by the compiler
// are not taken account.
//</Description>
//<HowToFix>
// To refactor such type and increase code quality and maintainability,
// certainly you'll have to split the type into several smaller types
// that together, implement the same logic.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[//<Name>Avoid transforming an immutable type into a mutable one</Name>
warnif count > 0
from t in Application.Types where
t.IsPresentInBothBuilds() &&
t.OlderVersion().IsImmutable &&
!t.IsImmutable &&
// Don't take account of immutable types transformed into static types (not deemed as immtable)
!t.IsStatic
let culpritFields = t.Fields.Where(f => f.IsImmutable)
select new {
t,
culpritFields
}
//<Description>
// This rule is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// A type is considered as *immutable* if its instance fields
// cannot be modified once an instance has been built by a constructor.
//
// Being immutable has several fortunate consequences for a type.
// For example its instance objects can be used concurrently
// from several threads without the need to synchronize accesses.
//
// Hence users of such type often rely on the fact that the type is immutable.
// If an immutable type becomes mutable, there are chances that this will break
// users code.
// This is why this rule warns about such immutable type that become mutable.
//</Description>
//<HowToFix>
// If being immutable is an important property for a matched type,
// then the code must be refactored to preserve immutability.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid adding instance fields to a type that already had many instance fields</Name>
warnif count > 0
// Count instance fields and non-readonly static fields
let getFieldsProc = new Func<IType, IList<IField>>(
t => t.Fields.Where(f =>
!f.IsLiteral &&
!f.IsGeneratedByCompiler &&
!(f.IsStatic && f.IsInitOnly)).ToArray())
from t in JustMyCode.Types where
!t.IsEnumeration &&
t.IsPresentInBothBuilds()
// Optimization: fast discard of non-relevant types
where t.OlderVersion().NbFields > 15
let oldFields = getFieldsProc(t.OlderVersion())
where oldFields.Count > 15
let newFields = getFieldsProc(t)
where newFields.Count > oldFields.Count
let addedFields = newFields.Where(f => f.WasAdded())
let removedFields = oldFields.Where(f => f.WasRemoved())
select new {
t,
nbOldFields = oldFields.Count,
nbNewFields = newFields.Count,
addedFields ,
removedFields
}
//<Description>
// This rule is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// Types where number of fields is greater than 15
// might be hard to understand and maintain.
//
// This rule lists types that already had more than 15 fields
// at the baseline time, and for which new fields have been added.
//
// Having many fields for a type might be a symptom
// of too many responsibilities implemented.
//
// Notice that *constants* fields and *static-readonly* fields are not taken account.
// Enumerations types are not taken account also.
//</Description>
//<HowToFix>
// To refactor such type and increase code quality and maintainability,
// certainly you'll have to group subsets of fields into smaller types
// and dispatch the logic implemented into the methods
// into these smaller types.
//</HowToFix>]]></Query>
</Group>
<Group Name="Object Oriented Design" Active="True" ShownInReport="True">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Base class should not use derivatives</Name>
warnif count > 0
from baseClass in JustMyCode.Types
where baseClass.IsClass && baseClass.NbChildren > 0 // <-- for optimization!
let derivedClassesUsed = baseClass.DerivedTypes.UsedBy(baseClass)
where derivedClassesUsed.Count() > 0
select new { baseClass, derivedClassesUsed }
//<Description>
// In *Object-Oriented Programming*, the **open/closed principle** states:
// *software entities (components, classes, methods, etc.) should be open
// for extension, but closed for modification*.
// http://en.wikipedia.org/wiki/Open/closed_principle
//
// Hence a base class should be designed properly to make it easy to derive from,
// this is *extension*. But creating a new derived class, or modifying an
// existing one, shouldn't provoke any *modification* in the base class.
// And if a base class is using some derivative classes somehow, there
// are good chances that such *modification* will be needed.
//
// Extending the base class is not anymore a simple operation,
// this is not good design.
//</Description>
//<HowToFix>
// Understand the need for using derivatives,
// then imagine a new design, and then refactor.
//
// Typically an algorithm in the base class needs to access something
// from derived classes. You can try to encapsulate this access behind
// an abstract or a virtual method.
//
// If you see in the base class some conditions on *typeof(DerivedClass)*
// not only *urgent refactoring* is needed. Such condition can easily
// be replaced through an abstract or a virtual method.
//
// Sometime you'll see a base class that creates instance of some derived classes.
// In such situation, certainly using the *factory method pattern*
// http://en.wikipedia.org/wiki/Factory_method_pattern
// or the *abstract factory pattern*
// http://en.wikipedia.org/wiki/Abstract_factory_pattern
// will improve the design.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Class shouldn't be too deep in inheritance tree</Name>
warnif count > 0 from t in JustMyCode.Types
where t.IsClass
let baseClasses = t.BaseClasses.ExceptThirdParty()
where baseClasses.Count() >= 3
select new { t, baseClasses,
// The metric value DepthOfInheritance takes account
// of third-party base classessee its definition here:
// http://www.ndepend.com/docs/code-metrics#DIT
t.DepthOfInheritance }
//<Description>
// This rule warns about classes having 3 or more base classes.
// Notice that third-party base classes are not counted
// because this rule is about your code design, not
// third-party libraries consumed design.
//
// *In theory*, there is nothing wrong having a *long inheritance chain*,
// if the modelization has been well thought out,
// if each base class is a well-designed refinement of the domain.
//
// *In practice*, modeling properly a domain demands a lot of effort
// and experience and more often than not, a *long inheritance chain*
// is a sign of confused design, that will be hard to work with and maintain.
//</Description>
//<HowToFix>
// In *Object-Oriented Programming*, a well-known motto is
// **Favor Composition over Inheritance**.
//
// This is because *inheritance* comes with pitfalls.
// In general, the implementation of a derived class is very bound up with
// the base class implementation. Also a base class exposes implementation
// details to its derived classes, that's why it's often said that
// inheritance breaks encapsulation.
//
// On the other hands, *Composition* favors binding with interfaces
// over binding with implementations. Hence, not only the encapsulation
// is preserved, but the design is clearer, because interfaces make it explicit
// and less coupled.
//
// Hence, to break a *long inheritance chain*, *Composition* is often
// a powerful way to enhance the design of the refactored underlying logic.
//
// You can also read:
// http://en.wikipedia.org/wiki/Composition_over_inheritance and
// http://stackoverflow.com/questions/49002/prefer-composition-over-inheritance
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Class with no descendant should be sealed if possible</Name>
warnif count > 0 from t in JustMyCode.Types where
t.IsClass &&
t.NbChildren ==0 &&
!t.IsSealed &&
!t.IsStatic
// && !t.IsPublic //<-- You might want to add this condition
// if you are developing a framework
// with classes that are intended to be
// sub-classed by your clients.
orderby t.NbLinesOfCode descending
select new { t, t.NbLinesOfCode }
//<Description>
// If a *non-static* class isn't declared with the keyword *sealed*,
// it means that it can be subclassed everywhere the *non-sealed*
// class is visible.
//
// Making a class a *base class* requires significant design effort.
// Subclassing a *non-sealed* class, not initially designed
// to be subclassed, will lead to unanticipated design issue.
//
// Most classes are *non-sealed* because developers don't care about
// the keyword *sealed*, not because the primary intention was to write
// a class that can be subclassed.
//
// There are minor performance gain in declaring a class as *sealed*.
// But the real benefit of doing so, is actually to **express the
// intention**: *this class has not be designed to be a base class,
// hence it is not allowed to subclass it*.
//
// Notice that by default this rule matches also *public* class.
// If you are developing a framework with classes that are intended
// to be subclassed by your clients, just uncomment the line
// *&& !t.IsPublic*.
//</Description>
//<HowToFix>
// For each matched class, take the time to assess if it is really
// meant to be subclassed. Certainly most matched class will end up
// being declared as *sealed*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Overrides of Method() should call base.Method()</Name>
warnif count > 0
from t in Types // Take account of third-party base classes also
// Bother only classes with descendant
where t.IsClass && t.NbChildren > 0
from mBase in t.InstanceMethods
where mBase.IsVirtual &&
!mBase.IsThirdParty &&
!mBase.IsAbstract &&
!mBase.IsExplicitInterfaceImpl
from mOverride in mBase.OverridesDirectDerived
where !mOverride.IsUsing(mBase)
select new { mOverride, shouldCall = mBase, definedInBaseClass = mBase.ParentType }
//<Description>
// Typically overrides of a base method, should **refine** or **complete**
// the behavior of the base method. If the base method is not called,
// the base behavior is not refined but it is *replaced*.
//
// Violations of this rule are a sign of *design flaw*,
// especially if the actual design provides valid reasons
// that advocates that the base behavior must be replaced and not refined.
//</Description>
//<HowToFix>
// You should investigate if *inheritance* is the right choice
// to bind the base class implementation with the derived classes
// implementations. Does presenting the method with polymorphic
// behavior through an interface, would be a better design choice?
//
// In such situation, often using the design pattern **template method**
// http://en.wikipedia.org/wiki/Template_method_pattern might help
// improving the design.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Do not hide base class methods</Name>
warnif count > 0
// Define a lookup table indexing methods by their name including parameters signature.
let lookup = Methods.Where(m => !m.IsConstructor && !m.IsStatic && !m.IsGeneratedByCompiler)
.ToLookup(m1 => m1.Name)
from t in Application.Types
where !t.IsStatic && t.IsClass &&
// Discard classes deriving directly from System.Object
t.DepthOfInheritance > 1
where t.BaseClasses.Any()
// For each methods not overriding any methods (new slot),
// let's check if it hides by name some methods defined in base classes.
from m in t.InstanceMethods
where m.IsNewSlot && !m.IsExplicitInterfaceImpl && !m.IsGeneratedByCompiler
// Notice how lookup is used to quickly retrieve methods with same name as m.
// This makes the query 10 times faster than iterating each base methods to check their name.
let baseMethodsHidden = lookup[m.Name].Where(m1 => m1 != m && t.DeriveFrom(m1.ParentType))
where baseMethodsHidden.Count() > 0
select new { m, baseMethodsHidden }
//<Description>
// Method hiding is when a base class has a non-virtual method *M()*,
// and a derived class has also a method *M()* with the same signature.
// In such situation, calling *base.M()* does something different
// than calling *derived.M()*.
//
// Notice that this is not *polymorphic* behavior. With *polymorphic*
// behavior, calling both *base.M()* and *derived.M()* on an instance
// object of *derived*, invoke the same implementation.
//
// This situation should be avoided because it obviously leads to confusion.
// This rule warns about all method hiding cases in the code base.
//</Description>
//<HowToFix>
// To fix a violation of this rule, remove or rename the method,
// or change the parameter signature so that the method does
// not hide the base method.
//
// However *method hiding is for those times when you need to have two
// things to have the same name but different behavior*. This is a very
// rare situations, described here:
// http://blogs.msdn.com/b/ericlippert/archive/2008/05/21/method-hiding-apologia.aspx
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>A stateless class or structure might be turned into a static type</Name>
warnif count > 0 from t in JustMyCode.Types where
!t.IsStatic &&
!t.IsGeneric &&
t.InstanceFields.Count() == 0 &&
// Don't match:
// --> types that implement some interfaces.
t.NbInterfacesImplemented == 0 &&
// --> or classes that have sub-classes children.
t.NbChildren == 0 &&
// --> or classes that have a base class
((t.IsClass && t.DepthOfDeriveFrom("System.Object".AllowNoMatch()) == 1) ||
t.IsStructure)
select t
//<Description>
// This rule matches classes and structures that are not static, nor generic,
// that doesn't have any instance fields, that doesn't implement any interface
// nor has a base class (different than *System.Object*).
//
// Such class or structure is a *stateless* collection of *pure* functions,
// that doesn't act on any *this* object data. Such collection of *pure* functions
// is better hosted in a **static class**. Doing so simplifies the client code
// that doesn't have to create an object anymore to invoke the *pure* functions.
//</Description>
//<HowToFix>
// Declare all methods as *static* and transform the class or structure
// into a *static* class.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Non-static classes should be instantiated or turned to static</Name>
warnif count > 0
from t in JustMyCode.Types
where t.IsClass &&
//!t.IsPublic && // if you are developing a framework,
// you might not want to match public classes
!t.IsStatic &&
!t.IsAttributeClass && // Attributes class are never seen as instantiated
// Don't suggest to turn to static, classes that implement interfaces
t.InterfacesImplemented.Count() == 0 &&
!t.DeriveFrom("System.MarshalByRefObject".AllowNoMatch()) && // Types instantiated through remoting infrastructure
// XML serialized type might never be seen as instantiated.
!t.HasAttribute("System.Xml.Serialization.XmlRootAttribute".AllowNoMatch()) &&
!t.IsUsing("System.Xml.Serialization.XmlElementAttribute".AllowNoMatch()) &&
!t.IsUsing("System.Xml.Serialization.XmlAttributeAttribute".AllowNoMatch())
// find the first constructor of t called
let ctorCalled = t.Constructors.FirstOrDefault(ctor => ctor.NbMethodsCallingMe > 0)
// match t if none of its constructors is called.
where ctorCalled == null
select new { t, t.Visibility }
// Notice that classes only instantiated through reflection, like plug-in root classes
// are matched by this rules.
//<Description>
// If the constructors of a class are never called, the class is
// never instantiated, and should be defined as a *static class*.
//
// However this rule doesn't match instantiation through reflection.
// As a consequence, plug-in root classes, instantiated through reflection
// via *IoC frameworks*, can be *false positives* for this rule.
//
// Notice that by default this rule matches also *public* class.
// If you are developing a framework with classes that are intended
// to be instantiated by your clients, just uncomment the line
// *!t.IsPublic*.
//</Description>
//<HowToFix>
// First it is important to investigate why the class is never instantiated.
// If the reason is *the class hosts only static methods* then the class
// can be safely declared as *static*.
//
// Others reasons like, *the class is meant to be instantiated via reflection*,
// or *is meant to be instantiated only by client code* should lead to
// adapt this rule code to avoid these matches.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods should be declared static if possible</Name>
warnif count > 0
from t in JustMyCode.Types.Where(t =>
!t.IsStatic && !t.IsInterface &&
!t.IsEnumeration && !t.IsDelegate &&
!t.IsGeneratedByCompiler)
let methodsThatCanBeMadeStatic =
from m in t.InstanceMethods
// An instance method can be turned to static if it is not virtual,
// not using the this reference and also, not using
// any of its class or base classes instance fields or instance methods.
where !m.IsAbstract && !m.IsVirtual &&
!m.AccessThis && !m.IsExplicitInterfaceImpl &&
// Optimization: Using FirstOrDefault() avoid to check all members,
// as soon as one member is found
// we know the method m cannot be made static.
m.MembersUsed.FirstOrDefault(
mUsed => !mUsed.IsStatic &&
(mUsed.ParentType == t ||
t.DeriveFrom(mUsed.ParentType))
) == null
select m
from m in methodsThatCanBeMadeStatic
let staticFieldsUsed = m.ParentType.StaticFields.UsedBy(m).Where(f => !f.IsGeneratedByCompiler)
select new { m, staticFieldsUsed }
//<Description>
// When an instance method can be *safely* declared as static you should declare it as static.
//
// Whenever you write a method, you fulfill a contract in a given scope.
// The narrower the scope is, the smaller the chance is that you write a bug.
//
// When a method is static, you can't access non-static members; hence, your scope is
// narrower. So, if you don't need and will never need (even in subclasses) instance
// fields to fulfill your contract, why give access to these fields to your method?
// Declaring the method static in this case will let the compiler check that you
// don't use members that you do not intend to use.
//
// Declaring a method as static if possible is also good practice because clients can
// tell from the method signature that calling the method can't alter the object's state.
//
// Doing so, is also a micro performance optimization, since a static method is a
// bit cheaper to invoke than an instance method, because the *this* reference*
// doesn't need anymore to be passed.
//
// Notice that if a matched method is a handler, bound to an event through code
// generated by a designer, declaring it as static might break the designer
// generated code, if the generated code use the *this* invocation syntax,
// (like *this.Method()*).
//</Description>
//<HowToFix>
// Declare matched methods as static.
//
// Since such method doesn't use any instance fields and methods of its type and
// base-types, you should consider if it makes sense, to move such a method
// to a static utility class.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Constructor should not call a virtual method</Name>
warnif count > 0
from t in Application.Types where
t.IsClass &&
!t.IsGeneratedByCompiler &&
!t.IsSealed
from ctor in t.Constructors
let virtualMethodsCalled =
from mCalled in ctor.MethodsCalled
where mCalled.IsVirtual && !mCalled.IsFinal &&
// Only take care of just-my-code virtual methods called
JustMyCode.Contains(mCalled) &&
( mCalled.ParentType == t ||
(t.DeriveFrom(mCalled.ParentType) &&
// Don't accept Object methods since they can be called
// from another reference than the 'this' reference.
mCalled.ParentType.FullName != "System.Object")
)
select mCalled
where virtualMethodsCalled.Count() > 0
select new { ctor ,
virtualMethodsCalled,
// If there is no derived type, it might be
// an opportunity to mark t as sealed.
t.DerivedTypes }
//<Description>
// This rule matches constructors of a non-sealed class that call one or
// several virtual methods.
//
// When an object written in C# is constructed, what happens is that constructors
// run in order from the base class to the most derived class.
//
// Also objects do not change type as they are constructed, but start out as
// the most derived type, with the method table being for the most derived type.
// This means that virtual method calls always run on the most derived type,
// even when calls are made from the constructor.
//
// When you combine these two facts you are left with the problem that if you
// make a virtual method call in a constructor, and it is not the most derived
// type in its inheritance hierarchy, then it will be called on a class whose
// constructor has not been run, and therefore may not be in a suitable state
// to have that method called.
//
// Hence this situation makes the class *fragile to derive from*.
//</Description>
//<HowToFix>
// Violations reported can be solved by re-designing object initialisation
// or by declaring the parent class as *sealed*, if possible.
//</HowToFix>
]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[//<Name>Avoid the Singleton pattern</Name>
warnif count > 0
from t in Application.Types
where !t.IsStatic && !t.IsAbstract && (t.IsClass || t.IsStructure)
// All ctors of a singleton are private
where t.Constructors.Where(ctor => !ctor.IsPrivate).Count() == 0
// A singleton contains one static field of its parent type, to reference the unique instance
let staticFieldInstances = t.StaticFields.WithFieldType(t)
where staticFieldInstances.Count() == 1
select new { t, staticFieldInstance = staticFieldInstances.First() }
//<Description>
// The *singleton pattern* consists in enforcing that a class has just
// a single instance: http://en.wikipedia.org/wiki/Singleton_pattern
// At first glance, this pattern looks appealing, it is simple to implement,
// it adresses a common situation, and as a consequence it is widely used.
//
// However, we discourage you from using singleton classes because experience
// shows that **singleton often results in less testable and less maintainable code**.
// Singleton is *by-design*, not testable. Each unit test should use their own objects
// while singleton forces multiple unit-tests to use the same instance object.
//
// Also the singleton static *GetInstance()* method allows *magic* access to that
// single object and its state from wherever developers want! This potentially
// attractive facility unfortunatly ends up into *unorganized*/*messy* code that
// will require effort to be refactored.
//
// More details available in these discussions:
// http://codebetter.com/patricksmacchia/2011/05/04/back-to-basics-usage-of-static-members/
// http://adamschepis.com/blog/2011/05/02/im-adam-and-im-a-recovering-singleton-addict/
//</Description>
//<HowToFix>
// This rule matches *the classic syntax of singletons*, where one
// static field hold the single instance of the parent class. We underline that
// *the problem is this particular syntax*, that plays against testability.
// The problem is not the fact that a single instance of the class lives
// at runtime.
//
// Hence to fix matches fo this rule, creates the single instance
// at the startup of the program, and pass it to all classes and methods
// that need to access it.
//
// If multiple singletons are identified, they actually form together a
// *program execution context*. Such context can be unified in a unique
// singleton context. Doing so will make it easier to propagate the
// context across the various program units.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Don't assign static fields from instance methods</Name>
warnif count > 0
from f in Application.Fields where
f.IsStatic &&
!f.IsLiteral &&
!f.IsInitOnly &&
!f.IsGeneratedByCompiler &&
// Contract API define such a insideContractEvaluation static field
f.Name != "insideContractEvaluation"
let assignedBy = f.MethodsAssigningMe.Where(m => !m.IsStatic)
where assignedBy .Count() > 0
select new { f, assignedBy }
//<Description>
// Assigning static fields from instance methods leads to
// poorly maintainable and non-thread-safe code.
//
// More discussion on the topic can be found here:
// http://codebetter.com/patricksmacchia/2011/05/04/back-to-basics-usage-of-static-members/
//</Description>
//<HowToFix>
// If the *static* field is just assigned once in the program
// lifetime, make sure to declare it as *readonly* and assign
// it inline, or from the static constructor.
//
// In *Object-Oriented-Programming* the natural artifact
// to hold states that can be modified is **instance fields**.
//
// Hence to fix violations of this rule, make sure to
// hold assignable states through *instance* fields, not
// through *static* fields.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid empty interfaces</Name>
warnif count > 0 from t in JustMyCode.Types where
t.IsInterface &&
t.NbMethods == 0
select new { t, t.TypesThatImplementMe }
//<Description>
//Interfaces define members that provide
//a behavior or usage contract.
//The functionality that is described by the interface
//can be adopted by any type, regardless of where the type
//appears in the inheritance hierarchy.
//A type implements an interface by providing implementations
//for the members of the interface.
//An empty interface does not define any members.
//Therefore, it does not define a contract that can be implemented.
//
//If your design includes empty interfaces that types
//are expected to implement, you are probably using an interface
//as a marker or a way to identify a group of types.
//If this identification will occur at run time,
//the correct way to accomplish this is to use a custom attribute.
//Use the presence or absence of the attribute,
//or the properties of the attribute, to identify the target types.
//If the identification must occur at compile time,
//then it is acceptable to use an empty interface.
//</Description>
//<HowToFix>
//Remove the interface or add members to it.
//If the empty interface is being used to label a set of types,
//replace the interface with a custom attribute.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid types initialization cycles</Name>
warnif count > 0
// Types initialization cycle can only happen between types of an assembly.
from assembly in Application.Assemblies
let cctorSuspects = assembly.ChildMethods.Where(
m => m.IsClassConstructor &&
// Optimization: types involved in a type cycle necessarily don't have type level.
m.ParentType.Level == null)
where cctorSuspects.Count() > 1
let typesSuspects = cctorSuspects.ParentTypes().ToHashSet()
//
// dicoTmp associates to each type suspect T, a set of types from typesSuspects
// that contains at least a method or a field used directly or indirectly by the cctor of T.
//
let dicoTmp = cctorSuspects.ToDictionary(
cctor => cctor.ParentType,
cctor => ((IMember)cctor).ToEnumerable().FillIterative(
members => from m in members
from mUsed in (m is IMethod) ? (m as IMethod).MembersUsed : new IMember[0]
where mUsed.ParentAssembly == assembly
select mUsed)
.DefinitionDomain
.Select(m => m.ParentType) // Don't need .Distinct() here, because of ToHashSet() below.
.Except(cctor.ParentType)
.Intersect(typesSuspects)
.ToHashSet()
)
//
// dico associates to each type suspect T, the set of types initialized (directly or indirectly)
// by the initialization of T. This second step is needed, because if a cctor of a type T1
// calls a member of a type T2, not only the cctor of T1 triggers the initialization of T2,
// but also it triggers the initialization of all types that are initialized by T2 initialization.
//
let dico = typesSuspects.Where(t => dicoTmp[t].Count() > 0).ToDictionary(
typeSuspect => typeSuspect,
typeSuspect => typeSuspect.ToEnumerable().FillIterative(
types => from t in types
from tUsed in dicoTmp[t]
select tUsed)
.DefinitionDomain
.Except(typeSuspect)
.ToHashSet()
)
//
// Now that dico is prepared, detect the cctor cycles
//
from t in dico.Keys
// Thanks to the work done to build dico, it is now pretty easy
// to spot types involved in an initialization cyle with t!
let usersAndUseds = from tTmp in dico[t]
where dico.ContainsKey(tTmp) && dico[tTmp].Contains(t)
select tTmp
where usersAndUseds.Count() > 0
// Here we've found type(s) both using and used by the suspect type.
// A cycle involving the type t is found!
let typeInitCycle = usersAndUseds.Append(t)
// Compute methodsCalled and fieldsUsed, useful to explore
// how a cctor involved in a type initialization cycle, triggers other type initialization.
let methodsCalledDepth = assembly.ChildMethods.DepthOfIsUsedBy(t.ClassConstructor)
let fieldsUsedDepth = assembly.ChildFields.DepthOfIsUsedBy(t.ClassConstructor)
let methodsCalled = methodsCalledDepth.DefinitionDomain.OrderBy(m => methodsCalledDepth[m]).ToArray()
let fieldsUsed = fieldsUsedDepth.DefinitionDomain.OrderBy(f => fieldsUsedDepth[f]).ToArray()
// Use the tick box to: Group cctors methods By parent types
select new { t.ClassConstructor,
cctorsCycle= typeInitCycle.Select(tTmp => tTmp.ClassConstructor),
// methodsCalled and fieldsUsed are members used directly and indirectly by the cctor.
// Export these members to the dependency graph (right click the cell Export/Append … to the Graph)
// and see how the cctor trigger the initialization of other types
methodsCalled,
fieldsUsed
}
//<Description>
// The *class constructor* (also called *static constructor*, and named *cctor* in IL code)
// of a type, if any, is executed by the CLR at runtime, the first time the type is used.
// A *cctor* doesn't need to be explicitly declared in C# or VB.NET, to exist in compiled IL code.
// Having a static field inline initialization is enough to have
// the *cctor* implicitly declared in the parent class or structure.
//
// If the *cctor* of a type *t1* is using the type *t2* and if the *cctor* of *t2* is using *t1*,
// some type initialization unexpected and hard-to-diagnose buggy behavior can occur.
// Such a cyclic chain of initialization is not necessarily limited to two types
// and can embrace *N* types in the general case.
// More information on types initialization cycles can be found here:
// http://codeblog.jonskeet.uk/2012/04/07/type-initializer-circular-dependencies/
//
// The present code rule enumerates types initialization cycles.
// Some *false positives* can appear if some lambda expressions are defined
// in *cctors* or in methods called by *cctors*. In such situation, this rule
// considers these lambda expressions as executed at type initialization time,
// while it is not necessarily the case.
//</Description>
//<HowToFix>
// Types initialization cycles create confusion and unexpected behaviors.
// If several states hold by several classes must be initialized during the first
// access of any of those classes, a better design option is to create a dedicated
// class whose responsibility is to initialize and hold all these states.
//</HowToFix>]]></Query>
</Group>
<Group Name="Design" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid custom delegates</Name>
warnif count > 0
from t in Application.Types where t.IsDelegate
let invokeMethod = (from m in t.Methods where m.SimpleName == "Invoke" select m).Single()
let signature1 = invokeMethod.Name.Substring(
invokeMethod.SimpleName.Length,
invokeMethod.Name.Length - invokeMethod.SimpleName.Length)
// 'ref' and 'out' parameters cannot be supported
where !signature1.Contains("&")
let signature2 = signature1.Replace("(","<").Replace(")",">")
let signature3 = signature2 == "<>" ? "" : signature2
let resultTypeName = invokeMethod.ReturnType == null ? "????" :
invokeMethod.ReturnType.FullName == "System.Void" ? "" :
invokeMethod.ReturnType.Name
let replaceWith =
resultTypeName == "Boolean" && invokeMethod.NbParameters == 1 ?
"Predicate" + signature3 : resultTypeName == "" ?
"Action" + signature3 : invokeMethod.NbParameters ==0 ?
"Func<" + resultTypeName + ">" :
"Func" + signature3.Replace(">", "," + resultTypeName + ">")
select new { t, replaceWith }
//<Description>
// Generic delegates sould be preferred over custom delegates.
// Generic delegates are:
//
// • *Action<…>* to represent any method with *void* return type.
//
// • *Func<…>* to represent any method with a return type. The last
// generic argument is the return type of the prototyped methods.
//
// • *Predicate<T>* to represent any method that takes an instance
// of *T* and that returns a *boolean*.
//
// • Expression<…> that represents function definitions that can be
// compiled and subsequently invoked at runtime but can also be
// serialized and passed to remote processes.
//
// Thanks to generic delegates, not only the code using these custom
// delegates will become clearer, but you'll be relieved from the
// maintenance of these delegate types.
//
// Notice that delegates that are consumed by *DllImport* extern methods
// must not be converted, else this could provoke marshalling issues.
//</Description>
//<HowToFix>
// Remove custom delegates and replace them with generic
// delegates shown in the **replaceWith** column.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types with disposable instance fields must be disposable</Name>
warnif count > 0
// Several IDisposable types can be found if several .NET Fx are referenced.
let iDisposables = ThirdParty.Types.WithFullName("System.IDisposable")
where iDisposables.Any() // in case the code base doesn't use at all System.IDisposable
from t in Application.Types.Except(Application.Types.ThatImplementAny(iDisposables))
where !t.IsGeneratedByCompiler
let instanceFieldsDisposable =
t.InstanceFields.Where(f => f.FieldType != null &&
f.FieldType.InterfacesImplemented.Intersect(iDisposables).Any())
where instanceFieldsDisposable.Any()
select new { t, instanceFieldsDisposable }
//<Description>
// This rule warns when a class declares and implements an instance field that
// is a *System.IDisposable* type and the class does not implement *IDisposable*.
//
// A class implements the *IDisposable* interface to dispose of unmanaged resources
// that it owns. An instance field that is an *IDisposable* type indicates that
// the field owns an unmanaged resource. A class that declares an *IDisposable*
// field indirectly owns an unmanaged resource and should implement the
// *IDisposable* interface. If the class does not directly own any unmanaged
// resources, it should not implement a finalizer.
//</Description>
//<HowToFix>
// To fix a violation of this rule, implement *IDisposable* and from the
// *IDisposable.Dispose()* method call the *Dispose()* method of the field(s).
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Disposable types with unmanaged resources should declare finalizer</Name>
// This default rule is disabled by default,
// see in the rule description (below) why.
// warnif count > 0
// Several IDisposable type can be found if several .NET Fx are referenced.
let iDisposables = ThirdParty.Types.WithFullName("System.IDisposable")
where iDisposables.Any() // in case the code base doesn't use at all System.IDisposable
let disposableTypes = Application.Types.ThatImplementAny(iDisposables)
let unmanagedResourcesFields = disposableTypes.ChildFields().Where(f =>
!f.IsStatic &&
f.FieldType != null &&
f.FieldType.FullName.EqualsAny(
"System.IntPtr",
"System.UIntPtr",
"System.Runtime.InteropServices.HandleRef")).ToHashSet()
let disposableTypesWithUnmanagedResource = unmanagedResourcesFields.ParentTypes()
from t in disposableTypesWithUnmanagedResource
where !t.HasFinalizer
let unmanagedResourcesTypeFields = unmanagedResourcesFields.Intersect(t.InstanceFields)
select new { t, unmanagedResourcesTypeFields }
//<Description>
//A type that implements *System.IDisposable*,
//and has fields that suggest the use of unmanaged resources,
//does not implement a finalizer as described by *Object.Finalize()*.
//A violation of this rule is reported
//if the disposable type contains fields of the following types:
//
// • *System.IntPtr*
//
// • *System.UIntPtr*
//
// • *System.Runtime.InteropServices.HandleRef*
//
// Notice that this default rule is disabled by default,
// because it typically reports *false positive* for classes
// that just hold some references to managed resources,
// without the responsibility to dispose them.
//
// To enable this rule just uncomment *warnif count > 0*.
//</Description>
//<HowToFix>
//To fix a violation of this rule,
//implement a finalizer that calls your *Dispose()* method.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Methods that create disposable object(s) and that don't call Dispose()</Name>
// Uncomment this to transform this code query into a code rule.
// warnif count > 0
// Several IDisposable types can be found if several .NET Fx are referenced.
let iDisposables = ThirdParty.Types.WithFullName("System.IDisposable")
where iDisposables.Any() // in case the code base doesn't use at all System.IDisposable
// Build sequences of disposableTypes and disposeMethods
let disposableTypes = Types.ThatImplementAny(iDisposables).Concat(iDisposables)
let disposeMethods = disposableTypes.ChildMethods().WithName("Dispose()").ToHashSet()
// -> You can refine this code query by assigning to disposableTypesToLookAfter something like:
// disposableTypes.WithFullNameIn("Namespace.TypeName1", "Namespace.TypeName2", ...)
let disposableTypesToLookAfter = disposableTypes
// -> You can refine this code query by assigning to methodsToLookAfter something like:
// Application.Assemblies.WithNameLike("Asm").ChildMethods()
let methodsToLookAfter = Application.Methods
// Enumerate methods that create any disposable type, without calling Dispose()
from m in methodsToLookAfter.ThatCreateAny(disposableTypesToLookAfter )
where !m.MethodsCalled.Intersect(disposeMethods).Any()
select new {
m,
disposableObjectsCreated = disposableTypes.Where(t => m.CreateA(t)),
m.MethodsCalled
}
//<Description>
// This code query enumerates methods that create one or several disposable object(s),
// without calling any Dispose() method.
//
// This code query is not a code rule because it is acceptable to do so,
// as long as disposable objects are disposed somewhere else.
//
// This code query is designed to be be easily refactored
// to look after only specific disposable types, or specific caller methods.
//
// You can then refactor this code query to adapt it to your needs and transform it into a code rule.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Classes that are candidate to be turned into structures</Name>
warnif count > 0 from t in JustMyCode.Types where
t.IsClass &&
!t.IsGeneratedByCompiler &&
!t.IsStatic &&
t.SizeOfInst > 0 &&
t.SizeOfInst <= 16 && // Structure instance must not be too big,
// else it degrades performance.
t.NbChildren == 0 && // Must not have children
// 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
// && t.IsSealed <-- You might want to add this condition
// to restraint the set.
// && t.IsImmutable <-- Structures should be immutable type.
// && t.!IsPublic <-- You might want to add this condition if
// you are developping a framework with classes
// that are intended to be sub-classed by
// your clients.
select new { t, t.SizeOfInst, t.InstanceFields }
//<Description>
// *Int32*, *Double*, *Char* or *Boolean* are structures and not classes.
// Structure are particularly suited to implement **lightweight value**s.
// Hence a class is candidate to be turned into a structure
// when its instances are *lightweight values*.
//
// This is a matter of *performance*. It is expected that a program
// works with plenty of *short lived lightweight values*.
// In such situation, the advantage of using *struct* instead of
// *class*, (in other words, the advantage of using *values* instead
// of *objects*), is that *values* are not managed by the garbage collector.
// This means that values are cheaper to deal with.
//
// This rule matches classes that looks like being *lightweight values*.
// The characterization of such class is:
//
// • Its instances have a small *memory footprint* (at most 16 bytes)
//
// • Its instances have a memory footprint greater than zero
// (i.e the class has at least one instance field).
//
// • It implements no interfaces.
//
// • It has no derived classes.
//
// • It derives directly from *System.Object*.
//
// This rule doesn't take account if instances of matched
// classes are numerous *short-lived* objects.
// These criterions are just indications. Only you can decide if it is
// *performance wise* to transform a class into a structure.
//
// A related case-study of using *class* or *struct* for *Tuple<…>* generic
// types can be found here:
// http://stackoverflow.com/questions/2410710/why-is-the-new-tuple-type-in-net-4-0-a-reference-type-class-and-not-a-value-t
//</Description>
//<HowToFix>
// Just use the keyword *struct* instead of the keyword *class*.
//
// **CAUTION:** Before applying this rule, make sure to understand
// the **deep implications** of transforming a class into a structure.
// http://msdn.microsoft.com/en-us/library/aa664471(v=vs.71).aspx
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid namespaces with few types</Name>
warnif count > 0 from n in JustMyCode.Namespaces
where n.Name.Length > 0 // Don't match anonymous namespaces
let types = n.ChildTypes.Where(t => !t.IsGeneratedByCompiler)
where
types.Count() < 5
orderby types.Count() ascending
select new { n, types }
//<Description>
// This rule warns about namespaces other than the global namespace
// that contain less than five types.
//
// Make sure that each of your namespaces has a logical organization
// and that a valid reason exists to put types in a sparsely
// populated namespace.
//
// Namespaces should contain types that are used together in most
// scenarios. When their applications are mutually exclusive,
// types should be located in separate namespaces. For example,
// the *System.Web.UI* namespace contains types that are used
// in Web applications, and the *System.Windows.Forms* namespace
// contains types that are used in Windows-based applications.
// Even though both namespaces have types that control aspects
// of the user interface, these types are not designed for
// use in the same application. Therefore, they are located in
// separate namespaces.
//
// Careful namespace organization can also be helpful because
// it increases the discoverability of a feature. By examining the
// namespace hierarchy, library consumers should be able to locate
// the types that implement a feature.
//</Description>
//<HowToFix>
// To fix a violation of this rule, try to combine namespaces
//that contain just a few types into a single namespace.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Nested types should not be visible</Name>
warnif count > 0 from t in JustMyCode.Types where
t.IsNested &&
!t.IsGeneratedByCompiler &&
!t.IsPrivate
select new {
t,
t.Visibility
}
//<Description>
// This rule warns about nested types not declared as private.
//
// A nested type is a type declared within the scope of another
// type. Nested types are useful for encapsulating private
// implementation details of the containing type. Used
// for this purpose, nested types should not be externally visible.
//
// Do not use externally visible nested types for logical
// grouping or to avoid name collisions; instead use namespaces.
//
// Nested types include the notion of member accessibility,
// which some programmers do not understand clearly.
//
// Protected types can be used in subclasses and nested types
// in advanced customization scenarios.
//</Description>
//<HowToFix>
// If you do not intend the nested type to be externally visible,
// change the type's accessibility.
//
// Otherwise, remove the nested type from its parent and make it
// *non-nested*.
//
// If the purpose of the nesting is to group some nested types,
// use a namespace to create the hierarchy instead.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Declare types in namespaces</Name>
warnif count > 0 from n in Application.Namespaces where
// If an anonymous namespace can be found,
// it means that it contains types outside of namespaces.
n.Name == ""
// Eliminate anonymous namespaces that contains
// only generated types.
let childTypes = n.ChildTypes.Where(t => !t.IsGeneratedByCompiler)
where childTypes.Count() > 0
select new {
n,
childTypes,
n.NbLinesOfCode }
//<Description>
// Types are declared within namespaces to prevent name collisions,
// and as a way of organizing related types in an object hierarchy.
//
// Types outside any named namespace are in a *global
// namespace* that cannot be referenced in code.
//
// The *global namespace* has no name, hence it is qualified as
// being the *anonymous namespace*.
//
// This rule warns about *anonymous namespaces*.
//</Description>
//<HowToFix>
// To fix a violation of this rule,
// declare all types of all anonymous
// namespaces in some named namespaces.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Empty static constructor can be discarded</Name>
warnif count > 0 from m in Application.Methods where
m.IsClassConstructor &&
m.NbLinesOfCode == 0
select m
//<Description>
// The *class constructor* (also called *static constructor*, and named *cctor* in IL code)
// of a type, if any, is executed by the CLR at runtime, just before the first time the type is used.
//
// This rule warns about the declarations of *static constructors*
// that don't contain any lines of code. Such *cctors* are useless
// and can be safely removed.
//</Description>
//<HowToFix>
// Remove matched empty *static constructors*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Instances size shouldn't be too big</Name>
warnif count > 0 from t in JustMyCode.Types where
t.SizeOfInst > 64
orderby t.SizeOfInst descending
select new { t, t.SizeOfInst, t.InstanceFields }
//<Description>
// Types where *SizeOfInst > 64* might degrade performance
// if many instances are created at runtime.
// They can also be hard to maintain.
//
// Notice that a class with a large *SizeOfInst* value
// doesn't necessarily have a lot of instance fields.
// It might derive from a class with a large *SizeOfInst* value.
//
// See the definition of the *SizeOfInst* metric here
// http://www.ndepend.com/docs/code-metrics#SizeOfInst
//</Description>
//<HowToFix>
// A type with a large *SizeOInst* value hold *directly*
// a lot of data. Typically, you can group this data into
// smaller types that can then be composed.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Boxing/unboxing should be avoided</Name>
warnif percentage > 5 from m in Application.Methods where
m.IsUsingBoxing ||
m.IsUsingUnboxing
select new { m, m.NbLinesOfCode, m.IsUsingBoxing, m.IsUsingUnboxing }
//<Description>
// *Boxing* is the process of converting a value type to the type
// *object* or to any interface type implemented by this value
// type. When the CLR boxes a value type, it wraps the value
// inside a *System.Object* and stores it on the managed heap.
//
// *Unboxing* extracts the value type from the object. Boxing
// is implicit; unboxing is explicit.
//
// The concept of boxing and unboxing underlies the C# unified
// view of the type system in which a value of any type can
// be treated as an object. More about *boxing* and *unboxing*
// here: https://msdn.microsoft.com/en-us/library/yz2be5wk.aspx
//
// This rule warns when more than 5% of methods in a code base
// are using *boxing* or *unboxing*. Because *boxing* and
// *unboxing* come with a performance penalty and can be often
// avoided.
//</Description>
//<HowToFix>
// Thanks to .NET generic, and especially thanks to
// generic collections, *boxing* and *unboxing* should
// be rarely used. Hence in most situations the code can
// be refactored to avoid relying on *boxing* and *unboxing*.
// See for example:
// http://stackoverflow.com/questions/4403055/boxing-unboxing-and-generics
//
// With a performance profiler, indentify methods that consume
// a lot of CPU time. If such method uses *boxing* or
// *unboxing*, especially in a **loop**, make sure to refactor it.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Attribute classes should be sealed</Name>
warnif count > 0 from t in Application.Types where
t.IsAttributeClass &&
!t.IsSealed &&
!t.IsAbstract &&
t.IsPublic
select new { t, t.NbLinesOfCode }
//<Description>
// The .NET Framework class library provides methods
// for retrieving custom attributes. By default,
// these methods search the attribute inheritance
// hierarchy; for example
// *System.Attribute.GetCustomAttribute()*
// searches for the specified attribute type, or any
// attribute type that extends the specified attribute
// type.
//
// Sealing the attribute eliminates the search
// through the inheritance hierarchy, and can improve
// performance.
//</Description>
//<HowToFix>
// To fix a violation of this rule, seal the attribute
// type or make it abstract.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Don't use obsolete types, methods or fields</Name>
warnif count > 0
let obsoleteTypes = Types.Where(t => t.IsObsolete)
let obsoleteMethods = Methods.Where(m => m.IsObsolete).ToHashSet()
let obsoleteFields = Fields.Where(f => f.IsObsolete)
from m in JustMyCode.Methods.UsingAny(obsoleteTypes).Union(
JustMyCode.Methods.UsingAny(obsoleteMethods)).Union(
JustMyCode.Methods.UsingAny(obsoleteFields))
let obsoleteTypesUsed = obsoleteTypes.UsedBy(m)
// Optimization: MethodsCalled + Intersect() is faster than using obsoleteMethods.UsedBy()
let obsoleteMethodsUsed = m.MethodsCalled.Intersect(obsoleteMethods)
let obsoleteFieldsUsed = obsoleteFields.UsedBy(m)
select new { m, obsoleteTypesUsed, obsoleteMethodsUsed, obsoleteFieldsUsed }
//<Description>
// The attribute *System.ObsoleteAttribute* is used to tag
// types, methods or fields of an API that clients shouldn't
// use because these code elements will be removed sooner
// or later.
//
// This rule warns about methods that use a type, a method
// or a field, tagged with *System.ObsoleteAttribute*.
//</Description>
//<HowToFix>
// Typically when a code element is tagged with
// *System.ObsoleteAttribute*, a *workaround message*
// is provided to clients.
//
// This *workaround message* will tell you what to do
// to avoid using the obsolete code element.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Don't forget to implement methods that throw NotImplementedException</Name>
warnif count > 0
from m in Application.Methods
where m.CreateA("System.NotImplementedException".AllowNoMatch())
select m
//<Description>
// The exception *NotImplementedException* is used to declare
// a method *stub* that can be invoked, and defer the
// development of the method implementation.
//
// This exception is especially useful when doing **TDD**
// (*Test Driven Development*) when tests are written first.
// This way tests fail until the implementation is written.
//
// Hence using *NotImplementedException* is a *temporary*
// facility, and before releasing, will come a time when
// this exception shouldn't be used anywhere in code.
//
// This rule warns about method still using
// *NotImplementedException*.
//</Description>
//<HowToFix>
// Investigate first why *NotImplementedException* is still
// used. Either the method must be implemented, or
// the method stub is not used and can be safely removed.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Override equals and operator equals on value types</Name>
warnif count > 0
from t in Application.Types where
t.IsStructure
let equalsMethod = t.InstanceMethods.Where(m0 => m0.Name == "Equals(Object)").SingleOrDefault()
where equalsMethod == null
select t
//<Description>
// For value types, the inherited implementation of *Equals()* uses
// the Reflection library, and compares the contents of all
// fields. Reflection is computationally expensive, and comparing
// every field for equality might be unnecessary.
//
// If you expect users to compare or sort instances, or use them
// as hash table keys, your value type should implement *Equals()*.
// In C# and VB.NET, you should also provide an implementation of
// the equality and inequality operators.
//</Description>
//<HowToFix>
// To fix a violation of this rule, provide an implementation
// of *Equals()* and implement the equality and inequality operators.
//</HowToFix>]]></Query>
</Group>
<Group Name="Architecture and Layering" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Avoid namespaces mutually dependent</Name>
warnif count > 0
// Optimization: restraint application assemblies set
// If some namespaces are mutually dependent
// - They must be declared in the same assembly
// - The parent assembly must ContainsNamespaceDependencyCycle
from assembly in Application.Assemblies.Where(a => a.ContainsNamespaceDependencyCycle != null && a.ContainsNamespaceDependencyCycle.Value)
// hashset is used to avoid reporting both A <-> B and B <-> A
let hashset = new HashSet<INamespace>()
// Optimization: restraint namespaces set
// If a namespace doesn't have a Level value, it must be in a dependency cycle
// or it must be using directly or indirectly a dependency cycle.
let namespacesSuspect = assembly.ChildNamespaces.Where(n => n.Level == null)
from nA in namespacesSuspect
// Select namespaces mutually dependent with nA
let unused = hashset.Add(nA) // Populate hashset
let namespacesMutuallyDependentWith_nA = nA.NamespacesUsed.Using(nA)
.Except(hashset) // <-- avoid reporting both A <-> B and B <-> A
where namespacesMutuallyDependentWith_nA.Count() > 0
from nB in namespacesMutuallyDependentWith_nA
// nA and nB are mutually dependent
// First select the one that shouldn't use the other.
// The first namespace is inferred from the fact that it is using less types of the second.
let typesOfBUsedByA = nB.ChildTypes.UsedBy(nA)
let typesOfAUsedByB = nA.ChildTypes.UsedBy(nB)
let first = (typesOfBUsedByA.Count() > typesOfAUsedByB.Count()) ? nB : nA
let second = (first == nA) ? nB : nA
let typesOfFirstUsedBySecond = (first == nA) ? typesOfAUsedByB : typesOfBUsedByA
let typesOfSecondUsedByFirst = (first == nA) ? typesOfBUsedByA : typesOfAUsedByB
select new {
first,
shouldntUseNamespace = second,
OK_typesOfFirstUsedBySecond = typesOfFirstUsedBySecond,
KO_typesOfSecondUsedByFirst = typesOfSecondUsedByFirst }
//<Description>
// This rule lists pair of namespace mutually dependent.
//
// The pair *{ first, second }* is formatted to show
// **first namespace shouldn't use the second namespace**.
// The *first/second* order is inferred from the number of types
// used by each other. The first namespace is using fewer types of
// the second. In general this indicates that the first namespace
// should be at a *lower-level* in the architecture than
// the second.
//
// Following this rule is useful to avoid **namespaces dependency
// cycles**. This will get the code architecture close to a
// *layered architecture*, where *low-level* code is not allowed
// to use *high-level* code.
//
// In other words, abiding by this rule will help significantly
// getting rid of what is often called **spagetthi code:
// Entangled code that is not properly layered and structured**.
//
// More on this in our white books relative to partitioning code.
// http://www.ndepend.com/docs/white-books
//</Description>
//<HowToFix>
// Refactor the code to make sure that *the first namespace doesn't
// use the second namespace*. To get started, first explore the
// coupling between two namespaces mutually dependent:
//
// 1) from the *right-click menu* export the first namespace to
// the vertical header of the dependency matrix.
//
// 2) export the second namespace to the horizontal header of
// the dependency matrix.
//
// 3) double-click the black matrix cell (it is black because of
// mutual dependency).
//
// 4) in the matrix command bar, click the button:
// *Remove empty Row(s) and Column(s)*.
//
// At this point, the dependency matrix shows types involved
// into the coupling.
//
// Some refactoring that helps uncouple two namespaces
// mutually dependent include:
//
// • Moving one or several types from the *low-level* namespaces
// to the *high-level* one, or do the opposite.
//
// • Use *Inversion of Control (IoC)*:
// http://en.wikipedia.org/wiki/Inversion_of_control
// This can consist in creating new interfaces in the
// *low-level* namespace, implemented by classes
// in the *high-level* namespace. This way *low-level*
// code can consume *high-level* code through interfaces,
// without using directly *high-level* implementations.
// Interfaces can be passed to *low-level* code through
// the *high-level* namespace code, or through even
// higher-level code. In related documentations
// you can see these interfaces named as *callbacks*,
// and the overall pattern is also known as
// *Dependency Injection (DI)*:
// http://en.wikipedia.org/wiki/Dependency_injection
//</HowToFix>P
]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid namespaces dependency cycles</Name>
warnif count > 0
// Optimization: restraint application assemblies set
// If some namespaces are mutually dependent
// - They must be declared in the same assembly
// - The parent assembly must ContainsNamespaceDependencyCycle
from assembly in Application.Assemblies
.Where(a => a.ContainsNamespaceDependencyCycle != null &&
a.ContainsNamespaceDependencyCycle.Value)
// Optimization: restraint namespaces set
// A namespace involved in a cycle necessarily have a null Level.
let namespacesSuspect = assembly.ChildNamespaces.Where(n => n.Level == null)
// hashset is used to avoid iterating again on namespaces already caught in a cycle.
let hashset = new HashSet<INamespace>()
from suspect in namespacesSuspect
// By commenting in this line, the query matches all namespaces involved in a cycle.
where !hashset.Contains(suspect)
// Define 2 code metrics
// - Namespaces depth of is using indirectly the suspect namespace.
// - Namespaces depth of is used by the suspect namespace indirectly.
// Note: for direct usage the depth is equal to 1.
let namespacesUserDepth = namespacesSuspect.DepthOfIsUsing(suspect)
let namespacesUsedDepth = namespacesSuspect.DepthOfIsUsedBy(suspect)
// Select namespaces that are both using and used by namespaceSuspect
let usersAndUsed = from n in namespacesSuspect where
namespacesUserDepth[n] > 0 &&
namespacesUsedDepth[n] > 0
select n
where usersAndUsed.Count() > 0
// Here we've found namespace(s) both using and used by the suspect namespace.
// A cycle involving the suspect namespace is found!
let cycle = usersAndUsed.Append(suspect)
// Fill hashset with namespaces in the cycle.
// .ToArray() is needed to force the iterating process.
let unused1 = (from n in cycle let unused2 = hashset.Add(n) select n).ToArray()
select new { suspect, cycle }
//<Description>
// This rule lists all *application namespace dependency cycles*.
// Each row shows a different cycle, indexed with one of the namespace entangled
// in the cycle.
//
// To browse a cycle on the dependency graph or the dependency matrix, right click
// a cycle cell and export the matched namespaces to the dependency graph or matrix.
//
// In the matrix, dependency cycles are represented with red squares and black cells.
// To easily browse dependency cycles, the dependency matrix comes with an option:
// *Display Direct and Indirect Dependencies*
//
// Read our white books relative to partitioning code,
// to know more about namespaces dependency cycles, and why avoiding them
// is a *simple yet efficient* solution to clean the architecture of a code base.
// http://www.ndepend.com/docs/white-books
//</Description>
//<HowToFix>
// Removing first pairs of *mutually dependent namespaces* will eliminate
// most *namespaces dependency cycles*. This is why we suggest
// focusing on matches of the default rule
// **Avoid namespaces mutually dependent** before dealing
// with the present rule.
//
// Once solving all *mutually dependent namespaces*, remaining cycles
// matched by the present rule necessarily involve 3 or more namespaces
// like in: *A is using B is using C is using A*.
// Such cycle can be broken by identifying which namespace should
// be at the *lower-level*. For example if B should be at the
// *lower-level*, then it means C should be at the *higher-level*
// and to break the cycle, you just have to remove the dependency
// from B to C, with a pattern described in the *HowToFix* section
// of the rule *Avoid namespaces mutually dependent*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid partitioning the code base through many small library Assemblies</Name>
warnif count > 10
from a in Application.Assemblies where
( a.NbLinesOfCode < 1000 ||
a.NbILInstructions < 7000 ) &&
a.FilePath.FileExtension.ToLower() == ".dll"
select new { a, a.NbLinesOfCode, a.NbILInstructions }
//<Description>
// Each .NET Assembly compiled represents one or several physical file(s).
// Having too many library .NET Assemblies is a symptom of
// considering **physical** .NET Assemblies as **logical** components.
//
// We advise having less, and bigger, .NET Assemblies
// and using the concept of namespaces to define logical components.
// Benefits are:
//
// • Faster compilation time.
//
// • Faster startup time for your program.
//
// • Easier deployment thanks to less files to manage.
//
// • If you are developing a Framework,
// less .NET assemblies to reference and manage for your clients.
//</Description>
//<HowToFix>
// Consider using the *physical* concept of assemblies for physical needs
// only.
//
// Our white book about **Partitioning code base through .NET assemblies
// and Visual Studio projects** explains in details valid and invalid
// reasons to use assemblies.
// Download it here:
// http://www.ndepend.com/Res/NDependWhiteBook_Assembly.pdf
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>UI layer shouldn't use directly DB types</Name>
warnif count > 0
// UI layer is made of types in namespaces using a UI framework
let uiTypes = Application.Namespaces.UsingAny(Assemblies.WithNameIn("PresentationFramework", "System.Windows", "System.Windows.Forms", "System.Web")).ChildTypes()
// You can easily customize this line to define what are DB types.
let dbTypes = ThirdParty.Assemblies.WithNameIn("System.Data", "EntityFramework", "NHibernate").ChildTypes()
// Ideally even DataSet and associated, usage should be forbidden from UI layer:
// http://stackoverflow.com/questions/1708690/is-list-better-than-dataset-for-ui-layer-in-asp-net
.Except(ThirdParty.Types.WithNameIn("DataSet", "DataTable", "DataRow"))
from uiType in uiTypes.UsingAny(dbTypes)
let dbTypesUsed = dbTypes.Intersect(uiType.TypesUsed)
select new { uiType, dbTypesUsed }
//<Description>
// This rule is more a *sample rule to adapt to your need*,
// than a rigid rule you should abide by. It shows how to
// define code layers in a rule and how to be warned about
// layers dependencies violations.
//
// This rule first defines the UI layer and the DB framework
// layer. Second it checks if any UI layer type is using
// directly any DB framework layer type.
//
// • The **DB framework layer** is defined as the set of *third-party*
// types in the framework *ADO.NET*, *EntityFramework*,
// *NHibernate* types, that the application is consuming.
// It is easy to append and suppress any DB framework.
//
// • The UI layer (**User Interface Layer**) is defined as the
// set of types that use *WPF*, *Windows Form*, *ASP.NET*.
//
// *UI using directly DB frameworks* is generally considered
// as *poor design* because DB frameworks accesses should be
// a concept hidden to UI, encapsulated into a **dedicated
// Data Access Layer (DAL)**.
//</Description>
//<HowToFix>
// This rule lists precisely which UI type uses which
// DB framework type. Instead of fixing matches one by one,
// first imagine how DB framework accesses could be
// encapsulated into a dedicated layer.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>UI layer shouldn't use directly DAL layer</Name>
warnif count > 0
// UI layer is made of types in namespaces using a UI framework
let uiTypes = Application.Namespaces.UsingAny(Assemblies.WithNameIn("PresentationFramework", "System.Windows", "System.Windows.Forms", "System.Web")).ChildTypes()
// Exclude commonly used DataSet and associated, from ADO.Net types
// You can easily customize this line to define what are DB types.
let dbTypes = ThirdParty.Assemblies.WithNameIn("System.Data", "EntityFramework", "NHibernate").ChildTypes()
.Except(ThirdParty.Types.WithNameIn("DataSet", "DataTable", "DataRow"))
// DAL layer is made of types in namespaces using a DB framework
// .ToHashSet() results to faster execution of dalTypes.Intersect(uiType.TypesIUse).
let dalTypes = Application.Namespaces.UsingAny(dbTypes).ChildTypes().ToHashSet()
from uiType in uiTypes.UsingAny(dalTypes)
let dalTypesUsed = dalTypes.Intersect(uiType.TypesUsed)
select new {
uiType,
// if dalTypesUsed is empty, it means that the uiType is part of the DAL
dalTypesUsed
}
//<Description>
// This rule is more a *sample rule to adapt to your need*,
// than a rigid rule you should abide by. It shows how to
// define code layers in a rule and how to be warned about
// layers dependencies violations.
//
// This rule first defines the UI layer and the DAL layer.
// Second it checks if any UI layer type is using directly
// any DAL layer type.
//
// • The DB layer (the DAL, **Data Access Layer**) is defined as
// the set of types of the application that use *ADO.NET*,
// *EntityFramework*, *NHibernate* types. It is easy to append
// and suppress any DB framework.
//
// • The UI layer (**User Interface Layer**) is defined as the
// set of types that use *WPF*, *Windows Form*, *ASP.NET*.
//
// *UI using directly DAL* is generally considered as *poor
// design* because DAL accesses should be a concept
// hidden to UI, encapsulated into an **intermediary domain
// logic**.
//</Description>
//<HowToFix>
// This rule lists precisely which UI type uses which DAL type.
//
// More about this particular design topic here:
// http://www.kenneth-truyers.net/2013/05/12/the-n-layer-myth-and-basic-dependency-injection/
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Assemblies with poor cohesion (RelationalCohesion)</Name>
warnif count > 0 from a in Application.Assemblies
// Build the types list on which we want to check cohesion
// This is the assembly 'a' type, minus enumeration
// and types generated by the compiler.
let types = a.ChildTypes.Where(
t => !t.IsGeneratedByCompiler &&
!t.IsEnumeration)
// Absolutly need ToHashet() to have fast Intersect() calls below.
.ToHashSet()
// Relational Cohesion metrics is relevant only if there are several types
where types.LongCount()> 20
// R is the total number of relationship between types of the assemblies.
let R = types.Sum(t => t.TypesUsed.Intersect(types).Count())
// Relational Cohesion formula
let relationalCohesion = (float)R / types.Count
where
(relationalCohesion < 1.5 ||
relationalCohesion > 4.0)
select new {
a,
a.ChildTypes,
relationalCohesion,
a.RelationalCohesion }
//<Description>
// This rule computes the *Relational Cohesion* metric for
// the application assemblies, and warns about wrong values.
//
// The *Relational Cohesion* for an assembly, is the total number
// of relationship between types of the assemblies, divided
// by the number of types. In other words it is the average
// number of types in the assembly used by a type in the assembly.
//
// 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 *Relational Cohesion* is **1.5 to 4.0**.
//
// Notice that assemblies with less than 20 types are ignored.
//</Description>
//<HowToFix>
// Matches of this present rule might reveal either assemblies
// with specific coding constraints (like code generated that
// have particular structure) either issues in design.
//
// In the second case, large refactoring can be planned
// not to respect this rule in particular, but to increase
// the overall design and code maintainability.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Namespaces with poor cohesion (RelationalCohesion)</Name>
warnif count > 0 from n in Application.Namespaces
// Build the types list on which we want to check cohesion
// This is the namespace children types, minus enumerations
// and types generated by the compiler.
let types = n.ChildTypes.Where(
t => !t.IsGeneratedByCompiler &&
!t.IsEnumeration)
// Absolutly need ToHashet() to have fast Intersect() calls below.
.ToHashSet()
// Relational Cohesion metrics is relevant only if there are enough types
where types.LongCount() > 20
// R is the total number of relationship between types of the namespaces.
let R = types.Sum(t => t.TypesUsed.Intersect(types).Count())
// Relational Cohesion formula
let relationalCohesion = (float)R / types.Count
where
(relationalCohesion < 1.5 ||
relationalCohesion > 4.0)
select new {
n,
n.ChildTypes,
relationalCohesion
}
//<Description>
// This rule computes the *Relational Cohesion* metric for
// the application namespaces, and warns about wrong values.
//
// The *Relational Cohesion* for a namespace, is the total number
// of relationship between types of the namespaces, divided
// by the number of types. In other words it is the average
// number of types in the namespace used by a type in the namespace.
//
// As classes inside an namespace 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 *Relational Cohesion* is **1.5 to 4.0**.
//
// Notice that namespaces with less than 20 types are ignored.
//</Description>
//<HowToFix>
// Matches of this present rule might reveal either namespaces
// with specific coding constraints (like code generated that
// have particular structure) either issues in design.
//
// In the second case, refactoring sessions can be planned
// to increase the overall design and code maintainability.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Assemblies that don't satisfy the Abstractness/Instability principle</Name>
warnif percentage > 15 from a in Application.Assemblies where
a.NormDistFromMainSeq > 0.7
orderby a.NormDistFromMainSeq descending
select new { a, a.NormDistFromMainSeq }
//<Description>
// The **Abstractness versus Instability Diagram** that is shown in the NDepend
// report helps to assess which assemblies are **potentially painful to maintain**
// (i.e concrete and stable) and which assemblies are **potentially useless**
// (i.e abstract and instable).
//
// • **Abstractness**: If an assembly contains many abstract types
// (i.e interfaces and abstract classes) and few concrete types,
// it is considered as abstract.
//
// • **Stability**: An assembly is considered stable if its types
// are used by a lot of types from other assemblies. In this context
// stable means *painful to modify*.
//
// From these metrics, we define the *perpendicular normalized distance of
// an assembly from the idealized line* **A + I = 1** (called *main sequence*).
// This metric is an indicator of the assembly's balance between abstractness
// and stability. We precise that the word *normalized* means that the range
// of values is [0.0 … 1.0].
//
// This rule warns about assemblies with a *normalized distance* greater than
// than 0.7.
//
// This rules use the default code metric on assembly
// *Normalized Distance from the Main Sequence* explained here:
// http://www.ndepend.com/docs/code-metrics#DitFromMainSeq
//
// These concepts have been originally introduced by *Robert C. Martin*
// in 1994 in this paper: http://www.objectmentor.com/resources/articles/oodmetrc.pdf
//</Description>
//<HowToFix>
// Violations of this rule indicate assemblies with an improper
// *abstractness / stability* balance.
//
// • Either the assembly is *potentially painful to maintain* (i.e is massively
// used and contains mostly concrete types). This can be fixed by creating
// abstractions to avoid too high coupling with concrete implementations.
//
// • Either the assembly is *potentially useless* (i.e contains mostly
// abstractions and is not used enough). In such situation, the design
// must be reviewed to see if it can be enhanced.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Example of custom rule to check for dependency</Name>
warnif count > 0 from a in Assemblies
where
a.IsUsing("Foo1.Foo2".AllowNoMatch().MatchNamespace()) &&
(a.Name == @"Foo3")
select new { a, a.NbLinesOfCode }
// the assembly Foo3
// shouldn't use directly
// the namespace Foo3.Foo4
// because (TODO insert your reason)
//<Description>
// This rule is a **sample rule** that shows how to
// check if a particular dependency exists or not,
// from a code element **A** to a code element **B**,
// **A** and **B** being an *Assembly*, a *Namespace*, a *Type*,
// a *Method* or a *Field*, **A** and **B** being not
// necessarily of same kind (i.e two Assemblies or
// two Namespaces…).
//
// Such rule can be generated:
//
// • by right clicking the cell in the *Dependency Matrix*
// with **B** in row and **A** in column,
//
// • or by right-clicking the concerned arrow in the *Dependency
// Graph* from **A** to **B**,
//
// and in both cases, click the menu
// **Generate a code rule that warns if this dependency exists**
//
// The generated rule will look like this one.
// It is now up to you to adapt this rule to check exactly
// your needs.
//</Description>
//<HowToFix>
// This is a *sample rule* there is nothing to fix *as is*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Higher cohesion - lower coupling</Name>
let abstractNamespaces = JustMyCode.Namespaces.Where(
n => n.ChildTypes.Where(t => !t.IsInterface && !t.IsEnumeration && !t.IsDelegate).Count() == 0
).ToHashSet()
let concreteNamespaces = JustMyCode.Namespaces.Except(abstractNamespaces).ToHashSet()
from n in concreteNamespaces
let namespacesUsed = n.NamespacesUsed.ExceptThirdParty()
let concreteNamespacesUsed = namespacesUsed.Except(abstractNamespaces)
let abstractNamespacesUsed = namespacesUsed.Except(concreteNamespaces)
select new { n, concreteNamespacesUsed , abstractNamespacesUsed }
//<Description>
// It is deemed as a good software architecture practice to clearly separate
// *abstract* namespaces that contain only abstractions (interfaces, enumerations, delegates)
// from *concrete* namespaces, that contain classes and structures.
//
// Typically, the more concrete namespaces rely on abstract namespaces *only*,
// the more **Decoupled** is the architecture, and the more **Cohesive** are
// classes inside concrete namespaces.
//
// The present code query defines sets of abstract and concrete namespaces
// and show for each concrete namespaces, which concrete and abstract namespaces
// are used.
//</Description>
//<HowToFix>
// This query can be transformed into a code rule, depending if you wish to
// constraint your code structure *coupling / cohesion* ratio.
//</HowToFix>]]></Query>
</Group>
<Group Name="API Breaking Changes" Active="True" ShownInReport="True">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>API Breaking Changes: Types</Name>
warnif count > 0 from t in codeBase.OlderVersion().Application.Types
where t.IsPubliclyVisible &&
// The type has been removed, it was not tagged as obsolete
// and its parent assembly hasn't been removed …
( ( t.WasRemoved() &&
!t.ParentAssembly.WasRemoved() &&
!t.IsObsolete) ||
// … or the type is not publicly visible anymore
!t.WasRemoved() && !t.NewerVersion().IsPubliclyVisible)
select new {
t,
NewVisibility =
(t.WasRemoved() ? " " :
t.NewerVersion().Visibility.ToString())
}
//<Description>
// This rule is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This rule warns if a type publicly visible in the *baseline*,
// is not publicly visible anymore or if it has been removed.
// Clients code using such type will be broken.
//</Description>
//<HowToFix>
// Make sure that public types that used to be presented to
// clients, still remain public now, and in the future.
//
// If a public type must really be removed, you can tag it
// with *System.ObsoleteAttribute* with a *workaround message*
// during a few public releases, until it gets removed definitely.
// Notice that this rule doesn't match types removed that were
// tagged as obsolete.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>API Breaking Changes: Methods</Name>
warnif count > 0 from m in codeBase.OlderVersion().Application.Methods
where m.IsPubliclyVisible &&
// The method has been removed, it was not tagged as obsolete
// and its parent type hasn't been removed …
( ( m.WasRemoved() &&
!m.ParentType.WasRemoved() &&
!m.IsObsolete) ||
// … or the method is not publicly visible anymore
!m.WasRemoved() && !m.NewerVersion().IsPubliclyVisible)
select new {
m,
NewVisibility =
(m.WasRemoved() ? " " :
m.NewerVersion().Visibility.ToString())
}
//<Description>
// This rule is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This rule warns if a method publicly visible in the *baseline*,
// is not publicly visible anymore or if it has been removed.
// Clients code using such method will be broken.
//</Description>
//<HowToFix>
// Make sure that public methods that used to be presented to
// clients, still remain public now, and in the future.
//
// If a public method must really be removed, you can tag it
// with *System.ObsoleteAttribute* with a *workaround message*
// during a few public releases, until it gets removed definitely.
// Notice that this rule doesn't match methods removed that were
// tagged as obsolete.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>API Breaking Changes: Fields</Name>
warnif count > 0 from f in codeBase.OlderVersion().Application.Fields
where f.IsPubliclyVisible &&
// The field has been removed, it was not tagged as obsolete
// and its parent type hasn't been removed …
( ( f.WasRemoved() &&
!f.ParentType.WasRemoved() &&
!f.IsObsolete) ||
// … or the field is not publicly visible anymore
!f.WasRemoved() && !f.NewerVersion().IsPubliclyVisible)
select new {
f,
NewVisibility =
(f.WasRemoved() ? " " :
f.NewerVersion().Visibility.ToString())
}
//<Description>
// This rule is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This rule warns if a field publicly visible in the *baseline*,
// is not publicly visible anymore or if it has been removed.
// Clients code using such field will be broken.
//</Description>
//<HowToFix>
// Make sure that public fields that used to be presented to
// clients, still remain public now, and in the future.
//
// If a public field must really be removed, you can tag it
// with *System.ObsoleteAttribute* with a *workaround message*
// during a few public releases, until it gets removed definitely.
// Notice that this rule doesn't match fields removed that were
// tagged as obsolete.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>API Breaking Changes: Interfaces and Abstract Classes</Name>
warnif count > 0 from tNewer in Application.Types where
(tNewer.IsInterface || tNewer.IsClass && tNewer.IsAbstract) &&
tNewer.IsPubliclyVisible &&
tNewer.IsPresentInBothBuilds()
let tOlder = tNewer.OlderVersion() where tOlder.IsPubliclyVisible
let methodsRemoved = tOlder.Methods.Where(m => m.IsAbstract && m.WasRemoved())
let methodsAdded = tNewer.Methods.Where(m => m.IsAbstract && m.WasAdded())
where methodsAdded.Count() > 0 || methodsRemoved.Count() > 0
select new {
tNewer,
methodsAdded,
methodsRemoved
}
//<Description>
// This rule is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This rule warns if a publicly visible interface or abstract class
// has been changed and contains new abstract methods or
// if some abstract methods have been removed.
//
// Clients code that implement such interface or derive from
// such abstract class will be broken.
//</Description>
//<HowToFix>
// Make sure that the public contracts of interfaces and abstract classes
// that used to be presented to clients, remain stable now, and in the future.
//
// If a public contract must really be changed, you can tag
// abstract methods that will be removed with *System.ObsoleteAttribute*
// with a *workaround message* during a few public releases, until it gets
// removed definitely.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Broken serializable types</Name>
warnif count > 0
from t in Application.Types where
// Collect types tagged with SerializableAttribute
t.HasAttribute("System.SerializableAttribute".AllowNoMatch()) &&
!t.IsDelegate &&
t.IsPresentInBothBuilds() &&
t.HasAttribute(t)
// Find newer and older versions of NonSerializedAttribute
let newNonSerializedAttribute = ThirdParty.Types.WithFullName("System.NonSerializedAttribute").SingleOrDefault()
let oldNonSerializedAttribute = newNonSerializedAttribute == null ? null : newNonSerializedAttribute.OlderVersion()
// Find added or removed fields not marked with NonSerializedAttribute
let addedInstanceField = from f in t.InstanceFields where
f.WasAdded() &&
(newNonSerializedAttribute == null || !f.HasAttribute(newNonSerializedAttribute))
select f
let removedInstanceField = from f in t.OlderVersion().InstanceFields where
f.WasRemoved() &&
(oldNonSerializedAttribute == null || !f.HasAttribute(oldNonSerializedAttribute))
select f
where addedInstanceField.Count() > 0 || removedInstanceField.Count() > 0
select new {
t,
addedInstanceField,
removedInstanceField
}
//<Description>
// This rule is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This rule warns about breaking changes in types tagged with
// *SerializableAttribute*.
//
// To do so, this rule searches for serializable type with serializable
// instance fields added or removed. Notice that it doesn't take account
// of fields tagged with *NonSerializedAttribute*.
//
// From http://msdn.microsoft.com/library/system.serializableattribute.aspx :
// "All the public and private fields in a type that are marked by the
// *SerializableAttribute* are serialized by default, unless the type
// implements the *ISerializable* interface to override the serialization process.
// The default serialization process excludes fields that are marked
// with the *NonSerializedAttribute* attribute."
//</Description>
//<HowToFix>
// Make sure that the serialization process of serializable types remains
// stable now, and in the future.
//
// Else you'll have to deal with **Version Tolerant Serialization**
// that is explained here:
// https://msdn.microsoft.com/en-us/library/ms229752(v=vs.110).aspx
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Avoid changing enumerations Flags status</Name>
warnif count > 0
let oldFlags = codeBase.OlderVersion().ThirdParty.Types.WithFullName("System.FlagsAttribute").SingleOrDefault()
let newFlags = ThirdParty.Types.WithFullName("System.FlagsAttribute").SingleOrDefault()
where oldFlags != null && newFlags != null
from t in Application.Types where
t.IsEnumeration &&
t.IsPresentInBothBuilds()
let hasFlagsAttributeNow = t.HasAttribute(newFlags)
let usedToHaveFlagsAttribute = t.OlderVersion().HasAttribute(oldFlags)
where hasFlagsAttributeNow != usedToHaveFlagsAttribute
select new {
t,
hasFlagsAttributeNow,
usedToHaveFlagsAttribute
}
//<Description>
// This rule is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This rule matches enumeration types that used to be tagged
// with *FlagsAttribute* in the *baseline*, and not anymore.
// It also matches the opposite, enumeration types that are now
// tagged with *FlagsAttribute*, and were not tagged in the *baseline*.
//
// Being tagged with *FlagsAttribute* is a strong property for an enumeration.
// Not so much in terms of *behavior* (only the *enum.ToString()* method
// behavior changes when an enumeration is tagged with *FlagsAttribute*)
// but in terms of *meaning*: is the enumeration a **range of values**
// or a **range of flags**?
//
// As a consequence, changing the *FlagsAttribute*s status of an enumeration can
// have significant impact for its clients.
//</Description>
//<HowToFix>
// Make sure the *FlagsAttribute* status of each enumeration remains stable
// now, and in the future.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>API: New publicly visible types</Name>
from t in Application.Types
where t.IsPubliclyVisible &&
// The type has been removed and its parent assembly hasn't been removed …
( (t.WasAdded() && !t.ParentAssembly.WasAdded()) ||
// … or the type existed but was not publicly visible
!t.WasAdded() && !t.OlderVersion().IsPubliclyVisible)
select new {
t,
OldVisibility =
(t.WasAdded() ? " " :
t.OlderVersion().Visibility.ToString())
}
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists types that are new in the public
// surface of the analyzed assemblies.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>API: New publicly visible methods</Name>
from m in Application.Methods
where m.IsPubliclyVisible &&
// The method has been removed and its parent assembly hasn'm been removed …
( (m.WasAdded() && !m.ParentType.WasAdded()) ||
// … or the t existed but was not publicly visible
!m.WasAdded() && !m.OlderVersion().IsPubliclyVisible)
select new {
m,
OldVisibility =
(m.WasAdded() ? " " :
m.OlderVersion().Visibility.ToString())
}
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists methods that are new in the public
// surface of the analyzed assemblies.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>API: New publicly visible fields</Name>
from f in Application.Fields
where f.IsPubliclyVisible &&
// The method has been removed and its parent assembly hasn'f been removed …
( (f.WasAdded() && !f.ParentType.WasAdded()) ||
// … or the t existed but was not publicly visible
!f.WasAdded() && !f.OlderVersion().IsPubliclyVisible)
select new {
f,
OldVisibility =
(f.WasAdded() ? " " :
f.OlderVersion().Visibility.ToString())
}
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists fields that are new in the public
// surface of the analyzed assemblies.
//</Description>]]></Query>
</Group>
<Group Name="Code Diff Summary" Active="True" ShownInReport="True">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>New assemblies</Name>
from a in Application.Assemblies where a.WasAdded()
select new { a, a.NbLinesOfCode }
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *assemblies* that have been added since the *baseline*.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Assemblies removed</Name>
from a in codeBase.OlderVersion().Application.Assemblies where a.WasRemoved()
select new { a, a.NbLinesOfCode }
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *assemblies* that have been removed since the *baseline*.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Assemblies where code was changed</Name>
from a in Application.Assemblies where a.CodeWasChanged()
select new {
a,
a.NbLinesOfCode,
oldNbLinesOfCode = a.OlderVersion().NbLinesOfCode.GetValueOrDefault() ,
delta = (int) a.NbLinesOfCode.GetValueOrDefault() - a.OlderVersion().NbLinesOfCode.GetValueOrDefault()
}
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *assemblies* in which, code has been changed since the *baseline*.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>New namespaces</Name>
from n in Application.Namespaces where
!n.ParentAssembly.WasAdded() &&
n.WasAdded()
select new { n, n.NbLinesOfCode }
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *namespaces* that have been added since the *baseline*.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Namespaces removed</Name>
from n in codeBase.OlderVersion().Application.Namespaces where
!n.ParentAssembly.WasRemoved() &&
n.WasRemoved()
select new { n, n.NbLinesOfCode }
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *namespaces* that have been removed since the *baseline*.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Namespaces where code was changed</Name>
from n in Application.Namespaces where n.CodeWasChanged()
select new {
n,
n.NbLinesOfCode,
oldNbLinesOfCode = n.OlderVersion().NbLinesOfCode.GetValueOrDefault() ,
delta = (int) n.NbLinesOfCode.GetValueOrDefault() - n.OlderVersion().NbLinesOfCode.GetValueOrDefault()
}
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *namespaces* in which, code has been changed since the *baseline*.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>New types</Name>
from t in Application.Types where
!t.ParentNamespace.WasAdded() &&
t.WasAdded() &&
!t.IsGeneratedByCompiler
select new { t, t.NbLinesOfCode }
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *types* that have been added since the *baseline*.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types removed</Name>
from t in codeBase.OlderVersion().Application.Types where
!t.ParentNamespace.WasRemoved() &&
t.WasRemoved() &&
!t.IsGeneratedByCompiler
select new { t, t.NbLinesOfCode }
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *types* that have been removed since the *baseline*.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types where code was changed</Name>
from t in Application.Types where t.CodeWasChanged()
//select new { t, t.NbLinesOfCode }
select new {
t,
t.NbLinesOfCode,
oldNbLinesOfCode = t.OlderVersion().NbLinesOfCode ,
delta = (int?) t.NbLinesOfCode - t.OlderVersion().NbLinesOfCode
}
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *types* in which, code has been changed since the *baseline*.
//
// To visualize changes in code, right-click a matched type and select:
//
// • Compare older and newer versions of source file
//
// • Compare older and newer versions disassembled with Reflector
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Heuristic to find types moved from one namespace or assembly to another</Name>
let typesRemoved = codeBase.OlderVersion().Types.Where(t => t.WasRemoved())
let typesAdded = Types.Where(t => t.WasAdded())
from tMoved in typesAdded.Join(
typesRemoved,
t => t.Name,
t => t.Name,
(tNewer, tOlder) => new { tNewer,
OlderParentNamespace = tOlder.ParentNamespace,
OlderParentAssembly = tOlder.ParentAssembly } )
select tMoved
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *types* moved from one namespace or assembly to another.
// The heuristic implemented consists in making a **join LINQ query** on
// type name (without namespace prefix), applied to the two sets of types *added*
// and types *removed*.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types directly using one or several types changed</Name>
let typesChanged = Application.Types.Where(t => t.CodeWasChanged()).ToHashSet()
from t in JustMyCode.Types.UsingAny(typesChanged) where
!t.CodeWasChanged() &&
!t.WasAdded()
let typesChangedUsed = t.TypesUsed.Intersect(typesChanged)
select new { t, typesChangedUsed }
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists types *unchanged* since the *baseline*
// but that use directly some *types* where code has been changed
// since the *baseline*.
//
// For such matched type, the code hasen't been changed, but still the overall
// behavior might have been changed.
//
// The query result includes types changed directly used,
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types indirectly using one or several types changed</Name>
let typesChanged = Application.Types.Where(t => t.CodeWasChanged()).ToHashSet()
// 'depth' represents a code metric defined on types using
// directly or indirectly any type where code was changed.
let depth = JustMyCode.Types.DepthOfIsUsingAny(typesChanged)
from t in depth.DefinitionDomain where
!t.CodeWasChanged() &&
!t.WasAdded()
let typesChangedDirectlyUsed = t.TypesUsed.Intersect(typesChanged)
let depthOfUsingTypesChanged = depth[t]
orderby depthOfUsingTypesChanged
select new {
t,
depthOfUsingTypesChanged,
typesChangedDirectlyUsed
}
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists types *unchanged* since the *baseline*
// but that **use directly or indirectly** some *types* where
// code has been changed since the *baseline*.
//
// For such matched type, the code hasen't been changed, but still the overall
// behavior might have been changed.
//
// The query result includes types changed directly used, and the **depth of usage**
// of types indirectly used, *depth of usage* as defined in the documentation of
// *DepthOfIsUsingAny()* NDepend API method:
// http://www.ndepend.com/api/webframe.html?NDepend.API~NDepend.CodeModel.ExtensionMethodsSequenceUsage~DepthOfIsUsingAny.html
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>New methods</Name>
from m in Application.Methods where
!m.ParentType.WasAdded() &&
m.WasAdded() &&
!m.IsGeneratedByCompiler
select new { m, m.NbLinesOfCode }
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *methods* that have been added since the *baseline*.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods removed</Name>
from m in codeBase.OlderVersion().Application.Methods where
!m.ParentType.WasRemoved() &&
m.WasRemoved() &&
!m.IsGeneratedByCompiler
select new { m, m.NbLinesOfCode }
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *methods* that have been removed since the *baseline*.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods where code was changed</Name>
from m in Application.Methods where m.CodeWasChanged()
select new {
m,
m.NbLinesOfCode,
oldNbLinesOfCode = m.OlderVersion().NbLinesOfCode ,
delta = (int?) m.NbLinesOfCode - m.OlderVersion().NbLinesOfCode
}
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *methods* in which, code has been changed since the *baseline*.
//
// To visualize changes in code, right-click a matched method and select:
//
// • Compare older and newer versions of source file
//
// • Compare older and newer versions disassembled with Reflector
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods directly calling one or several methods changed</Name>
let methodsChanged = Application.Methods.Where(m => m.CodeWasChanged()).ToHashSet()
from m in JustMyCode.Methods.UsingAny(methodsChanged ) where
!m.CodeWasChanged() &&
!m.WasAdded()
let methodsChangedCalled = m.MethodsCalled.Intersect(methodsChanged)
select new {
m,
methodsChangedCalled
}
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists methods *unchanged* since the *baseline*
// but that call directly some *methods* where code has been changed
// since the *baseline*.
//
// For such matched method, the code hasen't been changed, but still the overall
// behavior might have been changed.
//
// The query result includes methods changed directly used,
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods indirectly calling one or several methods changed</Name>
let methodsChanged = Application.Methods.Where(m => m.CodeWasChanged()).ToHashSet()
// 'depth' represents a code metric defined on methods using
// directly or indirectly any method where code was changed.
let depth = JustMyCode.Methods.DepthOfIsUsingAny(methodsChanged)
from m in depth.DefinitionDomain where
!m.CodeWasChanged() &&
!m.WasAdded()
let methodsChangedDirectlyUsed = m.MethodsCalled.Intersect(methodsChanged)
let depthOfUsingMethodsChanged = depth[m]
orderby depthOfUsingMethodsChanged
select new {
m,
depthOfUsingMethodsChanged,
methodsChangedDirectlyUsed
}
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists methods *unchanged* since the *baseline*
// but that **use directly or indirectly** some *methods* where
// code has been changed since the *baseline*.
//
// For such matched method, the code hasen't been changed, but still the overall
// behavior might have been changed.
//
// The query result includes methods changed directly used, and the **depth of usage**
// of methods indirectly used, *depth of usage* as defined in the documentation of
// *DepthOfIsUsingAny()* NDepend API method:
// http://www.ndepend.com/api/webframe.html?NDepend.API~NDepend.CodeModel.ExtensionMethodsSequenceUsage~DepthOfIsUsingAny.html
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>New fields</Name>
from f in Application.Fields where
!f.ParentType.WasAdded() &&
f.WasAdded() &&
!f.IsGeneratedByCompiler
select f
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *fields* that have been added since the *baseline*.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Fields removed</Name>
from f in codeBase.OlderVersion().Application.Fields where
!f.ParentType.WasRemoved() &&
f.WasRemoved() &&
!f.IsGeneratedByCompiler
select f
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *fields* that have been removed since the *baseline*.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Third party types that were not used and that are now used</Name>
from t in ThirdParty.Types where t.IsUsedRecently()
select new {
t,
t.Methods,
t.Fields,
t.TypesUsingMe
}
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *types* defined in **third-party assemblies**, that were not
// used at *baseline* time, and that are now used.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Third party types that were used and that are not used anymore</Name>
from t in codeBase.OlderVersion().Types where t.IsNotUsedAnymore()
select new {
t,
t.Methods,
t.Fields,
TypesThatUsedMe = t.TypesUsingMe
}
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *types* defined in **third-party assemblies**, that were
// used at *baseline* time, and that are not used anymore.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Third party methods that were not used and that are now used</Name>
from m in ThirdParty.Methods where
m.IsUsedRecently() &&
!m.ParentType.IsUsedRecently()
select new {
m,
m.MethodsCallingMe
}
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *methods* defined in **third-party assemblies**, that were not
// used at *baseline* time, and that are now used.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Third party methods that were used and that are not used anymore</Name>
from m in codeBase.OlderVersion().Methods where
m.IsNotUsedAnymore() &&
!m.ParentType.IsNotUsedAnymore()
select new {
m,
MethodsThatCalledMe = m.MethodsCallingMe
}
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *methods* defined in **third-party assemblies**, that were
// used at *baseline* time, and that are not used anymore.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Third party fields that were not used and that are now used</Name>
from f in ThirdParty.Fields where
f.IsUsedRecently() &&
!f.ParentType.IsUsedRecently()
select new {
f,
f.MethodsUsingMe
}
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *fields* defined in **third-party assemblies**, that were not
// used at *baseline* time, and that are now used.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Third party fields that were used and that are not used anymore</Name>
from f in codeBase.OlderVersion().Fields where
f.IsNotUsedAnymore() &&
!f.ParentType.IsNotUsedAnymore()
select new {
f,
MethodsThatUsedMe = f.MethodsUsingMe
}
//<Description>
// This query is executed only if a *baseline for comparison* is defined (*diff mode*).
//
// This code query lists *fields* defined in **third-party assemblies**, that were
// used at *baseline* time, and that are not used anymore.
//</Description>]]></Query>
</Group>
<Group Name="Test and Code Coverage" Active="True" ShownInReport="True">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>C.R.A.P method code metric</Name>
warnif count > 0
from m in JustMyCode.Methods
// Don't match too short methods
where m.NbLinesOfCode > 10
let CC = m.CyclomaticComplexity
let uncov = (100 - m.PercentageCoverage) / 100f
let CRAP = (CC * CC * uncov * uncov * uncov) + CC
where CRAP != null && CRAP > 30
orderby CRAP descending, m.NbLinesOfCode descending
select new { m, CRAP, CC, m.PercentageCoverage, m.NbLinesOfCode }
//<Description>
// This rule is executed only if some code coverage data is imported
// from some code coverage files.
//
// **Change Risk Analyzer and Predictor** (i.e. CRAP) is a code metric
// that helps in pinpointing overly complex and untested code.
// Is has been first defined here:
// http://www.artima.com/weblogs/viewpost.jsp?thread=215899
//
// The Formula is: **CRAP(m) = CC(m)^2 * (1 cov(m)/100)^3 + CC(m)**
//
// • where *CC(m)* is the *cyclomatic complexity* of the method *m*
//
// • and *cov(m)* is the *percentage coverage* by tests of the method *m*
//
// Matched methods cumulates two highly *error prone* code smells:
//
// • A complex method, difficult to develop and maintain.
//
// • Non 100% covered code, difficult to refactor without introducing any regression bug.
//
// The highest the CRAP score, the more painful to maintain and error prone is the method.
//
// An arbitrary threshold of 30 is fixed for this code rule as suggested by inventors.
//
// Notice that no amount of testing will keep methods with a Cyclomatic Complexity
// highest than 30, out of CRAP territory.
//
// Notice that this rule doesn't match too short method
// with less than 10 lines of code.
//</Description>
//<HowToFix>
// In such situation, it is recommended to both refactor the complex method logic
// into several smaller and less complex methods
// (that might belong to some new types especially created),
// and also write unit-tests to full cover the refactored logic.
//
// You'll find code impossible to cover by unit-tests, like calls to *MessageBox.Show()*.
// An infrastructure must be defined to be able to *mock* such code at test-time.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Complex methods partially covered by tests should be 100% covered</Name>
warnif count > 0 from m in JustMyCode.Methods
where
// These metrics' definitions are available here:
// http://www.ndepend.com/docs/code-metrics#MetricsOnMethods
( m.NbLinesOfCode > 30 ||
m.ILCyclomaticComplexity > 50 ||
m.ILNestingDepth > 4 ||
m.NbVariables > 8) &&
// Take care only of complex methods
// already partially covered, but not completely covered.
m.PercentageCoverage > 0 &&
m.PercentageCoverage < 100
orderby m.NbLinesOfCodeNotCovered ascending,
m.NbLinesOfCode descending
select new { m, m.PercentageCoverage, m.NbLinesOfCode,
m.NbLinesOfCodeCovered, m.NbLinesOfCodeNotCovered,
m.ILCyclomaticComplexity, m.ILNestingDepth, m.NbVariables }
//<Description>
// This rule is executed only if some code coverage data is imported
// from some code coverage files.
//
// There are default rules that warn about complex methods to refactor.
// The present rule warns about complex methods that are already a bit covered by tests
// but not 100% covered by tests.
//
// Such situation cumulates two highly *error prone* code smells:
//
// • A complex method, difficult to develop and maintain.
//
// • Non 100% covered code, difficult to refactor without creating any regression bug.
//
// Because the complex method is already covered partially by tests,
// it might be not so costly to write more tests to full cover it.
//</Description>
//<HowToFix>
// In such situation, it is recommended to both:
//
// • refactor the complex method logic
// into several smaller and less complex methods
// (that might belong to some new types especially created),
//
// • and also write more unit-tests to full cover the refactored logic.
//
// You'll find code impossible to cover by unit-tests, like calls to *MessageBox.Show()*.
// An infrastructure must be defined to be able to *mock* such code at test-time.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Method changed poorly covered</Name>
warnif count > 0
from m in Application.Methods where
m.PercentageCoverage < 30 &&
m.CodeWasChanged()
orderby m.NbLinesOfCode descending,
m.NbLinesOfCodeNotCovered ,
m.PercentageCoverage
select new { m, m.PercentageCoverage, m.NbLinesOfCode,
m.NbLinesOfCodeNotCovered }
//<Description>
// This rule is executed only if a *baseline for comparison* is defined (*diff mode*).
// This rule operates only on methods added or refactored since the baseline.
//
// This rule is executed only if some code coverage data is imported
// from some code coverage files.
//
// It is important to write code mostly covered by tests
// to achieve *maintainable* and *non-error-prone* code.
//
// In real-world, many code bases are poorly covered by tests.
// However it is not practicable to stop the development for months
// to refactor and write tests to achieve high code coverage ratio.
//
// Hence it is recommended that each time a method (or a type) gets refactored,
// the developer takes the time to write associated unit-tests to cover it.
//
// Doing so will help to increase significantly the maintainability of the code base.
// You'll notice that quickly, refactoring will also be driven by testability,
// and as a consequence, the overall code structure and design will increase as well.
//</Description>
//<HowToFix>
// Write unit-tests to cover the code of most methods and classes refactored.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Method added poorly covered</Name>
warnif count > 0
from m in Application.Methods where
m.NbLinesOfCode > 0 &&
m.PercentageCoverage < 30 &&
m.WasAdded()
orderby m.NbLinesOfCode descending,
m.NbLinesOfCodeNotCovered ,
m.PercentageCoverage
select new { m, m.PercentageCoverage, m.NbLinesOfCode,
m.NbLinesOfCodeNotCovered }
//<Description>
// This rule is executed only if a *baseline for comparison* is defined (*diff mode*).
// This rule operates only on methods added or refactored since the baseline.
//
// This rule is executed only if some code coverage data is imported
// from some code coverage files.
//
// It is important to write code mostly covered by tests
// to achieve *maintainable* and *non-error-prone* code.
//
// In real-world, many code bases are poorly covered by tests.
// However it is not practicable to stop the development for months
// to refactor and write tests to achieve high code coverage ratio.
//
// Hence it is recommended that each time a method (or a type) gets added,
// the developer takes the time to write associated unit-tests to cover it.
//
// Doing so will help to increase significantly the maintainability of the code base.
// You'll notice that quickly, refactoring will also be driven by testability,
// and as a consequence, the overall code structure and design will increase as well.
//</Description>
//<HowToFix>
// Write unit-tests to cover the code of most methods and classes added.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types 95% to 99% covered</Name>
warnif count > 0
from t in Application.Types where
t.PercentageCoverage >= 95 &&
t.PercentageCoverage <= 99 &&
!t.IsGeneratedByCompiler
let methodsCulprit = t.Methods.Where(m => m.PercentageCoverage < 100)
orderby t.NbLinesOfCode descending ,
t.NbLinesOfCodeNotCovered ,
t.PercentageCoverage
select new { t, t.PercentageCoverage, t.NbLinesOfCode,
t.NbLinesOfCodeNotCovered, methodsCulprit }
//<Description>
// This rule is executed only if some code coverage data is imported
// from some code coverage files.
//
// Often covering the few percents of remaining uncovered code of a class,
// requires as much work as covering the first 90%.
// For this reason, often teams estimate that 90% coverage is enough.
// However *untestable code* usually means *poorly written code*
// which usually leads to *error prone code*.
// So it might be worth refactoring and making sure to cover the few uncovered lines of code
// **because most tricky bugs might come from this small portion of hard-to-test code**.
//
// Not all classes should be 100% covered by tests (like UI code can be hard to test)
// but you should make sure that most of the logic of your application
// is defined in some *easy-to-test classes*, 100% covered by tests.
//</Description>
//<HowToFix>
// Write more unit-tests dedicated to cover code not covered yet.
// If you find some *hard-to-test code*, it is certainly a sign that this code
// is not *well designed* and hence, needs refactoring.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Namespaces 95% to 99% covered</Name>
warnif count > 0
from n in Application.Namespaces where
n.PercentageCoverage >= 95 &&
n.PercentageCoverage <= 99
let methodsCulprit = n.ChildMethods.Where(m => m.PercentageCoverage < 100)
orderby n.NbLinesOfCode descending ,
n.NbLinesOfCodeNotCovered ,
n.PercentageCoverage
select new { n, n.PercentageCoverage, n.NbLinesOfCode,
n.NbLinesOfCodeNotCovered, methodsCulprit }
//<Description>
// This rule is executed only if some code coverage data is imported
// from some code coverage files.
//
// Often covering the few percents of remaining uncovered code of
// one or several classes in a namespace
// requires as much work as covering the first 90%.
// For this reason, often teams estimate that 90% coverage is enough.
// However *untestable code* usually means *poorly written code*
// which usually leads to *error prone code*.
// So it might be worth refactoring and making sure to cover the few uncovered lines of code
// **because most tricky bugs might come from this small portion of hard-to-test code**.
//
// Not all classes should be 100% covered by tests (like UI code can be hard to test)
// but you should make sure that most of the logic of your application
// is defined in some *easy-to-test classes*, 100% covered by tests.
//</Description>
//<HowToFix>
// Write more unit-tests dedicated to cover code not covered yet in the namespace.
// If you find some *hard-to-test code*, it is certainly a sign that this code
// is not *well designed* and hence, needs refactoring.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types tagged with FullCoveredAttribute should be 100% covered</Name>
warnif count > 0
from t in Application.Types where
t.HasAttribute ("NDepend.Attributes.FullCoveredAttribute".AllowNoMatch()) &&
t.PercentageCoverage < 100
let notFullCoveredMethods = t.Methods.Where(
m => m.NbLinesOfCode> 0 &&
m.PercentageCoverage < 100 &&
!m.HasAttribute("NDepend.Attributes.UncoverableByTestAttribute".AllowNoMatch()))
orderby t.NbLinesOfCodeNotCovered descending
select new { t, t.PercentageCoverage, t.NbLinesOfCodeNotCovered, notFullCoveredMethods,
t.NbLinesOfCode, t.NbLinesOfCodeCovered }
//<Description>
// This rule is executed only if some code coverage data is imported
// from some code coverage files.
//
// By using a **FullCoveredAttribute**, you can express in source code the intention
// that a class is 100% covered by tests, and should remain 100% covered in the future.
// If you don't want to link *NDepend.API.dll*,
// you can use your own attribute and adapt the source code of this rule.
//
// Benefits of using a **FullCoveredAttribute** are twofold:
// Not only the intention is expressed in source code,
// but it is also continuously checked by the present rule.
//
// Often covering 10% of remaining uncovered code of a class,
// requires as much work as covering the first 90%.
// For this reason, often teams estimate that 90% coverage is enough.
// However *untestable code* usually means *poorly written code* which usually means *error prone code*.
// So it might be worth refactoring and making sure to cover the 10% remaining code
// **because most tricky bugs might come from this small portion of hard-to-test code**.
//
// Not all classes should be 100% covered by tests (like UI code can be hard to test)
// but you should make sure that most of the logic of your application
// is defined in some *easy-to-test classes*, 100% covered by tests.
//</Description>
//<HowToFix>
// Write more unit-tests dedicated to cover code of matched classes not covered yet.
// If you find some *hard-to-test code*, it is certainly a sign that this code
// is not *well designed* and hence, needs refactoring.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types 100% covered should be tagged with FullCoveredAttribute</Name>
warnif count > 0 from t in JustMyCode.Types where
!t.HasAttribute ("NDepend.Attributes.FullCoveredAttribute".AllowNoMatch()) &&
t.PercentageCoverage == 100 &&
!t.IsGeneratedByCompiler
select new { t, t.NbLinesOfCode }
//<Description>
// This rule is executed only if some code coverage data is imported
// from some code coverage files.
//
// By using a **FullCoveredAttribute**, you can express in source code the intention
// that a class is 100% covered by tests, and should remain 100% covered in the future.
//
// Benefits of using a **FullCoveredAttribute** are twofold:
// Not only the intention is expressed in source code,
// but it is also continuously checked by the present rule.
//</Description>
//<HowToFix>
// Just tag types 100% covered by tests with the **FullCoveredAttribute**
// that can be found in *NDepend.API.dll*,
// or by an attribute of yours defined in your own code
// (in which case this rule must be adapted).
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types not covered at all</Name>
from t in Application.Types where
t.PercentageCoverage == 0
orderby t.NbLinesOfCode descending
select new { t, t.NbLinesOfCode }
//<Description>
// This query is executed only if some code coverage data is imported
// from some code coverage files.
//
// This code query lists types not covered at all by any test.
//
// Often, when code is not covered at all by test, one can reach
// decent coverage ratio (like 50% to 90%) just by writing a few tests.
//
// The idea is not to cover code for the sake of it.
// The idea is that thanks to a small effort of writing a few tests,
// one can continuously check, that a significant portion of code
// runs without a problem.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Namespaces not covered at all</Name>
from n in Application.Namespaces where
n.PercentageCoverage == 0
orderby n.NbLinesOfCode descending
select new { n, n.NbLinesOfCode}
//<Description>
// This query is executed only if some code coverage data is imported
// from some code coverage files.
//
// This code query lists namespaces not covered at all by any test.
//
// Often, when code is not covered at all by test, one can reach
// decent coverage ratio (like 50% to 90%) just by writing a few tests.
//
// The idea is not to cover code for the sake of it.
// The idea is that thanks to a small effort of writing a few tests,
// one can continuously check, that a significant portion of code
// runs without a problem.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Test Methods</Name>
let testAttr = ThirdParty.Types.WithNameIn("TestAttribute", "TestCaseAttribute")
let testMethods = Methods.TaggedWithAnyAttributes(testAttr)
from m in testMethods
select m
//<Description>
// We advise to not include test assemblies in code analyzed by NDepend.
// We estimate that it is acceptable and practical to lower the quality gate of test code,
// because the important measures for tests are:
//
// • The coverage ratio,
//
// • And the amount of logic results asserted: This includes both
// assertions in test code, and assertions in code covered by tests,
// like *Code Contract* assertions and *Debug.Assert(…)* assertions.
//
// But if you wish to enforce the quality of test code, you'll need to
// consider test assemblies in your list of application assemblies
// analyzed by NDepend.
//
// In such situation, this code query lists tests methods and you can
// reuse this code in custom rules.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods directly called by test Methods</Name>
let testAttr = ThirdParty.Types.WithNameIn("TestAttribute", "TestCaseAttribute")
let testMethods = Methods.TaggedWithAnyAttributes(testAttr).ToHashSet()
// --- Uncomment this line if your test methods are in dedicated test assemblies ---
//let testAssemblies = testMethods.ParentAssemblies().ToHashSet()
from m in Application.Methods.UsedByAny(testMethods)
// --- Uncomment this line if your test methods are in dedicated test assemblies ---
//where !testAssemblies.Contains(m.ParentAssembly)
select new { m ,
calledByTests = m.MethodsCallingMe.Intersect(testMethods ),
// --- Uncomment this line if your project import some coverage data ---
// m.PercentageCoverage
}
//<Description>
// This query lists all methods directly called by tests methods.
// Overrides of virtual and abstract methods, called through polymorphism, are not listed.
// Methods solely invoked through a delegate are not listed.
// Methods solely invoked through reflection are not listed.
//
// We advise to not include test assemblies in code analyzed by NDepend.
// We estimate that it is acceptable and practical to lower the quality gate of test code,
// because the important measures for tests are:
//
// • The coverage ratio,
//
// • And the amount of logic results asserted: This includes both
// assertions in test code, and assertions in code covered by tests,
// like *Code Contract* assertions and *Debug.Assert(…)* assertions.
//
// But if you wish to run this code query,
// you'll need to consider test assemblies in your list of
// application assemblies analyzed by NDepend.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods directly and indirectly called by test Methods</Name>
let testAttr = from t in ThirdParty.Types.WithNameIn("TestAttribute", "TestCaseAttribute") select t
let testMethods = Methods.TaggedWithAnyAttributes(testAttr)
// --- Uncomment this line if your test methods are in dedicated test assemblies ---
// let testAssemblies = testMethods.ParentAssemblies().ToHashSet()
let depthOfCalledByTest = Application.Methods.DepthOfIsUsedByAny(testMethods)
from pair in depthOfCalledByTest
where pair.Value > 0
orderby pair.Value ascending
// --- Uncomment this line if your test methods are in dedicated test assemblies ---
//&& !testAssemblies.Contains(pair.CodeElement.ParentAssembly)
select new {
method = pair.CodeElement,
// (depthOfCalledByTests == 1) means that the method is directly called by tests
// (depthOfCalledByTests == 2) means that the method is directly called by a method directly called by tests
// …
depthOfCalledByTests = pair.Value,
nbLinesOfCode = pair.CodeElement.NbLinesOfCode,
// --- Uncomment this line if your project import some coverage data ---
// m.PercentageCoverage
}
//<Description>
// This query lists all methods *directly or indirectly* called by tests methods.
// *Indirectly* called by a test means that a test method calls a method, that calls a method…
// From this recursion, a code metric named *depthOfCalledByTests* is inferred,
// The value *1* means directly called by test,
// the value *2* means called by a method that is called by a test…
//
// Overrides of virtual and abstract methods, called through polymorphism, are not listed.
// Methods solely invoked through a delegate are not listed.
// Methods solely invoked through reflection are not listed.
//
// We advise to not include test assemblies in code analyzed by NDepend.
// We estimate that it is acceptable and practical to lower the quality gate of test code,
// because the important measures for tests are:
//
// • The coverage ratio,
//
// • And the amount of logic results asserted: This includes both
// assertions in test code, and assertions in code covered by tests,
// like *Code Contract* assertions and *Debug.Assert(…)* assertions.
//
// But if you wish to run this code query,
// you'll need to consider test assemblies in your list of
// application assemblies analyzed by NDepend.
//</Description>]]></Query>
</Group>
<Group Name="Dead Code" Active="True" ShownInReport="True">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Potentially dead Types</Name>
warnif count > 0
// Filter procedure for types that should'nt be considered as dead
let canTypeBeConsideredAsDeadProc = new Func<IType, bool>(
t => !t.IsPublic && // Public types might be used by client applications of your assemblies.
t.Name != "Program" &&
!t.IsGeneratedByCompiler &&
// If you don't want to link NDepend.API.dll, you can use your own IsNotDeadCodeAttribute
// and adapt the source code of this rule.
!t.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) &&
// Exclude static types that define only const fields
// because they cannot be seen as used in IL code.
!(t.IsStatic && t.NbMethods == 0 && !t.Fields.Where(f => !f.IsLiteral).Any()))
// Select types unused
let typesUnused =
from t in JustMyCode.Types where
t.NbTypesUsingMe == 0 && canTypeBeConsideredAsDeadProc(t)
select t
// Dead types = types used only by unused types (recursive)
let deadTypesMetric = typesUnused.FillIterative(
types => from t in codeBase.Application.Types.UsedByAny(types).Except(types)
where canTypeBeConsideredAsDeadProc(t) &&
t.TypesUsingMe.Intersect(types).Count() == t.NbTypesUsingMe
select t)
from t in deadTypesMetric.DefinitionDomain
select new {
t,
depth = deadTypesMetric[t],
t.TypesUsingMe
}
//<Description>
// This rule lists *potentially* **dead types**.
// A dead type is a type that can be removed
// because it is never used by the program.
//
// This rule lists not only types not used anywhere in code,
// but also types used only by types not used anywhere in code.
// This is why this rule comes with a column *TypesusingMe* and
// this is why there is a code metric named *depth*:
//
// • A *depth* value of *0* means the type is not used.
//
// • A *depth* value of *1* means the type is used only by types not used.
//
// • etc…
//
// By reading the source code of this rule, you'll see that by default,
// *public* types are not matched, because such type might not be used
// by the analyzed code, but still be used by client code, not analyzed by NDepend.
// This default behavior can be easily changed.
//</Description>
//<HowToFix>
// *Static analysis* cannot provide an *exact* list of dead types,
// because there are several ways to use a type *dynamically* (like through reflection).
//
// For each type matched by this query, first investigate if the type is used somehow
// (like through reflection).
// If the type is really never used, it is important to remove it
// to avoid maintaining useless code.
// If you estimate the code of the type might be used in the future,
// at least comment it, and provide an explanatory comment about the future intentions.
//
// If a type is used somehow,
// but still is matched by this rule, you can tag it with the attribute
// **IsNotDeadCodeAttribute** found in *NDepend.API.dll* to avoid matching the type again.
// You can also provide your own attribute for this need,
// but then you'll need to adapt this code rule.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Potentially dead Methods</Name>
warnif count > 0
// Filter procedure for methods that should'nt be considered as dead
let canMethodBeConsideredAsDeadProc = new Func<IMethod, bool>(
m => !m.IsPubliclyVisible && // Public methods might be used by client applications of your assemblies.
!m.IsEntryPoint && // Main() method is not used by-design.
!m.IsExplicitInterfaceImpl && // The IL code never explicitly calls explicit interface methods implementation.
!m.IsClassConstructor && // The IL code never explicitly calls class constructors.
!m.IsFinalizer && // The IL code never explicitly calls finalizers.
!m.IsVirtual && // Only check for non virtual method that are not seen as used in IL.
!(m.IsConstructor && // Don't take account of protected ctor that might be call by a derived ctors.
m.IsProtected) &&
!m.IsEventAdder && // The IL code never explicitly calls events adder/remover.
!m.IsEventRemover &&
!m.IsGeneratedByCompiler &&
!m.ParentType.IsDelegate &&
// Methods tagged with these two attributes are called by the serialization infrastructure.
!m.HasAttribute("System.Runtime.Serialization.OnSerializingAttribute".AllowNoMatch()) &&
!m.HasAttribute("System.Runtime.Serialization.OnDeserializedAttribute".AllowNoMatch()) &&
// If you don't want to link NDepend.API.dll, you can use your own IsNotDeadCodeAttribute
// and adapt the source code of this rule.
!m.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()))
// Get methods unused
let methodsUnused =
from m in JustMyCode.Methods where
m.NbMethodsCallingMe == 0 &&
canMethodBeConsideredAsDeadProc(m)
select m
// Dead methods = methods used only by unused methods (recursive)
let deadMethodsMetric = methodsUnused.FillIterative(
methods => // Unique loop, just to let a chance to build the hashset.
from o in (new object()).ToEnumerable()
// Use a hashet to make Intersect calls much faster!
let hashset = methods.ToHashSet()
from m in codeBase.Application.Methods.UsedByAny(methods).Except(methods)
where canMethodBeConsideredAsDeadProc(m) &&
// Select methods called only by methods already considered as dead
hashset.Intersect(m.MethodsCallingMe).Count() == m.NbMethodsCallingMe
select m)
from m in JustMyCode.Methods.Intersect(deadMethodsMetric.DefinitionDomain)
select new {
m,
depth = deadMethodsMetric[m],
m.MethodsCallingMe
}
//<Description>
// This rule lists *potentially* **dead methods**.
// A dead method is a method that can be removed
// because it is never called by the program.
//
// This rule lists not only methods not called anywhere in code,
// but also methods called only by methods not called anywhere in code.
// This is why this rule comes with a column *MethodsCallingMe* and
// this is why there is a code metric named *depth*:
//
// • A *depth* value of *0* means the method is not called.
//
// • A *depth* value of *1* means the method is called only by methods not called.
//
// • etc…
//
// By reading the source code of this rule, you'll see that by default,
// *public* methods are not matched, because such method might not be called
// by the analyzed code, but still be called by client code, not analyzed by NDepend.
// This default behavior can be easily changed.
//</Description>
//<HowToFix>
// *Static analysis* cannot provide an *exact* list of dead methods,
// because there are several ways to invoke a method *dynamically* (like through reflection).
//
// For each method matched by this query, first investigate if the method is invoked somehow
// (like through reflection).
// If the method is really never invoked, it is important to remove it
// to avoid maintaining useless code.
// If you estimate the code of the method might be used in the future,
// at least comment it, and provide an explanatory comment about the future intentions.
//
// If a method is invoked somehow,
// but still is matched by this rule, you can tag it with the attribute
// **IsNotDeadCodeAttribute** found in *NDepend.API.dll* to avoid matching the method again.
// You can also provide your own attribute for this need,
// but then you'll need to adapt this code rule.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Potentially dead Fields</Name>
warnif count > 0
from f in JustMyCode.Fields where
f.NbMethodsUsingMe == 0 &&
!f.IsPublic && // Although not recommended, public fields might be used by client applications of your assemblies.
!f.IsLiteral && // The IL code never explicitly uses literal fields.
!f.IsEnumValue && // The IL code never explicitly uses enumeration value.
f.Name != "value__" && // Field named 'value__' are relative to enumerations and the IL code never explicitly uses them.
!f.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) &&
!f.IsGeneratedByCompiler
// If you don't want to link NDepend.API.dll, you can use your own IsNotDeadCodeAttribute
// and adapt the source code of this rule.
select f
//<Description>
// This rule lists *potentially* **dead fields**.
// A dead field is a field that can be removed
// because it is never used by the program.
//
// By reading the source code of this rule, you'll see that by default,
// *public* fields are not matched, because such field might not be used
// by the analyzed code, but still be used by client code, not analyzed by NDepend.
// This default behavior can be easily changed.
// Some others default rules in the *Visibility* group, warn about public fields.
//
// More restrictions are applied by this rule because of some *by-design* limitations.
// NDepend mostly analyzes compiled IL code, and the information that
// an enumeration value or a literal constant (which are fields) is used
// is lost in IL code. Hence by default this rule won't match such field.
//</Description>
//<HowToFix>
// *Static analysis* cannot provide an *exact* list of dead fields,
// because there are several ways to assign or read a field *dynamically*
// (like through reflection).
//
// For each field matched by this query, first investigate
// if the field is used somehow (like through reflection).
// If the field is really never used, it is important to remove it
// to avoid maintaining a useless code element.
//
// If a field is used somehow,
// but still is matched by this rule, you can tag it with the attribute
// **IsNotDeadCodeAttribute** found in *NDepend.API.dll*
// to avoid matching the field again.
// You can also provide your own attribute for this need,
// but then you'll need to adapt this code rule.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Wrong usage of IsNotDeadCodeAttribute</Name>
warnif count == 1
let tAttr = Types.WithFullName("NDepend.Attributes.IsNotDeadCodeAttribute").FirstOrDefault()
where tAttr != null
// Get types that do a wrong usage of IsNotDeadCodeAttribute
let types = from t in Application.Types where
t.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) &&
( // types used don't need to be tagged with IsNotDeadCodeAttribute!
t.TypesUsingMe.Count(t1 =>
!t.NestedTypes.Contains(t1) &&
!t1.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) ) > 0 ||
// Static types that define only const fields cannot be seen as used in IL code.
// They don't need to be tagged with IsNotDeadCodeAttribute.
(t.IsStatic && t.NbMethods == 0 && !t.Fields.Where(f => !f.IsLiteral).Any())
)
select t
// Get methods that do a wrong usage of IsNotDeadCodeAttribute
let methods = from m in Application.Methods where
m.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) &&
m.MethodsCallingMe.Count(m1 => !m1.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch())) > 0
select m
// Get fields that do a wrong usage of IsNotDeadCodeAttribute
let fields = from f in Application.Fields where
f.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) &&
f.MethodsUsingMe.Count(m1 => !m1.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch())) > 0
select f
where types.Count() > 0 || methods.Count() > 0 || fields.Count() > 0
select new { tAttr, types , methods, fields }
//<Description>
// The attribute **NDepend.Attributes.IsNotDeadCodeAttribute**
// is defined in *NDepend.API.dll*. This attribute is used
// to mean that a code element is not used directly, but is used
// somehow, like through reflection.
//
// This attribute is used in the dead code rules,
// *Potentially dead Types*, *Potentially dead Methods*
// and *Potentially dead Fields*.
// If you don't want to link *NDepend.API.dll*, you can use
// your own *IsNotDeadCodeAttribute* and adapt the source code of
// this rule, and the source code of the *dead code* rules.
//
// In this context, this code rule matches code elements
// (types, methods, fields) that are tagged with this attribute,
// but still used directly somewhere in the code.
//</Description>
//<HowToFix>
// Just remove *IsNotDeadCodeAttribute* tagging of
// types, methods and fields matched by this rule
// because this tag is not useful anymore.
//</HowToFix>]]></Query>
</Group>
<Group Name="Visibility" Active="True" ShownInReport="False">
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods that could have a lower visibility</Name>
warnif count > 0 from m in JustMyCode.Methods where
m.Visibility != m.OptimalVisibility &&
!m.HasAttribute("NDepend.Attributes.CannotDecreaseVisibilityAttribute".AllowNoMatch()) &&
!m.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) &&
// If you don't want to link NDepend.API.dll, you can use your own attributes
// and adapt the source code of this rule.
// Eliminate default constructor from the result.
// Whatever the visibility of the declaring class,
// default constructors are public and introduce noise
// in the current rule.
!( m.IsConstructor && m.IsPublic && m.NbParameters == 0) &&
// Don't decrease the visibility of Main() methods.
!m.IsEntryPoint
select new {
m,
m.Visibility ,
CouldBeDeclared = m.OptimalVisibility,
m.MethodsCallingMe
}
//<Description>
// This rule warns about methods that can be declared with a lower visibility
// without provoking any compilation error.
// For example *private* is a visibility lower than *internal*
// which is lower than *public*.
//
// **Narrowing visibility** is a good practice because doing so **promotes encapsulation**.
// The scope from which methods can be called is then reduced to a minimum.
//
// Notice that methods tagged with one of the attribute
// *NDepend.Attributes.CannotDecreaseVisibilityAttribute* or
// *NDepend.Attributes.IsNotDeadCodeAttribute*, found in *NDepend.API.dll*
// are not matched. If you don't want to link *NDepend.API.dll* but still
// wish to rely on this facility, you can declare these attributes in your code.
//</Description>
//<HowToFix>
// Declare each matched method with the specified *optimal visibility*
// in the *CouldBeDeclared* rule result column.
//
// By default, this rule matches *public methods*. If you are publishing an API
// many public methods matched should remain public. In such situation,
// you can opt for the *coarse solution* to this problem by adding in the
// rule source code *&& !m.IsPubliclyVisible* or you can prefer the
// *finer solution* by tagging each concerned method with
// *CannotDecreaseVisibilityAttribute*.
//</HowToFix>]]></Query>
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types that could have a lower visibility</Name>
warnif count > 0 from t in JustMyCode.Types where
t.Visibility != t.OptimalVisibility &&
// If you don't want to link NDepend.API.dll, you can use your own attributes
// and adapt the source code of this rule.
!t.HasAttribute("NDepend.Attributes.CannotDecreaseVisibilityAttribute".AllowNoMatch()) &&
!t.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) &&
// XML serialized type must remain public.
!t.HasAttribute("System.Xml.Serialization.XmlRootAttribute".AllowNoMatch()) &&
// Static types that define only const fields cannot be seen as used in IL code.
// They don't have to be tagged with CannotDecreaseVisibilityAttribute.
!( t.IsStatic &&
!t.Methods.Any(m => !m.IsClassConstructor) &&
!t.Fields.Any(f => !f.IsLiteral && !(f.IsStatic && f.IsInitOnly))) &&
// A type used by an interface that has the same visibility
// cannot have its visibility decreased, else a compilation error occurs!
!t.TypesUsingMe.Any(tUser =>
tUser.IsInterface &&
tUser.Visibility == t.Visibility) &&
// Don't change the visibility of a type that contain an entry point method.
!t.Methods.Any(m =>m.IsEntryPoint)
select new {
t,
t.Visibility ,
CouldBeDeclared = t.OptimalVisibility,
t.TypesUsingMe
}
//<Description>
// This rule warns about types that can be declared with a lower visibility
// without provoking any compilation error.
// For example *private* is a visibility lower than *internal*
// which is lower than *public*.
//
// **Narrowing visibility** is a good practice because doing so **promotes encapsulation**.
// The scope from which types can be consumed is then reduced to a minimum.
//
// Notice that types tagged with one of the attribute
// *NDepend.Attributes.CannotDecreaseVisibilityAttribute* or
// *NDepend.Attributes.IsNotDeadCodeAttribute*, found in *NDepend.API.dll*
// are not matched. If you don't want to link *NDepend.API.dll* but still
// wish to rely on this facility, you can declare these attributes in your code.
//</Description>
//<HowToFix>
// Declare each matched type with the specified *optimal visibility*
// in the *CouldBeDeclared* rule result column.
//
// By default, this rule matches *public types*. If you are publishing an API
// many public types matched should remain public. In such situation,
// you can opt for the *coarse solution* to this problem by adding in the
// rule source code *&& !m.IsPubliclyVisible* or you can prefer the
// *finer solution* by tagging each concerned type with
// *CannotDecreaseVisibilityAttribute*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Fields that could have a lower visibility</Name>
warnif count > 0 from f in JustMyCode.Fields where
f.Visibility != f.OptimalVisibility &&
!f.HasAttribute("NDepend.Attributes.CannotDecreaseVisibilityAttribute".AllowNoMatch()) &&
!f.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) &&
// If you don't want to link NDepend.API.dll, you can use your own attributes
// and adapt the source code of this rule.
// XML serialized fields must remain public.
!f.HasAttribute("System.Xml.Serialization.XmlElementAttribute".AllowNoMatch()) &&
!f.HasAttribute("System.Xml.Serialization.XmlAttributeAttribute".AllowNoMatch())
select new {
f,
f.Visibility ,
CouldBeDeclared = f.OptimalVisibility,
f.MethodsUsingMe
}
//<Description>
// This rule warns about fields that can be declared with a lower visibility
// without provoking any compilation error.
// For example *private* is a visibility lower than *internal*
// which is lower than *public*.
//
// **Narrowing visibility** is a good practice because doing so **promotes encapsulation**.
// The scope from which fields can be consumed is then reduced to a minimum.
//
// Notice that fields tagged with one of the attribute
// *NDepend.Attributes.CannotDecreaseVisibilityAttribute* or
// *NDepend.Attributes.IsNotDeadCodeAttribute*, found in *NDepend.API.dll*
// are not matched. If you don't want to link *NDepend.API.dll* but still
// wish to rely on this facility, you can declare these attributes in your code.
//</Description>
//<HowToFix>
// Declare each matched field with the specified *optimal visibility*
// in the *CouldBeDeclared* rule result column.
//
// By default, this rule matches *public fields*. If you are publishing an API
// some public fields matched should remain public. In such situation,
// you can opt for the *coarse solution* to this problem by adding in the
// rule source code *&& !m.IsPubliclyVisible* or you can prefer the
// *finer solution* by tagging eah concerned field with
// *CannotDecreaseVisibilityAttribute*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types that could be declared as private, nested in a parent type</Name>
warnif count > 0
from t in Application.Types
where !t.IsGeneratedByCompiler &&
!t.IsNested &&
!t.IsPubliclyVisible &&
!t.IsEnumeration &&
// Only one type user…
t.TypesUsingMe.Count() == 1
let couldBeNestedIn = t.TypesUsingMe.Single()
where !couldBeNestedIn.IsGeneratedByCompiler &&
// …declared in the same namespace
couldBeNestedIn.ParentNamespace == t.ParentNamespace
select new {
t,
couldBeNestedIn
}
//<Description>
// This rule matches types that can be potentially
// *nested* and declared *private* into another type.
//
// The conditions for a type to be potentially nested
// into a *parent type* are:
//
// • the *parent type* is the only type consuming it,
//
// • the type and the *parent type* are declared in the same namespace.
//
// Declaring a type as private into a parent type **promotes encapsulation**.
// The scope from which the type can be consumed is then reduced to a minimum.
//</Description>
//<HowToFix>
// Nest each matched *type* into the specified *parent type* and
// declare it as private.
//
// However *nested private types* are hardly testable. Hence this rule
// might not be applied to types consumed directly by tests.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid publicly visible constant fields</Name>
warnif count > 0
from f in Application.Fields
where f.IsLiteral &&
f.IsPubliclyVisible &&
!f.IsEnumValue
select f
//<Description>
// This rule warns about constant fields that are visible outside their
// parent assembly. Such field, when used from outside its parent assembly,
// has its constant value *hard-coded* into the client assembly.
// Hence, when changing the field's value, it is *mandatory* to recompile
// all assemblies that consume the field, else the program will run
// with different constant values in-memory. Certainly in such situation
// bugs are lurking.
//</Description>
//<HowToFix>
// Declare matched fields as **static readonly** instead of **constant**.
// This way, the field value is *safely changeable* without the need to
// recompile client assemblies.
//
// Notice that enumeration value fields suffer from the same *potential
// pitfall*. But enumeration values cannot be declared as
// *static readonly* hence the rule comes with the condition
// **&& !f.IsEnumValue** to avoid matching these. Unless you decide
// to banish public enumerations, just let the rule *as is*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Fields should be declared as private</Name>
warnif count > 0 from f in Application.Fields where
!f.IsPrivate &&
// These conditions filter cases where fields
// doesn't represent state that should be encapsulated.
!f.IsGeneratedByCompiler &&
!f.IsSpecialName &&
!f.IsInitOnly &&
!f.IsLiteral &&
!f.IsEnumValue
// A non-private field assigned from outside its class,
// usually leads to complicated field state management.
let outsideMethodsAssigningMe =
f.MethodsAssigningMe.Where(m => m.ParentType != f.ParentType)
select new {
f,
f.Visibility,
outsideMethodsAssigningMe
}
//<Description>
// This rule matches **non-private and mutable fields**.
// *Mutable* means that the field value can be modified.
// Typically mutable fields are *non-constant*,
// *non-readonly* fields.
//
// Fields should be considered as **implementation details**
// and as a consequence they should be declared as private.
//
// If something goes wrong with a *non-private field*,
// the culprit can be anywhere, and so in order to track down
// the bug, you may have to look at quite a lot of code.
//
// A private field, by contrast, can only be assigned from
// inside the same class, so if something goes wrong with that,
// there is usually only one source file to look at.
//</Description>
//<HowToFix>
// Declare a matched mutable field as *private*, or declare it
// as *readonly*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[//<Name>Constructors of abstract classes should be declared as protected or private</Name>
warnif count > 0
from t in Application.Types where
t.IsClass &&
t.IsAbstract
let ctors = t.Constructors.Where(c => !c.IsProtected && !c.IsPrivate)
where ctors.Count() > 0
select new { t, ctors }
//<Description>
// Constructors of abstract classes can only be called from derived
// classes.
//
// Because a public constructor is creating instances of its class,
// and it is forbidden to create instances of an *abstract* type,
// as a consequence an abstract class with a public constructor is
// wrong design.
//</Description>
//<HowToFix>
// To fix a violation of this rule,
// either declare the constructor as *protected*,
// or do not declare the type as *abstract*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[//<Name>Avoid public methods not publicly visible</Name>
warnif count > 0
from m in JustMyCode.Methods where
!m.IsPubliclyVisible && m.IsPublic &&
// Eliminate virtual methods
!m.IsVirtual &&
// Eliminate interface and delegate types
!m.ParentType.IsInterface &&
!m.ParentType.IsDelegate &&
// Eliminate default constructors
!(m.IsConstructor && m.NbParameters == 0) &&
// Eliminate operators that must be declared public
!m.IsOperator &&
// Eliminate methods generated by compiler
!m.IsGeneratedByCompiler
let calledOutsideParentType =
m.MethodsCallingMe.FirstOrDefault(mCaller => mCaller.ParentType != m.ParentType) != null
select new {
m,
parentTypeVisibility = m.ParentType.Visibility,
declareMethodAs = (Visibility) (calledOutsideParentType ? Visibility.Internal : Visibility.Private),
methodsCaller = m.MethodsCallingMe
}
//<Description>
// This rule warns about methods declared as *public*
// whose parent type is not declared as *public*.
//
// In such situation *public* means, *can be accessed
// from anywhere my parent type is visible*. Some
// developers think this is an elegant language construct,
// some others find it misleading.
//
// This rule can be deactivated if you don't agree with it.
// Read the whole debate here:
// http://ericlippert.com/2014/09/15/internal-or-public/
//</Description>
//<HowToFix>
// Declare the method as *internal* if it is used outside of
// its type, else declare it as *private*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods that should be declared as 'public' in C#, 'Public' in VB.NET</Name>
from m in Application.Methods where
m.ShouldBePublic
let usedInAssemblies = m.MethodsCallingMe.ParentAssemblies().Except(m.ParentAssembly)
select new { m, m.ParentAssembly, usedInAssemblies, m.MethodsCallingMe }
//<Description>
// This code query lists methods that *should* be declared
// as *public*. Such method is actually declared as *internal*
// and is consumed from outside its parent assembly
// thanks to the attribute
// *System.Runtime.CompilerServices.InternalsVisibleToAttribute*.
//
// This query relies on the property
// *NDepend.CodeModel.IMember.ShouldBePublic*
// http://www.ndepend.com/api/webframe.html?NDepend.API~NDepend.CodeModel.IMember~ShouldBePublic.html
//
// This is just a code query, it is not intended to advise
// you to declare the method as *public*, but to inform you
// that the code actually relies on the peculiar behavior
// of the attribute *InternalsVisibleToAttribute*.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Wrong usage of CannotDecreaseVisibilityAttribute</Name>
warnif count == 1
let tAttr = Types.WithFullName("NDepend.Attributes.CannotDecreaseVisibilityAttribute").FirstOrDefault()
where tAttr != null
// Get types that do a wrong usage of CannotDecreaseVisibilityAttribute
let types = from t in Application.Types where
t.HasAttribute("NDepend.Attributes.CannotDecreaseVisibilityAttribute".AllowNoMatch()) &&
( t.Visibility == t.OptimalVisibility ||
// Static types that define only const fields cannot be seen as used in IL code.
// They don't need to be tagged with CannotDecreaseVisibilityAttribute.
(t.IsStatic && t.NbMethods == 0 && !t.Fields.Any(f => !f.IsLiteral))
)
select t
// Get methods that do a wrong usage of CannotDecreaseVisibilityAttribute
let methods = from m in Application.Methods where
m.HasAttribute("NDepend.Attributes.CannotDecreaseVisibilityAttribute".AllowNoMatch()) &&
m.Visibility == m.OptimalVisibility
select m
// Get fields that do a wrong usage of CannotDecreaseVisibilityAttribute
let fields = from f in Application.Fields where
f.HasAttribute("NDepend.Attributes.CannotDecreaseVisibilityAttribute".AllowNoMatch()) &&
f.Visibility == f.OptimalVisibility
select f
where types.Count() > 0 || methods.Count() > 0 || fields.Count() > 0
select new {
tAttr,
types ,
methods,
fields
}
//<Description>
// The attribute **NDepend.Attributes.CannotDecreaseVisibilityAttribute**
// is defined in *NDepend.API.dll*. If you don't want to reference
// *NDepend.API.dll* you can declare it in your code.
//
// Usage of this attribute means that a code element visibility is not
// optimal (it can be lowered like for example from *public* to *internal*)
// but shouldnt be modified anyway. Typical reasons to do so include:
//
// • Public code elements consumed through reflection, through a mocking
// framework, through XML or binary serialization, through designer,
// COM interfaces…
//
// • Non-private code element invoked by test code, that would be difficult
// to reach from test if it was declared as *private*.
//
// In such situation *CannotDecreaseVisibilityAttribute* is used to avoid
// that default rules about not-optimal visibility warn. Using this
// attribute can be seen as an extra burden, but it can also be seen as
// an opportunity to express in code: **Don't change the visibility else
// something will be broken**
//
// In this context, this code rule matches code elements
// (types, methods, fields) that are tagged with this attribute,
// but still already have an optimal visibility.
//</Description>
//<HowToFix>
// Just remove *CannotDecreaseVisibilityAttribute* tagging of
// types, methods and fields matched by this rule
// because this tag is not useful anymore.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Event handler methods should be declared private</Name>
warnif count > 0
from m in Application.Methods where
!m.IsPrivate &&
!m.IsGeneratedByCompiler &&
// A method is considered as an event handler if…
m.NbParameters == 2 && // … it has two parameters …
m.Name.Contains("Object") && // … of types Object …
m.Name.Contains("EventArgs") && // … and EventArgs
// Discard special cases
!m.ParentType.IsDelegate &&
!m.IsGeneratedByCompiler
select new { m,m.Visibility }
//<Description>
// Think of a event handler like for example *OnClickButton()*.
// Typically such method must be declared as private
// and shouldn't be invoked in other context than event firing.
//</Description>
//<HowToFix>
// If you have the need that event handler method should be called
// from another class, then find a code structure that more
// closely matches the concept of what you're trying to do.
// Certainly you don't want the other class to click a button; you
// want your other class to do something that clicking a button
// also do.
//</HowToFix>]]></Query>
</Group>
<Group Name="Immutability" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Fields should be marked as ReadOnly when possible</Name>
warnif count > 0
from f in JustMyCode.Fields where
f.IsImmutable &&
!f.IsInitOnly && // The condition IsInitOnly matches fields that
// are marked with the C# readonly keyword
// (ReadOnly in VB.NET).
!f.IsGeneratedByCompiler &&
!f.IsEventDelegateObject
select new { f, f.MethodsReadingMeButNotAssigningMe, f.MethodsAssigningMe }
//<Description>
// This rule warns about instance and static fields that
// can be declared as **readonly**.
//
// This source code of this rule is based on the conditon
// *IField.IsImmutable*.
// http://www.ndepend.com/api/webframe.html?NDepend.API~NDepend.CodeModel.IField~IsImmutable.html
//
// A field that matches the condition *IsImmutable*
// is a field that is assigned only by constructors
// of its class.
//
// For an *instance field*, this means its value
// will remain constant through the lifetime
// of the object.
//
// For a *static field*, this means its value will
// remain constant through the lifetime of the
// program.
//</Description>
//<HowToFix>
// Declare the field with the C# *readonly* keyword
// (*ReadOnly* in VB.NET). This way the intention
// that the field value shouldn't change is made
// explicit.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Structures should be immutable</Name>
warnif count > 0 from t in Application.Types where
t.IsStructure &&
!t.IsImmutable
let mutableFields = t.Fields.Where(f => !f.IsImmutable)
select new { t, t.NbLinesOfCode, mutableFields }
//<Description>
// An object is immutable if its state doesnt change once the
// object has been created. Consequently, a structure or a class
// is immutable if its instances fields are only assigned inline
// or from constructor(s).
//
// But for structure it is a bit different. **Structures are value
// types** which means instances of structures are copied when they are
// passed around (like through a method argument).
//
// So if you change a copy you are changing only that copy, not the
// original and not any other copies which might be around. Such
// situation is very different than what happen with instances of
// classes. Hence developers are not used to work with modified values
// and doing so introduces *confusion* and is *error-prone*.
//</Description>
//<HowToFix>
// Make sure matched structures are immutable. This way, all
// automatic copies of an original instance, resulting from being
// *passed by value* will hold the same values and there will be
// no surprises.
//
// If your structure is immutable then if you want to change
// a value, you have to consciously do it by creating a new instance
// of the structure with the modified data.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Property Getters should be immutable</Name>
warnif count > 0 from m in Application.Methods where
!m.IsGeneratedByCompiler &&
m.IsPropertyGetter &&
( ( !m.IsStatic && m.ChangesObjectState) ||
( m.IsStatic && m.ChangesTypeState) )
let fieldsAssigned = m.FieldsAssigned
select new { m, m.NbLinesOfCode, fieldsAssigned }
//<Description>
// It is not expected that a state gets modified when
// accessing a property getter. Hence doing so create
// confusion and property getters should be pure methods,
// they shouldn't assign any field.
//</Description>
//<HowToFix>
// Make sure that matched property getters don't assign any
// field.
//
// Notice the case of an object *lazy-initialized* when
// the property getter is accessed the first time. In such
// situation the getter assigns a field, but it is only once
// and from the client point of view, lazy initialization
// is an invisible implementation detail. If you are using
// a lot lazy initalization but still want to use the
// present rule, an option is to create a
// *LazyInitializerAttribute* that tags concerned property
// getters, and modify the present rule to avoid matching them.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid static fields with a mutable field type</Name>
warnif count > 0
from f in Application.Fields
where f.IsStatic &&
!f.IsEnumValue &&
!f.IsGeneratedByCompiler
&& !f.IsLiteral
let fieldType = f.FieldType
where fieldType != null &&
!fieldType.IsThirdParty &&
!fieldType.IsInterface &&
!fieldType.IsImmutable
select new {
f,
mutableFieldType = fieldType ,
isFieldImmutable = f.IsImmutable,
isFieldIsReadOnly = f.IsInitOnly }
//<Description>
// This rule warns about static fields that can be assigned
// (i.e they are not declared as *readonly*). This rule
// also warns about *readonly* static fields whose field type
// is mutable. In both cases, each matched static field is
// holding a state that can be modified.
//
// In *Object-Oriented-Programming* the natural artifact
// to hold states that can be modified is **instance fields**.
// Hence such static fields create *confusion* at runtime.
//
// More discussion on the topic can be found here:
// http://codebetter.com/patricksmacchia/2011/05/04/back-to-basics-usage-of-static-members/
//</Description>
//<HowToFix>
// If the *static* field is just assigned once in the program
// lifetime, make sure to declare it as *readonly* and assign
// it inline, or from the static constructor.
//
// Else, to fix violations of this rule, make sure to
// hold mutable states through objects that are passed
// **explicitly** everywhere they need to be consumed, in
// opposition to mutable object hold by a static field that
// makes it modifiable from a bit everywhere in the program.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>A field must not be assigned from outside its parent hierarchy types</Name>
warnif count > 0
from f in JustMyCode.Fields.Where(f =>
(f.IsInternal || f.IsPublic) &&
!f.IsGeneratedByCompiler &&
!f.IsImmutable &&
!f.IsEnumValue)
let methodsAssignerOutsideOfMyType = f.MethodsAssigningMe.Where(
m =>!m.IsGeneratedByCompiler &&
m.ParentType != f.ParentType &&
!m.ParentType.DeriveFrom(f.ParentType) )
where methodsAssignerOutsideOfMyType.Count() > 0
select new {
f,
methodsAssignerOutsideOfMyType
}
//<Description>
// This rule is related to the rule *Fields should be declared as
// private*. It matches any **public or internal, mutable field**
// that is assigned from outside its parent class and subclasses.
//
// Fields should be considered as **implementation details**
// and as a consequence they should be declared as private.
//
// If something goes wrong with a *non-private field*,
// the culprit can be anywhere, and so in order to track down
// the bug, you may have to look at quite a lot of code.
//
// A private field, by contrast, can only be assigned from
// inside the same class, so if something goes wrong with that,
// there is usually only one source file to look at.
//</Description>
//<HowToFix>
// Matched fields must be declared as *protected* and even better
// as *private*. Alternatively, if the field can reference
// immutable states, it can remain visible from the outside,
// but then must be declared as *readonly*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Don't assign a field from many methods</Name>
warnif count > 0
from f in JustMyCode.Fields where
!f.IsEnumValue &&
!f.IsImmutable &&
!f.IsInitOnly &&
!f.IsGeneratedByCompiler &&
!f.IsEventDelegateObject
let methodsAssigningMe = f.MethodsAssigningMe.Where(m => !m.IsConstructor)
// The threshold 4 is arbitrary and it should avoid matching too many fields.
// Threshold is even lower for static fields because this reveals situations even more complex.
where methodsAssigningMe.Count() >= (!f.IsStatic ? 4 : 2)
select new {
f,
methodsAssigningMe,
f.MethodsReadingMeButNotAssigningMe,
f.MethodsUsingMe
}
//<Description>
// A field assigned from many methods is a symptom of **bug-prone code**.
// Notice that:
//
// • For an instance field, constructor(s) of its class that assign the field are not counted.
//
// • For a static field, the class constructor that assigns the field is not counted.
//
// The default threshold for *instance fields* is *assigned by more than 4
// methods*. Such situation makes harder to anticipate the field state during
// runtime. The code is then complicated to read, hard to debug and hard to
// maintain. Hard-to-solve bugs due to corrupted state are often the
// consequence of fields *anarchically assigned*.
//
// The situation is even more complex if the field is *static*.
// Indeed, such situation potentially involves global random accesses from
// various parts of the application. This is why this rule provides a lower
// threshold equals to *assigned by more than 2 methods* for static fields.
//
// If the object containing such field is meant to be used from multiple threads,
// there are **alarming chances** that the code is unmaintainable and bugged.
// When multiple threads are involved, the rule of thumb is to use immutable objects.
//
// If the field type is a reference type (interfaces, classes, strings, delegates)
// corrupted state might result in a *NullReferenceException*.
// If the field type is a value type (number, boolean, structure)
// corrupted state might result in wrong result not even signaled by an exception
// thrown.
//</Description>
//<HowToFix>
// There is no straight advice to refactor the number of methods responsible
// for assigning a field. Solutions often involve rethinking and then rewriting
// a complex algorithm. Such field can sometime become just a variable accessed
// locally by a method or a *closure*. Sometime, just rethinking the life-time
// and the role of the parent object allows the field to become immutable
// (i.e assigned only by the constructor).
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Do not declare read only mutable reference types</Name>
warnif count > 0 from f in JustMyCode.Fields where
f.IsInitOnly &&
!f.ParentType.IsPrivate &&
!f.IsPrivate &&
f.FieldType != null &&
f.FieldType.IsClass &&
!f.FieldType.IsThirdParty &&
!f.FieldType.IsImmutable
select new {
f,
f.FieldType,
FieldVisibility = f.Visibility
}
//<Description>
// This rule is violated when a *public* or *internal*
// type contains a *public* or *internal* read-only field
// whose field type is a mutable reference type.
//
// This situation provides the wrong impression that the
// value can't change, when actually it's only the field
// value that can't change, but the object state
// can still change.
//</Description>
//<HowToFix>
// To fix a violation of this rule,
// replace the field type with an immutable type,
// or declare the field as *private*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Array fields should not be read only</Name>
warnif count > 0 from f in Application.Fields where
f.IsInitOnly &&
f.IsPubliclyVisible &&
f.FieldType != null &&
f.FieldType.FullName == "System.Array"
select new {
f,
FieldVisibility = f.Visibility
}
//<Description>
// This rule is violated when a publicly visible field
// that holds an array, is declared read-only.
//
// This situation represents a *security vulnerability*.
// Because the field is read-only it cannot be changed to refer
// to a different array. However, the elements of the array
// that are stored in a read-only field can be changed.
// Code that makes decisions or performs operations that are
// based on the elements of a read-only array that can be publicly
// accessed might contain an exploitable security vulnerability.
//</Description>
//<HowToFix>
// To fix the security vulnerability that is identified by
// this rule do not rely on the contents of a read-only array
// that can be publicly accessed. It is strongly recommended
// that you use one of the following procedures:
//
// • Replace the array with a strongly typed collection
// that cannot be changed. See for example:
// *System.Collections.Generic.IReadOnlyList<T>* ;
// *System.Collections.Generic.IReadOnlyCollection<T>* ;
// *System.Collections.ReadOnlyCollectionBase*
//
// • Or replace the public field with a method that returns a clone
// of a private array. Because your code does not rely on
// the clone, there is no danger if the elements are modified.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types tagged with ImmutableAttribute must be immutable</Name>
warnif count > 0
from t in Application.Types where
t.HasAttribute ("NDepend.Attributes.ImmutableAttribute".AllowNoMatch()) &&
!t.IsImmutable
let culpritFields = t.Fields.Where(f => !f.IsStatic && !f.IsImmutable)
select new {
t,
culpritFields
}
//<Description>
// An object is immutable if its state doesnt change once the
// object has been created. Consequently, a structure or a class
// is immutable if its instances fields are only assigned inline
// or from constructor(s).
//
// An attribute **NDepend.Attributes.ImmutableAttribute** can be
// used to express in code that a type is immutable. In such
// situation, the present code rule checks continuously that the
// type remains immutable whatever the modification done.
//
// This rule warns when a type that is tagged with
// *ImmutableAttribute* is actually not immutable anymore.
//
// Notice that *FullCoveredAttribute* is defined in *NDepend.API.dll*
// and if you don't want to link this assembly, you can create your
// own *FullCoveredAttribute* and adapt the rule.
//</Description>
//<HowToFix>
// First understand which modification broke the type immutability.
// The list of *culpritFields* provided in this rule result can help.
// Then try to refactor the type to bring it back to immutability.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types immutable should be tagged with ImmutableAttribute</Name>
from t in Application.Types where
!t.HasAttribute ("NDepend.Attributes.ImmutableAttribute".AllowNoMatch()) &&
t.IsImmutable
select new { t, t.NbLinesOfCode }
//<Description>
// An object is immutable if its state doesnt change once the
// object has been created. Consequently, a structure or a class
// is immutable if its instances fields are only assigned inline
// or from constructor(s).
//
// This code query lists immutable type that are not tagged with
// an **ImmutableAttribute**. By using such attribute, you can express
// in source code the intention that a class is immutable, and
// should remain immutable in the future. Benefits of using
// an **ImmutableAttribute** are twofold:
//
// • Not only the intention is expressed in source code,
//
// • but it is also continuously checked by the rule
// *Types tagged with ImmutableAttribute must be immutable*.
//</Description>
//<HowToFix>
// Just tag types matched by this code query with **ImmutableAttribute**
// that can be found in *NDepend.API.dll*,
// or by an attribute of yours defined in your own code
// (in which case this code query must be adapted).
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods tagged with PureAttribute must be pure</Name>
warnif count > 0 from m in Application.Methods where
( m.HasAttribute ("NDepend.Attributes.PureAttribute".AllowNoMatch()) ||
m.HasAttribute ("System.Diagnostics.Contract.PureAttribute".AllowNoMatch()) ) &&
( m.ChangesObjectState || m.ChangesTypeState ) &&
m.NbLinesOfCode > 0
let fieldsAssigned = m.FieldsAssigned
select new { m, m.NbLinesOfCode, fieldsAssigned }
//<Description>
// A method is pure if its execution doesnt change
// the value of any instance or static field.
// Pure methods naturally simplify code by **limiting
// side-effects**.
//
// An attribute **PureAttribute** can be
// used to express in code that a method is pure. In such
// situation, the present code rule checks continuously that the
// method remains pure whatever the modification done.
//
// This rule warns when a method that is tagged with
// *PureAttribute* is actually not pure anymore.
//
// Notice that *NDepend.Attributes.PureAttribute* is defined
// in *NDepend.API.dll* and if you don't want to link this
// assembly, you can also use
// *System.Diagnostics.Contract.PureAttribute*
// or create your own *PureAttribute* and adapt the rule.
//
// Notice that *System.Diagnostics.Contract.PureAttribute* is
// taken account by the compiler only when the VS project has
// Microsoft Code Contract enabled.
//</Description>
//<HowToFix>
// First understand which modification broke the method purity.
// Then refactor the method to bring it back to purity.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Pure methods should be tagged with PureAttribute</Name>
// warnif count > 0 <-- not a cod rule per default
from m in Application.Methods where
!m.IsGeneratedByCompiler &&
!m.HasAttribute ("NDepend.Attributes.PureAttribute".AllowNoMatch()) &&
!m.HasAttribute ("System.Diagnostics.Contract.PureAttribute".AllowNoMatch()) &&
!m.ChangesObjectState && !m.ChangesTypeState &&
m.NbLinesOfCode > 0
select new { m, m.NbLinesOfCode }
//<Description>
// A method is pure if its execution doesnt change
// the value of any instance or static field.
// Pure methods naturally simplify code by **limiting
// side-effects**.
//
// This code query lists pure methods that are not tagged with
// a **PureAttribute**. By using such attribute, you can express
// in source code the intention that a method is pure, and
// should remain pure in the future. Benefits of using
// a **PureAttribute** are twofold:
//
// • Not only the intention is expressed in source code,
//
// • but it is also continuously checked by the rule
// *Methods tagged with PureAttribute must be pure*.
//
// This code query is not by default defined as a code rule
// because certainly many of the methods of the code base
// are matched. Hence fixing all matches and then
// maintaining the rule unviolated might require a lot of
// work. This may *counter-balance* such rule benefits.
//</Description>
//<HowToFix>
// Just tag methods matched by this code query with
// *NDepend.Attributes.PureAttribute*
// that can be found in *NDepend.API.dll*,
// or with *System.Diagnostics.Contract.PureAttribute*,
// or with an attribute of yours defined in your own code
// (in which case this code query must be adapted).
//
// Notice that *System.Diagnostics.Contract.PureAttribute* is
// taken account by the compiler only when the VS project has
// Microsoft Code Contract enabled.
//</HowToFix>]]></Query>
</Group>
<Group Name="Naming Conventions" Active="True" ShownInReport="False">
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Instance fields should be prefixed with a 'm_'</Name>
warnif count > 0 from f in Application.Fields where
!f.NameLike(@"^m_") &&
!f.IsStatic &&
!f.IsLiteral &&
!f.IsGeneratedByCompiler &&
!f.IsSpecialName &&
!f.IsEventDelegateObject
select new { f, f.SizeOfInst }
//<Description>
// In terms of behavior, a *static field* is something completely different
// than an *instance field*, so it is interesting to differentiate them at
// a glance through a naming convetion.
//
// The present rule enforces **m_** prefix for *instance fields* names.
// To do so, the rule relies on a **f.NameLike(@"^m_")** *regular expression*.
// You can easily adapt the *regular expression* to your own naming convention.
//
// Related discussion:
// http://codebetter.com/patricksmacchia/2013/09/04/on-hungarian-notation-for-instance-vs-static-fields-naming/
//</Description>
//<HowToFix>
// Once the rule has been adapted to your own naming convention
// make sure to name all matched instance fields adequately.
//</HowToFix>]]></Query>
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Static fields should be prefixed with a 's_'</Name>
warnif count > 0 from f in Application.Fields where
!f.NameLike (@"^s_") &&
f.IsStatic &&
!f.IsLiteral &&
!f.IsGeneratedByCompiler &&
!f.IsSpecialName &&
!f.IsEventDelegateObject
select new { f, f.SizeOfInst }
//<Description>
// In terms of behavior, a *static field* is something completely different
// than an *instance field*, so it is interesting to differentiate them at
// a glance through a naming convetion.
//
// The present rule enforces **s_** prefix for *static fields* names.
// To do so, the rule relies on a **f.NameLike(@"^s_")** *regular expression*.
// You can easily adapt the *regular expression* to your own naming convention.
//
// Related discussion:
// http://codebetter.com/patricksmacchia/2013/09/04/on-hungarian-notation-for-instance-vs-static-fields-naming/
//</Description>
//<HowToFix>
// Once the rule has been adapted to your own naming convention
// make sure to name all matched static fields adequately.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Interface name should begin with a 'I'</Name>
warnif count > 0 from t in Application.Types where
t.IsInterface &&
// Don't apply this rule for COM interfaces.
!t.HasAttribute("System.Runtime.InteropServices.ComVisibleAttribute".AllowNoMatch())
// Discard outter type(s) name prefix for nested types
let name = !t.IsNested ?
t.Name :
t.Name.Substring(t.Name.LastIndexOf('+') + 1, t.Name.Length - t.Name.LastIndexOf('+') - 1)
where name[0] != 'I'
select t
//<Description>
// In the .NET world, interfaces names are commonly prefixed
// with an upper case **I**. This rule warns about interfaces
// whose names don't follow this convention. Because this
// naming convention is widely used and accepted, we
// recommend abiding by this rule.
//
// Typically COM interfaces names don't follow this rule.
// Hence this code rule doesn't take care of interfaces tagged
// with *ComVisibleAttribute*.
//</Description>
//<HowToFix>
// Make sure that matched interfaces names are prefixed with
// an upper **I**.
//</HowToFix>]]></Query>
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Abstract base class should be suffixed with 'Base'</Name>
warnif count > 0 from t in Application.Types where
t.IsAbstract &&
t.IsClass &&
t.BaseClass != null &&
t.BaseClass.FullName == "System.Object" &&
((!t.IsGeneric && !t.NameLike (@"Base$")) ||
( t.IsGeneric && !t.NameLike (@"Base<")))
select t
//<Description>
// This rule warns about *abstract classes* whose names are not
// suffixed with **Base**. It is a common practice in the .NET
// world to suffix base classes names with **Base**.
//
// Notice that this rule doesn't match
// abstract classes that are in a middle of a hierarchy chain.
// Only base classes that derive directly from *System.Object*
// are matched.
//</Description>
//<HowToFix>
// Suffix the names of matched base classes with **Base**.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Exception class name should be suffixed with 'Exception'</Name>
warnif count > 0 from t in Application.Types where
t.IsExceptionClass &&
// We use SimpleName, because in case of generic Exception type
// SimpleName suppresses the generic suffix (like <T>).
!t.SimpleNameLike(@"Exception$") &&
!t.SimpleNameLike(@"ExceptionBase$") // Allow the second suffix Base
// for base exception classes.
select t
//<Description>
// This rule warns about *exception classes* whose names are not
// suffixed with **Exception**. It is a common practice in the .NET
// world to suffix exception classes names with **Exception**.
//
// For exception base classes, the suffix **ExceptionBase**
// is also accepted.
//</Description>
//<HowToFix>
// Suffix the names of matched exception classes with **Exception**.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Attribute class name should be suffixed with 'Attribute'</Name>
warnif count > 0 from t in Application.Types where
t.IsAttributeClass &&
!t.NameLike (@"Attribute$")
select t
//<Description>
// This rule warns about *attribute classes* whose names are not
// suffixed with **Attribute**. It is a common practice in the .NET
// world to suffix attribute classes names with **Attribute**.
//</Description>
//<HowToFix>
// Suffix the names of matched attribute classes with **Attribute**.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types name should begin with an Upper character</Name>
warnif count > 0 from t in JustMyCode.Types where
// The name of a type should begin with an Upper letter.
!t.SimpleNameLike (@"^[A-Z]") &&
// Except if it is generated by compiler.
!t.IsSpecialName &&
!t.IsGeneratedByCompiler
select new {
t,
// We show the type simple name
// that doesn't include the parent type name
// for nested types.
t.SimpleName }
//<Description>
// This rule warns about *types* whose names don't start
// with an Upper character. It is a common practice in the .NET
// world to use **Pascal Casing Style** to name types.
//
// **Pascal Casing Style** : The first letter in the identifier
// and the first letter of each subsequent concatenated word
// are capitalized. For example: *BackColor*
//</Description>
//<HowToFix>
// *Pascal Case* the names of matched types.
//</HowToFix>]]></Query>
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods name should begin with an Upper character</Name>
warnif count > 0 from m in JustMyCode.Methods where
!m.NameLike (@"^[A-Z]") &&
!m.IsSpecialName &&
!m.IsGeneratedByCompiler
select m
//<Description>
// This rule warns about *methods* whose names don't start
// with an Upper character. It is a common practice in the .NET
// world to use **Pascal Casing Style** to name methods.
//
// **Pascal Casing Style** : The first letter in the identifier
// and the first letter of each subsequent concatenated word
// are capitalized. For example: *ComputeSize*
//</Description>
//<HowToFix>
// *Pascal Case* the names of matched methods.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Do not name enum values 'Reserved'</Name>
warnif count > 0 from f in Application.Fields where
f.IsEnumValue &&
f.NameLike (@"Reserved")
select f
//<Description>
// This rule assumes that an enumeration member
// with a name that contains **"Reserved"**
// is not currently used but is a placeholder to
// be renamed or removed in a future version.
// Renaming or removing a member is a breaking
// change. You should not expect users to ignore
// a member just because its name contains
// **"Reserved"** nor can you rely on users to read or
// abide by documentation. Furthermore, because
// reserved members appear in object browsers
// and smart integrated development environments,
// they can cause confusion as to which members
// are actually being used.
//
// Instead of using a reserved member, add a
// new member to the enumeration in the future
// version.
//
// In most cases, the addition of the new
// member is not a breaking change, as long as the
// addition does not cause the values of the
// original members to change.
//</Description>
//<HowToFix>
// To fix a violation of this rule, remove or
// rename the member.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid types with name too long</Name>
warnif count > 0 from t in Application.Types
where !t.IsGeneratedByCompiler
where t.SimpleName.Length > 35
select new { t, t.SimpleName }
//<Description>
// Types with a name too long tend to decrease code readability.
// This might also be an indication that a type is doing too much.
//
// This rule matches types with names with more than 35 characters.
//</Description>
//<HowToFix>
// To fix a violation of this rule, rename the type with a shortest name
// or eventually split the type in several more fine-grained types.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid methods with name too long</Name>
warnif count > 0 from m in Application.Methods where
// Explicit Interface Implementation methods are
// discarded because their names are prefixed
// with the interface name.
!m.IsExplicitInterfaceImpl &&
!m.IsGeneratedByCompiler &&
((!m.IsSpecialName && m.SimpleName.Length > 35) ||
// Property getter/setter are prefixed with "get_" "set_" of length 4.
( m.IsSpecialName && m.SimpleName.Length - 4 > 35))
select new { m, m.SimpleName }
//<Description>
// Methods with a name too long tend to decrease code readability.
// This might also be an indication that a method is doing too much.
//
// This rule matches methods with names with more than 35 characters.
//</Description>
//<HowToFix>
// To fix a violation of this rule, rename the method with a shortest name
// that equally conveys the behavior of the method.
// Or eventually split the method into several smaller methods.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid fields with name too long</Name>
warnif count > 0 from f in Application.Fields where
!f.IsGeneratedByCompiler &&
f.Name.Length > 35
select f
//<Description>
// Fields with a name too long tend to decrease code readability.
//
// This rule matches fields with names with more than 35 characters.
//</Description>
//<HowToFix>
// To fix a violation of this rule, rename the field with a shortest name
// that equally conveys the same information.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Avoid having different types with same name</Name>
warnif count > 0
// This rule matches also collisions between
// application and third-party types sharing a same name.
let groups = JustMyCode.Types.Union(ThirdParty.Types)
// Discard nested types, whose name is
// prefixed with the parent type name.
.Where(t => !t.IsNested)
// Group types by name.
.GroupBy(t => t.Name)
from @group in groups
where @group.Count() > 1
// Let's see if types with the same name are declared
// in different namespaces.
// (t.FullName is {namespaceName}.{typeName} )
let groupsFullName = @group.GroupBy(t => t.FullName)
where groupsFullName.Count() > 1
// If several types with same name are declared in different namespaces
// eliminate the case where all types are declared in third-party assemblies.
let types= groupsFullName.SelectMany(g => g)
where types.Any(t => !t.IsThirdParty)
// Uncomment this line, to only gets naming collision involving
// both application and third-party types.
// && types.Any(t => t.IsThirdParty)
orderby types.Count() descending
select new {
t = types.First(),
// In the 'types' column, make sure to group matched types
// by parent assemblies and parent namespaces, to get a result
// more readable.
types
}
//<Description>
// This rule warns about multiple types with same name,
// that are defined in different *application* or
// *third-party* namespaces or assemblies.
//
// Such practice create confusion and also naming collision
// in source files that use different types with same name.
//</Description>
//<HowToFix>
// To fix a violation of this rule, rename concerned types.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid prefixing type name with parent namespace name</Name>
warnif count > 0
from n in Application.Namespaces
where n.Name.Length > 0
from t in n.ChildTypes
where
!t.IsGeneratedByCompiler &&
!t.IsNested &&
t.Name.IndexOf(n.SimpleName) == 0
select new {
t,
namespaceName = n.SimpleName
}
//<Description>
// This rule warns about situations where the parent namespace name
// is used as the prefix of a contained type.
//
// For example a type named "RuntimeEnvironment"
// declared in a namespace named "Foo.Runtime"
// should be named "Environment".
//
// Such situation creates naming redundancy with no readability gain.
//</Description>
//<HowToFix>
// To fix a violation of this rule, remove the prefix from the type name.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid naming types and namespaces with the same identifier</Name>
warnif count > 0
let hashsetShortNames = Namespaces.Where(n => n.Name.Length > 0).Select(n => n.SimpleName).ToHashSet()
from t in JustMyCode.Types
where hashsetShortNames.Contains(t.Name)
select new {
t,
namespaces = Namespaces.Where(n => n.SimpleName == t.Name)
}
//<Description>
// This rule warns when a type and a namespace have the same name.
//
// For example when a type is named *Environment*
// and a namespace is named *Foo.Environment*.
//
// Such situation provokes tedious compiler resolution collision,
// and makes the code less readable because concepts are not
// concisely identified.
//</Description>
//<HowToFix>
// To fix a violation of this rule, renamed the concerned type or namespace.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Don't call your method Dispose</Name>
warnif count > 0
from m in JustMyCode.Methods.WithSimpleName("Dispose")
where !m.ParentType.Implement("System.IDisposable".AllowNoMatch())
select m
//<Description>
// In .NET programming, the identifier *Dispose()* should be kept
// only for implementations of *System.IDisposable*.
//
// This rule warns when a method is named *Dispose()*,
// but the parent type doesn't implement *System.IDisposable*.
//</Description>
//<HowToFix>
// To fix a violation of this rule,
// either make the parent type implements *System.IDisposable*,
// or rename the *Dispose()* method with another identifier:
// *Close() Terminate() Finish() Quit() Exit() Unlock() ShutDown()*…
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods prefixed with 'Try' should return a boolean</Name>
warnif count > 0
from m in Application.Methods where
m.SimpleNameLike("^Try") &&
m.ReturnType != null &&
m.ReturnType.FullName != "System.Boolean"
select new {
m,
m.ReturnType
}
//<Description>
// When a method has a name prefixed with **Try**, it is expected that
// it returns a *boolean*, that reflects the method execution status,
// *success* or *failure*.
//
// Such method usually returns a result through an *out parameter*.
// For example: *System.Int32.TryParse(int,out string):bool*
//</Description>
//<HowToFix>
// To fix a violation of this rule,
// Rename the method, or transform it into an operation that can fail.
//</HowToFix>]]></Query>
</Group>
<Group Name="Source Files Organization" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid referencing source file out of Visual Studio project directory</Name>
warnif count > 0
from a in Application.Assemblies
where a.VisualStudioProjectFilePath != null
let vsProjDirPathLower = a.VisualStudioProjectFilePath.ParentDirectoryPath.ToString().ToLower()
from t in a.ChildTypes
where JustMyCode.Contains(t) && t.SourceFileDeclAvailable
from decl in t.SourceDecls
let sourceFilePathLower = decl.SourceFile.FilePath.ToString().ToLower()
where sourceFilePathLower.IndexOf(vsProjDirPathLower) != 0
select new {
t,
sourceFilePathLower,
projectFilePath = a.VisualStudioProjectFilePath.ToString()
}
//<Description>
// A source file located outside of the VS project directory can be added through:
// *> Add > Existing Items… > Add As Link*
//
// Doing so can be used to share types definitions across several assemblies.
// This provokes type duplication at binary level.
// Hence maintainability is degraded and subtle versioning bug can appear.
//
// This rule matches types whose source files are not declared under the
// directory that contains the related Visual Studio project file, or under
// any sub-directory of this directory.
//
// This practice can be tolerated for certain types shared across executable assemblies.
// Such type can be responsible for startup related concerns,
// such as registering custom assembly resolving handlers or
// checking the .NET Framework version before loading any custom library.
//</Description>
//<HowToFix>
// To fix a violation of this rule, prefer referencing from a VS project
// only source files defined in sub-directories of the VS project file location.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid duplicating a type definition across assemblies</Name>
warnif count > 0
let groups = Application.Types
.Where(t => !t.IsGeneratedByCompiler)
.GroupBy(t => t.FullName)
from @group in groups
where @group.Count() > 1
select new {
t = @group.First(),
// In the 'types' column, make sure to group matched types by parent assemblies.
typesDefs = @group.ToArray()
}
//<Description>
// A source file located outside of the VS project directory can be added through:
// *> Add > Existing Items… > Add As Link*
//
// This rule warns about using this feature to share code across several assemblies.
// This provokes type duplication at binary level.
// Hence maintainability is degraded and subtle versioning bug can appear.
//
// This practice can be tolerated for certain types shared across executable assemblies.
// Such type can be responsible for startup related concerns,
// such as registering custom assembly resolving handlers or
// checking the .NET Framework version before loading any custom library.
//</Description>
//<HowToFix>
// To fix a violation of this rule, prefer sharing types through DLLs.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid defining multiple types in a source file</Name>
warnif count > 0
// Build a lookup indexed by source files, values being a sequence of types defined in the source file.
let lookup = Application.Types.Where(t =>
t.SourceFileDeclAvailable &&
// except nested types and types generated by compilers!
!t.IsGeneratedByCompiler &&
!t.IsNested)
// It could make sense to not apply this rule for enumerations.
// && !t.IsEnumeration)
// We use multi-key, since a type can be declared in multiple source files.
.ToMultiKeyLookup(t => t.SourceDecls.Select(d => d.SourceFile))
from @group in lookup where @group.Count() > 1
let sourceFile = @group.Key
// CQLinq doesn't let indexing result with sourceFile
// so we choose a typeIndex in types,
// preferably the type that has the file name.
let typeWithSourceFileName = @group.FirstOrDefault(t => t.SimpleName == sourceFile.FileNameWithoutExtension)
let typeIndex = typeWithSourceFileName ?? @group.First()
select new {
typeIndex,
TypesInSourceFile = @group as IEnumerable<IType>,
SourceFilePathString = sourceFile.FilePathString
}
//<Description>
// Defining multiple types in a single source file decreases code readability,
// because developers are used to see all types in a namespace,
// when expanding a folder in the *Visual Studio Solution Explorer*.
// Also doing so, leads to source files with too many lines.
//
// Each match of this rule is a source file that contains several types
// definitions, indexed by one of those types, preferably the one with
// the same name than the source file name without file extension, if any.
//</Description>
//<HowToFix>
// To fix a violation of this rule, create a source file for each type.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Namespace name should correspond to file location</Name>
warnif count > 0
from n in Application.Namespaces
// Replace dots by spaces in namespace name
let dirCorresponding = n.Name.Replace('.', ' ')
// Look at source file decl of JustMyCode type's declared in n
from t in n.ChildTypes
where JustMyCode.Contains(t) && t.SourceFileDeclAvailable
from decl in t.SourceDecls
let sourceFilePath = decl.SourceFile.FilePath.ToString()
// Replace dots and path separators by spaces in source files names
where !sourceFilePath.Replace('.',' ').Replace('\\',' ').Contains(dirCorresponding)
select new {
t,
dirCorresponding ,
sourceFilePath
}
//<Description>
// For a solid code structure and organization,
// do mirror the namespaces hierarchy and the directories hierarchy containing source files.
//
// Doing so is a widely accepted convention, and not respecting this convention
// will lead to less maintainable and less browsable source code.
//
// This rule matches all types in such source file, whose location doesn't correspond
// to the type parent namespace. If a source file contains several such types (that
// are not necessarily in the same namespace) each type will result in a violation.
//</Description>
//<HowToFix>
// To fix a violation of this rule, make sure that the type parent namespace and
// the directory sub-paths that contains the type source file, are mirrored.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types with source files stored in the same directory, should be declared in the same namespace</Name>
warnif count > 0
// Group JustMyCode types in a lookup
// where groups are keyed with directories that contain the types' source file(s).
// Note that a type can be contained in several groups
// if it is declared in several source files stored in different directories.
let lookup = JustMyCode.Types.Where(t => t.SourceFileDeclAvailable)
.ToMultiKeyLookup(
t => t.SourceDecls.Select(
decl => decl.SourceFile.FilePath.ParentDirectoryPath).Distinct()
)
from groupOfTypes in lookup
let parentNamespaces = groupOfTypes.ParentNamespaces()
// Select group of types (with source files stored in the same directory) …
// … but contained in several namespaces
where parentNamespaces.Count() > 1
// mainNamespaces is the namespace that contains many types
// declared in the directory groupOfTypes .key
let mainNamespace = groupOfTypes
.ToLookup(t => t.ParentNamespace)
.OrderByDescending(g => g.Count()).First().Key
// Select types with source files stored in the same directory,
// but contained in namespaces different than mainNamespace.
let typesOutOfMainNamespace = groupOfTypes
.Where(t => t.ParentNamespace != mainNamespace &&
t.ParentAssembly == mainNamespace.ParentAssembly)
// Filter types declared on several source files that contain generated methods
// because typically such type contains one or several partial definitions generated.
// These partially generated types would be false positive for the present rule.
.Where(t => t.SourceDecls.Count() == 1 ||
t.Methods.Count(m => JustMyCode.Contains(m)) == 0)
where typesOutOfMainNamespace.Count() > 0
let typesInMainNamespace = groupOfTypes.Where(t => t.ParentNamespace == mainNamespace)
select new {
mainNamespace,
typesOutOfMainNamespace,
typesInMainNamespace }
//<Description>
// For a solid code structure and organization, do mirror the namespaces
// hierarchy and the directories hierarchy containing source files.
//
// Doing so is a widely accepted convention, and not respecting this convention
// will lead to less maintainable and less browsable code.
//
// Respecting this convention means that types with source files stored in the same directory,
// should be declared in the same namespace.
//
// For each directory that contains several source files, where most types are declared
// in a namespace (what we call the **main namespace**) and a few types are declared
// out of the *main namespace*, this code rule matches:
//
// • The *main namespace*
//
// • **typesOutOfMainNamespace**: Types declared in source files in the *main namespace*'s directory
// but that are not in the *main namespace*.
//
// • *typesInMainNamespace*: And for informational purposes, types declared in source files in the
// *main namespace*'s directory, and that are in the *main namespace*.
//</Description>
//<HowToFix>
// Violations of this rule are types in the *typesOutOfMainNamespace* column.
// Typically such type …
//
// • … is contained in the wrong namespace but its source file is stored in the right directory.
// In such situation the type should be contained in *main namespace*.
//
// • … is contained in the right namespace but its source file is stored in the wrong directory
// In such situation the source file of the type must be moved to the proper parent namespace directory.
//
// • … is declared in multiple source files, stored in different directories.
// In such situation it is preferable that all source files are stored in a single directory.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types declared in the same namespace, should have their source files stored in the same directory</Name>
warnif count > 0
from @namespace in Application.Namespaces
// Group types of @namespace in a lookup
// where groups are keyed with directories that contain the types' source file(s).
// Note that a type can be contained in several groups
// if it is declared in several source files stored in different directories.
let lookup = @namespace.ChildTypes.Where(t => t.SourceFileDeclAvailable && JustMyCode.Contains(t))
.ToMultiKeyLookup(
t => t.SourceDecls.Select(
decl => decl.SourceFile.FilePath.ParentDirectoryPath).Distinct()
)
// Are types of @namespaces declared in more than one directory?
where lookup.Count > 1
// Infer the main folder, preferably the one that has the same name as the namespace.
let dirs = lookup.Select(types => types.Key)
let mainDirNullable = dirs.Where(d => d.DirectoryName == @namespace.SimpleName).FirstOrDefault()
let mainDir = mainDirNullable ?? dirs.First()
// Types declared out of mainDir, are types in group of types declared in a directory different than mainDir!
let typesDeclaredOutOfMainDir =
lookup.Where(types => types.Key != mainDir)
.SelectMany(types => types)
// Filter types declared on several source files that contain generated methods
// because typically such type contains one or several partial definitions generated.
// These partially generated types would be false positive for the present rule.
.Where(t => t.SourceDecls.Count() == 1 ||
t.Methods.Count(m => JustMyCode.Contains(m)) == 0)
where typesDeclaredOutOfMainDir.Count() > 0
let typesDeclaredInMainDir =
lookup.Where(types => types.Key == mainDir)
.SelectMany(types => types)
select new {
@namespace,
typesDeclaredOutOfMainDir ,
mainDir = mainDir.ToString(),
typesDeclaredInMainDir
}
//<Description>
// For a solid code structure and organization,
// do mirror the namespaces hierarchy and the directories hierarchy containing source files.
//
// Doing so is a widely accepted convention, and not respecting this convention
// will lead to less maintainable and less browsable code.
//
// Respecting this convention means that types declared in the same namespace,
// should have their source files stored in the same directory.
//
// For each namespace that contains types whose source files
// are declared in several directories, infer the **main directory**,
// the directory that naturally hosts source files of types,
// preferably the directory whose name corresponds with the namespace
// name. In this context, this code rule matches:
//
// • The namespace
//
// • **typesDeclaredOutOfMainDir**: types in the namespace whose source files
// are stored out of the *main directory*.
//
// • The *main directory*
//
// • *typesDeclaredInMainDir*: for informational purposes, types declared
// in the namespace, whose source files are stored in the *main directory*.
//</Description>
//<HowToFix>
// Violations of this rule are types in the **typesDeclaredOutOfMainDir** column.
// Typically such type…
//
// • … is contained in the wrong namespace but its source file is stored in the right directory.
// In such situation the type should be contained in the namespace corresponding to
// the parent directory.
//
// • … is contained in the right namespace but its source file is stored in the wrong directory.
// In such situation the source file of the type must be moved to the *main directory*.
//
// • … is declared in multiple source files, stored in different directories.
// In such situation it is preferable that all source files are stored in a single directory.
//</HowToFix>]]></Query>
</Group>
<Group Name=".NET Framework Usage" Active="True" ShownInReport="False">
<Group Name="System" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Mark ISerializable types with SerializableAttribute</Name>
warnif count > 0
from t in Application.Types where
t.IsPublic &&
!t.IsDelegate &&
!t.IsExceptionClass && // Don't match exceptions, since the Exception class
// implements ISerializable, this would generate
// too many false positives.
t.Implement ("System.Runtime.Serialization.ISerializable".AllowNoMatch()) &&
!t.HasAttribute ("System.SerializableAttribute".AllowNoMatch())
select new { t, t.NbLinesOfCode }
//<Description>
// To be recognized by the CLR as serializable,
// types must be marked with the *SerializableAttribute*
// attribute even if the type uses a custom
// serialization routine through implementation of
// the *ISerializable* interface.
//
// This rule matches types that implement *ISerializable* and
// that are not tagged with *SerializableAttribute*.
//</Description>
//<HowToFix>
// To fix a violation of this rule, tag the matched type
// with *SerializableAttribute* .
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Mark assemblies with assembly version</Name>
warnif count > 0 from a in Application.Assemblies where
!a.HasAttribute ("System.Reflection.AssemblyVersionAttribute".AllowNoMatch())
select a
//<Description>
// The identity of an assembly is composed of the following information:
//
// • Assembly name
//
// • Version number
//
// • Culture
//
// • Public key (for strong-named assemblies).
//
// The .NET Framework uses the version number to uniquely identify an
// assembly, and to bind to types in strong-named assemblies. The
// version number is used together with version and publisher policy.
// By default, applications run only with the assembly version with
// which they were built.
//
// This rule matches assemblies that are not tagged with
// **System.Reflection.AssemblyVersionAttribute**.
//</Description>
//<HowToFix>
// To fix a violation of this rule, add a version number to the assembly
// by using the *System.Reflection.AssemblyVersionAttribute* attribute.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Mark assemblies with CLSCompliant</Name>
warnif count > 0 from a in Application.Assemblies where
!a.HasAttribute ("System.CLSCompliantAttribute".AllowNoMatch())
select a
//<Description>
// The *Common Language Specification* (CLS) defines naming restrictions,
// data types, and rules to which assemblies must conform if they are to
// be used across programming languages. Good design dictates that all
// assemblies explicitly indicate CLS compliance with **CLSCompliantAttribute**.
// If the attribute is not present on an assembly, the assembly is not compliant.
//
// Notice that it is possible for a CLS-compliant assembly to contain types or
// type members that are not compliant.
//
// This rule matches assemblies that are not tagged with
// **System.CLSCompliantAttribute**.
//</Description>
//<HowToFix>
// To fix a violation of this rule, tag the assembly with *CLSCompliantAttribute*.
//
// Instead of marking the whole assembly as non-compliant, you should determine
// which type or type members are not compliant and mark these elements as such.
// If possible, you should provide a CLS-compliant alternative for non-compliant
// members so that the widest possible audience can access all the functionality
// of your assembly.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Mark assemblies with ComVisible</Name>
warnif count > 0 from a in Application.Assemblies where
!a.HasAttribute ("System.Runtime.InteropServices.ComVisibleAttribute".AllowNoMatch())
select a
//<Description>
// The **ComVisibleAttribute** attribute determines how COM clients access
// managed code. Good design dictates that assemblies explicitly indicate
// COM visibility. COM visibility can be set for a whole assembly and then
// overridden for individual types and type members. If the attribute is not
// present, the contents of the assembly are visible to COM clients.
//
// This rule matches assemblies that are not tagged with
// **System.Runtime.InteropServices.ComVisibleAttribute**.
//</Description>
//<HowToFix>
// To fix a violation of this rule, tag the assembly with *ComVisibleAttribute*.
//
// If you do not want the assembly to be visible to COM clients, set the
// attribute value to **false**.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Mark attributes with AttributeUsageAttribute</Name>
warnif count > 0 from t in Application.Types where
t.DeriveFrom ("System.Attribute".AllowNoMatch()) &&
!t.HasAttribute ("System.AttributeUsageAttribute".AllowNoMatch())
select t
//<Description>
// When you define a custom attribute, mark it by using **AttributeUsageAttribute**
// to indicate where in the source code the custom attribute can be applied. The
// meaning and intended usage of an attribute will determine its valid locations
// in code. For example, you might define an attribute that identifies the person
// who is responsible for maintaining and enhancing each type in a library, and
// that responsibility is always assigned at the type level. In this case, compilers
// should enable the attribute on classes, enumerations, and interfaces, but should
// not enable it on methods, events, or properties. Organizational policies and
// procedures would dictate whether the attribute should be enabled on assemblies.
//
// The **System.AttributeTargets** enumeration defines the targets that you can
// specify for a custom attribute. If you omit *AttributeUsageAttribute*, your
// custom attribute will be valid for all targets, as defined by the **All** value of
// *AttributeTargets* enumeration.
//
// This rule matches attribute classes that are not tagged with
// **System.AttributeUsageAttribute**.
//</Description>
//<HowToFix>
// To fix a violation of this rule, specify targets for the attribute by using
// *AttributeUsageAttribute*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Remove calls to GC.Collect()</Name>
warnif count > 0
let gcCollectMethods = ThirdParty.Methods.WithFullNameWildcardMatch(
"System.GC.Collect(*)").ToHashSet()
from m in Application.Methods.UsingAny(gcCollectMethods)
select new {
m,
gcCollectMethodCalled = m.MethodsCalled.Intersect(gcCollectMethods)
}
//<Description>
// It is preferable to avoid calling **GC.Collect()**
// explicitly in order to avoid some performance pitfall.
//
// More in information on this here:
// http://blogs.msdn.com/ricom/archive/2004/11/29/271829.aspx
//
// This rule matches application methods that call an
// overload of the method *GC.Collect()*.
//</Description>
//<HowToFix>
// Remove matched calls to *GC.Collect()*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Don't call GC.Collect() without calling GC.WaitForPendingFinalizers()</Name>
warnif count > 0
let gcCollectMethods = ThirdParty.Methods.WithFullNameWildcardMatch(
"System.GC.Collect(*)").ToHashSet()
from m in Application.Methods.UsingAny(gcCollectMethods) where
!m.IsUsing ("System.GC.WaitForPendingFinalizers()".AllowNoMatch())
select new {
m,
gcCollectMethodCalled = m.MethodsCalled.Intersect(gcCollectMethods)
}
//<Description>
// It is preferable to avoid calling **GC.Collect()**
// explicitly in order to avoid some performance
// pitfall. This situation is checked through the
// default rules: *Remove calls to GC.Collect()*
//
// But if you wish to call *GC.Collect()* anyway,
// you must do it this way:
//
// GC.Collect();
//
// GC.WaitForPendingFinalizers();
//
// GC.Collect();
//
// To make sure that finalizer got executed, and
// object with finalizer got cleaned properly.
//
// This rule matches application methods that call an
// overload of the method *GC.Collect()*, without calling
// *GC.WaitForPendingFinalizers()*.
//</Description>
//<HowToFix>
// To fix a violation of this rule, if you really
// need to call *GC.Collect()*, make sure to call
// *GC.WaitForPendingFinalizers()* properly.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Enum Storage should be Int32</Name>
warnif count > 0 from f in Fields where
f.Name == @"value__" &&
!f.FieldTypeIs ("System.Int32".AllowNoMatch()) &&
!f.IsThirdParty
select new { f, f.SizeOfInst, f.FieldType }
//<Description>
// An enumeration is a value type that defines a set of related named constants.
// By default, the **System.Int32** data type is used to store the constant value.
//
// Even though you can change this underlying type, it is not necessary or
// recommended for most scenarios. Note that *no significant performance gain* is
// achieved by using a data type that is smaller than *Int32*. If you cannot use
// the default data type, you should use one of the Common Language System
// (CLS)-compliant integral types, *Byte*, *Int16*, *Int32*, or *Int64* to make
// sure that all values of the enumeration can be represented in CLS-compliant
// programming languages.
//
// This rule matches enumerations whose underlying type used to store
// values is not *System.Int32*.
//</Description>
//<HowToFix>
// To fix a violation of this rule, unless size or compatibility issues exist,
// use *Int32*. For situations where *Int32* is not large enough to hold the values,
// use *Int64*. If backward compatibility requires a smaller data type, use
// *Byte* or *Int16*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Do not raise too general exception types</Name>
warnif count > 0 from m in Application.Methods where
// Test for non-constructor, else this rule
// would warn on ctor of classes that derive
// from these exception types.
!m.IsConstructor && (
m.CreateA("System.Exception".AllowNoMatch()) ||
m.CreateA("System.ApplicationException".AllowNoMatch()) ||
m.CreateA("System.SystemException".AllowNoMatch()) )
select m
//<Description>
// The following exception types are too general
// to provide sufficient information to the user:
//
// • System.Exception
//
// • System.ApplicationException
//
// • System.SystemException
//
// If you throw such a general exception type in a library or framework,
// it forces consumers to catch all exceptions,
// including unknown exceptions that they do not know how to handle.
//
// This rule matches methods that create an instance of
// such general exception class.
//</Description>
//<HowToFix>
// To fix a violation of this rule, change the type of the thrown exception
// to either a more derived type that already exists in the framework,
// or create your own type that derives from *System.Exception*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Do not raise reserved exception types</Name>
warnif count > 0
let reservedExceptions = ThirdParty.Types.WithFullNameIn(
"System.ExecutionEngineException",
"System.IndexOutOfRangeException",
"System.NullReferenceException",
"System.OutOfMemoryException",
"System.StackOverflowException",
"System.InvalidProgramException",
"System.AccessViolationException",
"System.CannotUnloadAppDomainException",
"System.BadImageFormatException",
"System.DataMisalignedException")
from m in Application.Methods.ThatCreateAny(reservedExceptions)
let reservedExceptionsCreated = reservedExceptions.Where(t => m.IsUsing(t))
select new { m, reservedExceptionsCreated }
//<Description>
// The following exception types are reserved
// and should be thrown only by the Common Language Runtime:
//
// • System.ExecutionEngineException
//
// • System.IndexOutOfRangeException
//
// • System.NullReferenceException
//
// • System.OutOfMemoryException
//
// • System.StackOverflowException
//
// • System.InvalidProgramException
//
// • System.AccessViolationException
//
// • System.CannotUnloadAppDomainException
//
// • System.BadImageFormatException
//
// • System.DataMisalignedException
//
// Do not throw an exception of such reserved type.
//
// This rule matches methods that create an instance of
// such reserved exception class.
//</Description>
//<HowToFix>
// To fix a violation of this rule, change the type of the
// thrown exception to a specific type that is not one of
// the reserved types.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Use integral or string argument for indexers</Name>
warnif count > 0
from m in Application.Methods where
m.IsIndexerGetter &&
!( (m.Name == @"get_Item(String)") ||
m.NameLike (@"get_Item\(Int") ||
m.NameLike (@"get_Item\(Byte") ||
m.NameLike (@"get_Item\(SByte") )
select m
//<Description>
// Indexers, that is, indexed properties, should use *integer* or *string*
// types for the index. These types are typically used for indexing data
// structures and increase the usability of the library. Use of the *Object*
// type should be restricted to those cases where the specific *integer* or
// *string* type cannot be specified at design time. If the design requires
// other types for the index, reconsider whether the type represents a
// logical data store. If it does not represent a logical data store,
// use a method.
//
// This rule matches indexer getter methods that whose index type
// is not *string*, *int*, *byte* or *sbyte*.
//</Description>
//<HowToFix>
// To fix a violation of this rule, change the index to an *integer* or *string*
// type, or use a method instead of the indexer.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Uri fields should be of type System.Uri</Name>
warnif count > 0 from f in Application.Fields where
(f.NameLike (@"Uri$") ||
f.NameLike (@"Url$")) &&
!f.FieldTypeIs ("System.Uri".AllowNoMatch())
select new {
f,
f.FieldType
}
//<Description>
// A field with the name ending with *'Uri'* or *'Url'* is deemed
// to represent a *Uniform Resource Identifier or Locator*.
// Such field should be of type **System.Uri**.
//
// This rule matches fields with the name ending with *'Uri'* or
// *'Url'* that are not typed with *System.Uri*.
//</Description>
//<HowToFix>
// Rename the field, or change the field type to *System.Uri*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types should not extend System.ApplicationException</Name>
warnif count > 0 from t in Application.Types where
t.DeriveFrom("System.ApplicationException".AllowNoMatch())
select t
//<Description>
// At .NET Framework version 1 time, it was
// recommended to derive new exceptions from
// *ApplicationException*.
//
// The recommendation has changed and new
// exceptions should derive from **System.Exception**
// or one of its subclasses in the *System* namespace.
//
// This rule matches application exception classes
// that derive from *ApplicationException*.
//</Description>
//<HowToFix>
// Make sure that matched exception types,
// derive from **System.Exception** or one of its
// subclasses in the *System* namespace.
//</HowToFix>]]></Query>
</Group>
<Group Name="System.Collection" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Collection properties should be read only</Name>
warnif count > 0
// First find collectionTypes
let collectionInterfaces = ThirdParty.Types.WithFullNameIn(
"System.Collections.ICollection",
"System.Collections.Generic.ICollection<T>")
where collectionInterfaces.Count() > 0
let collectionTypes = Types.ThatImplementAny(collectionInterfaces)
.Union(collectionInterfaces)
.ToHashSet()
// Then find all property setters that have an associated
// getter that returns a collection type.
from propGetter in Application.Methods.Where(
m => m.IsPropertyGetter &&
m.ReturnType != null &&
collectionTypes.Contains(m.ReturnType))
let propSetter = propGetter.ParentType.Methods.WithSimpleName(
propGetter.SimpleName.Replace("get_","set_")
).FirstOrDefault()
where propSetter != null &&
!propSetter.IsPrivate &&
!propSetter.ParentType.IsPrivate // ignore setters of private types
select new {
propSetter,
CollectionType = propGetter.ReturnType
}
//<Description>
// A writable collection property allows a user to replace the collection with
// a completely different collection. A read-only property stops the collection
// from being replaced but still allows the individual members to be set. If
// replacing the collection is a goal, the preferred *design pattern* is to include
// a method to remove all the elements from the collection and a method to
// re-populate the collection. See the *Clear()* and *AddRange()* methods of the
// *System.Collections.Generic.List<T>* class for an example of this pattern.
//
// Both binary and XML serialization support read-only properties that are
// collections. The *System.Xml.Serialization.XmlSerializer* class has specific
// requirements for types that implement *ICollection* and *System.Collections.IEnumerable*
// in order to be serializable.
//
// This rule matches property setter methods that assign a collection object.
//</Description>
//<HowToFix>
// To fix a violation of this rule, make the property read-only and, if
// the design requires it, add methods to clear and re-populate the collection.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Don't use .NET 1.x HashTable and ArrayList</Name>
warnif count > 0
let forbiddenTypes = ThirdParty.Types.WithFullNameIn(
"System.Collections.HashTable",
"System.Collections.ArrayList",
"System.Collections.Queue",
"System.Collections.Stack",
"System.Collections.SortedList")
where forbiddenTypes.Count() > 0
from m in Application.Methods.ThatCreateAny(forbiddenTypes)
select m
//<Description>
// This rule warns about application methods that use a non-generic
// collection class, including **ArrayList**, **HashTable**, **Queue**,
// **Stack** or **SortedList**.
//</Description>
//<HowToFix>
// **List<T>** should be preferred over **ArrayList**.
// It is generic hence you get strongly typed elements.
// Also, it is faster with *T* as a value types since it avoids boxing.
//
// For the same reasons:
//
// • **Dictionary<K,V>** should be prevered over **HashTable**.
//
// • **Queue<T>** should be prevered over **Queue**.
//
// • **Stack<T>** should be prevered over **Stack**.
//
// • **SortedDictionary<K,V>** or **SortedList<K,V>** should be prevered over **SortedList**.
//
// You can be forced to use *non generic* collections
// because you are using third party code that requires
// working with these classes or because you are
// coding with .NET 1.x, but nowadays this situation should
// question about using newer updates of .NET.
// .NET 1.x is an immature platform conpared to newer .NET
// updates.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Caution with List.Contains()</Name>
let containsMethods = ThirdParty.Methods.WithFullNameIn(
"System.Collections.Generic.List<T>.Contains(T)",
"System.Collections.Generic.IList<T>.Contains(T)",
"System.Collections.ArrayList.Contains(Object)")
from m in Application.Methods.UsingAny(containsMethods)
select m
//<Description>
// This code query matches calls to *List<T>.Contains()* method.
//
// The cost of checking if a list contains an object is proportional
// to the size of the list. In other words it is a *O(N)* operation.
// For large lists and/or frequent calls to *Contains()*, prefer using
// the *System.Collections.Generic.HashSet<T>* class
// where calls to *Contains()* take a constant
// time (*O(0)* operation).
//
// This code query is not a code rule, because more often than not,
// calling *O(N) Contains()* is not a mistake. This code query
// aims at pointing out this potential performance pitfall.
//</Description>]]></Query>
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Prefer return collection abstraction instead of implementation</Name>
let implTypes = ThirdParty.Types.WithFullNameIn(
"System.Collections.Generic.List<T>",
"System.Collections.Generic.HashSet<T>",
"System.Collections.Generic.Dictionary<TKey,TValue>")
from m in Application.Methods.WithReturnTypeIn(implTypes)
select new { m, m.ReturnType }
//<Description>
// This code query matches methods that return a
// collection implementation, such as *List<T>*
// *HashSet<T>* or *Dictionary<TKey,TValue>*.
//
// Most often than not, clients of a method don't
// need to know the exact implementation of the
// collection returned. It is preferable to return
// a collection interface such as *IList<T>*,
// *ICollection<T>*, *IEnumerable<T>* or
// *IDictionary<TKey,TValue>*.
//
// Using the collection interface instead of the
// implementation shouldn't applies to all cases,
// hence this code query is not code rule.
//</Description>]]></Query>
</Group>
<Group Name="System.Runtime.InteropServices" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>P/Invokes should be static and not be visible</Name>
warnif count > 0 from m in Application.Methods where
!m.IsThirdParty &&
(m.HasAttribute ("System.Runtime.InteropServices.DllImportAttribute".AllowNoMatch())) &&
( m.IsPublic ||
!m.IsStatic)
select new { m, m.Visibility, m.IsStatic }
//<Description>
// Methods that are marked with the **DllImportAttribute** attribute
// (or methods that are defined by using the **Declare** keyword in Visual Basic)
// use **Platform Invocation Services** to access unmanaged code.
//
// Such methods should not be exposed. By keeping these methods *private* or *internal*,
// you make sure that your library cannot be used to breach security by allowing
// callers access to unmanaged APIs that they could not call otherwise.
//
// This rule matches methods tagged with *DllImportAttribute* attribute
// that are declared as *public* or declared as *non-static*.
//</Description>
//<HowToFix>
// To fix a violation of this rule, change the access level of the method
// and/or declare it as static.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Move P/Invokes to NativeMethods class</Name>
warnif count > 0 from m in Application.Methods where
m.HasAttribute ("System.Runtime.InteropServices.DllImportAttribute".AllowNoMatch()) &&
m.ParentType.Name != "NativeMethods"
select m
//<Description>
// **Platform Invocation methods**, such as those that are marked by using the
// **System.Runtime.InteropServices.DllImportAttribute** attribute, or methods
// that are defined by using the **Declare** keyword in Visual Basic, access
// unmanaged code. These methods should be in one of the following classes:
//
// • **NativeMethods** - This class does not suppress stack walks for unmanaged
// code permission. (*System.Security.SuppressUnmanagedCodeSecurityAttribute*
// must not be applied to this class.) This class is for methods that can be
// used anywhere because a stack walk will be performed.
//
// • **SafeNativeMethods** - This class suppresses stack walks for unmanaged
// code permission. (*System.Security.SuppressUnmanagedCodeSecurityAttribute*
//is applied to this class.) This class is for methods that are safe for anyone
// to call. Callers of these methods are not required to perform a full security
// review to make sure that the usage is secure because the methods are harmless
// for any caller.
//
// • **UnsafeNativeMethods** - This class suppresses stack walks for unmanaged
// code permission. (*System.Security.SuppressUnmanagedCodeSecurityAttribute*
// is applied to this class.) This class is for methods that are potentially
// dangerous. Any caller of these methods must perform a full security review
// to make sure that the usage is secure because no stack walk will be performed.
//
// These classes are declared as *static internal*. The methods in these
// classes are *static* and *internal*.
//
// This rule matches *P/Invoke* methods not declared in such *NativeMethods*
// class.
//</Description>
//<HowToFix>
// To fix a violation of this rule, move the method to the appropriate
// **NativeMethods** class. For most applications, moving P/Invokes to a new
// class that is named **NativeMethods** is enough.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>NativeMethods class should be static and internal</Name>
warnif count > 0 from t in Application.Types.WithNameIn(
@"NativeMethods", "SafeNativeMethods", "UnsafeNativeMethods") where
t.IsPublic || !t.IsStatic
select new { t, t.Visibility, t.IsStatic }
//<Description>
// In the description of the default rule *Move P/Invokes to NativeMethods class*
// it is explained that *NativeMethods* classes that host *P/Invoke* methods,
// should be declared as *static* and *internal*.
//
// This code rule warns about *NativeMethods* classes that are not declared
// *static* and *internal*.
//</Description>
//<HowToFix>
// Matched *NativeMethods* classes must be declared as *static* and *internal*.
//</HowToFix>]]></Query>
</Group>
<Group Name="System.Threading" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Method non-synchronized that read mutable states</Name>
from m in Application.Methods where
(m.ReadsMutableObjectState || m.ReadsMutableTypeState) &&
!m.IsUsing ("System.Threading.Monitor".AllowNoMatch()) &&
!m.IsUsing ("System.Threading.ReaderWriterLock".AllowNoMatch())
select new {
m,
mutableFieldsUsed = m.FieldsUsed.Where(f => !f.IsImmutable)
}
//<Description>
// Mutable object states are instance fields that
// can be modified through the lifetime of the object.
//
// Mutable type states are static fields that can be
// modified through the lifetime of the program.
//
// This query lists methods that read mutable state
// without synchronizing access. In the case of
// multi-threaded program, doing so can lead to
// state corruption.
//
// This code query is not a code rule because more often
// than not, a match of this query is not an issue.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Don't create threads explicitly</Name>
warnif count > 0 from m in Application.Methods where
m.CreateA ("System.Threading.Thread".AllowNoMatch())
select m
//<Description>
// This code rule warns about methods that create *threads* explicitly
// by creating an instance of the class *System.Threading.Thread*.
//
// Prefer using the thread pool instead of creating manually your
// own threads. Threads are costly objects. They take approximately
// 200,000 cycles to create and about 100,000 cycles to destroy.
// By default they reserve 1 Mega Bytes of virtual memory for its
// stack and use 2,000-8,000 cycles for each context switch.
//
// As a consequence, it is preferable to let the thread pool
// recycle threads.
//</Description>
//<HowToFix>
// Instead of creating explicitly threads, use the **Task Parralel
// Library** *(TPL)* that relies on the CLR thread pool.
//
// Introduction to TPL: https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx
//
// TPL and the CLR v4 thread pool:
// http://www.danielmoth.com/Blog/New-And-Improved-CLR-4-Thread-Pool-Engine.aspx
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Don't use dangerous threading methods</Name>
warnif count > 0
let wrongMethods = ThirdParty.Methods.WithFullNameIn(
"System.Threading.Thread.Abort()",
"System.Threading.Thread.Abort(Object)",
"System.Threading.Thread.Sleep(Int32)",
"System.Threading.Thread.Suspend()",
"System.Threading.Thread.Resume()")
from m in Application.Methods.UsingAny(wrongMethods)
select new {
m,
suppressCallsTo = m.MethodsCalled.Intersect(wrongMethods)
}
//<Description>
// This rule warns about using the methods
// *Abort()*, *Sleep()*, *Suspend()* or *Resume()*
// declared by the *Thread* class.
//
// • Usage of *Thread.Abort()* is dangerous.
// More information on this here:
// http://www.interact-sw.co.uk/iangblog/2004/11/12/cancellation
//
// • Usage of *Thread.Sleep()* is a sign of
// flawed design. More information on this here:
// http://msmvps.com/blogs/peterritchie/archive/2007/04/26/thread-sleep-is-a-sign-of-a-poorly-designed-program.aspx
//
// • *Suspend()* and *Resume()* are dangerous threading methods, marked as obsolete.
// More information on workaround here:
// http://stackoverflow.com/questions/382173/what-are-alternative-ways-to-suspend-and-resume-a-thread
//</Description>
//<HowToFix>
// Suppress calls to *Thread* methods exposed in the
// *suppressCallsTo* column in the rule result.
//
// Use instead facilities offered by the **Task Parralel
// Library** *(TPL)* :
// https://msdn.microsoft.com/en-us/library/dd460717(v=vs.110).aspx
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Monitor TryEnter/Exit must be both called within the same method</Name>
warnif count > 0
let enterMethods = ThirdParty.Methods.WithFullNameWildcardMatchIn(
"System.Threading.Monitor.Enter(*",
"System.Threading.Monitor.TryEnter(*")
from m in Application.Methods.UsingAny(enterMethods) where
!m.IsUsing ("System.Threading.Monitor.Exit(Object)".AllowNoMatch())
select new {
m,
enterMethodsCalled = m.MethodsCalled.Intersect(enterMethods)
}
//<Description>
// This rule warns when **System.Threading.Monitor** *Enter()*
// (or *TryEnter()*) and *Exit() methods are not called within
// the same method.
//
// Doing so makes the code *less readable*, because it gets harder
// to locate when **critical sections** begin and end.
//
// Also, you expose yourself to complex and error-prone scenarios.
//</Description>
//<HowToFix>
// Refactor matched methods to make sure that *Monitor critical
// sections* begin and end within the same method. Basics scenarios
// can be handled through the C# **lock** keyword. Using explicitly
// the class *Monitor* should be left for advanced situations,
// that require calls to methods like *Wait()* and *Pulse()*.
//
// More information on using the *Monitor* class can be found here:
// http://www.codeproject.com/Articles/13453/Practical-NET-and-C-Chapter
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>ReaderWriterLock AcquireLock/ReleaseLock must be both called within the same method</Name>
warnif count > 0
let acquireLockMethods = ThirdParty.Methods.WithFullNameWildcardMatch(
"System.Threading.ReaderWriterLock.Acquire*Lock(*")
let releaseLockMethods = ThirdParty.Methods.WithFullNameWildcardMatch(
"System.Threading.ReaderWriterLock.Release*Lock(*")
from m in Application.Methods.UsingAny(acquireLockMethods)
.Except(Application.Methods.UsingAny(releaseLockMethods))
select new {
m,
acquireLockMethods = m.MethodsCalled.Intersect(acquireLockMethods)
}
//<Description>
// This rule warns when **System.Threading.ReaderWriterLock**
// acquire and release, reader or writer locks methods are not called
// within the same method.
//
// Doing so makes the code *less readable*, because it gets harder
// to locate when **critical sections** begin and end.
//
// Also, you expose yourself to complex and error-prone scenarios.
//</Description>
//<HowToFix>
// Refactor matched methods to make sure that *ReaderWriterLock
// read or write critical sections* begin and end within the
// same method.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Don't tag instance fields with ThreadStaticAttribute</Name>
warnif count > 0
from f in Application.Fields
where !f.IsStatic &&
f.HasAttribute ("System.ThreadStaticAttribute".AllowNoMatch())
select f
//<Description>
// This rule warns when the attribute **System.ThreadStaticAttribute**
// is tagging *instance* fields. As explained in documentation, this attribute
// is designed to tag only *static* fields.
// https://msdn.microsoft.com/en-us/library/system.threadstaticattribute
//</Description>
//<HowToFix>
// Refactor the code to make sure that all fields tagged with
// *ThreadStaticAttribute* are *static*.
//</HowToFix>]]></Query>
</Group>
<Group Name="System.Xml" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Method should not return concrete XmlNode</Name>
warnif count > 0
let concreteXmlTypes = ThirdParty.Types.ThatDeriveFromAny(
ThirdParty.Types.WithFullName("System.Xml.XmlNode"))
from m in Application.Methods.WithReturnTypeIn(concreteXmlTypes)
select new { m, m.ReturnType }
//<Description>
// This rule warns about method whose return type is
// **System.Xml.XmlNode** or any type derived from *XmlNode*.
//
// *XmlNode* implements the interface **System.Xml.Xpath.IXPathNavigable**.
// In most situation, returning this interface instead of the concrete
// type is a better *design* choice that will abstract client code
// from implementation details.
//</Description>
//<HowToFix>
// To fix a violation of this rule, change the concrete returned type
// to the suggested interface *IXPathNavigable*.
//</HowToFix>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types should not extend System.Xml.XmlDocument</Name>
warnif count > 0 from t in Application.Types where
t.DeriveFrom("System.Xml.XmlDocument".AllowNoMatch())
select t
//<Description>
// This rule warns aboud subclasses of **System.Xml.XmlDocument**.
//
// Do not create a subclass of *XmlDocument* if you want to
// create an XML view of an underlying object model or data source.
//</Description>
//<HowToFix>
// Instead of subclassing *XmlDocument*, you can use the interface
// **System.Xml.XPath.IXPathNavigable** implemented by the class
// *XmlDocument*.
//
// An alternative of using *XmlDocument*, is to use
// **System.Xml.Linq.XDocument**, aka **LINQ2XML**.
// More information on this can be found here:
// http://stackoverflow.com/questions/1542073/xdocument-or-xmldocument
//</HowToFix>]]></Query>
</Group>
<Group Name="System.Globalization" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Float and Date Parsing must be culture aware</Name>
warnif count > 0
from m in ThirdParty.Types.WithFullNameIn(
"System.DateTime",
"System.Single",
"System.Double",
"System.Decimal").ChildMethods()
where m.NbParameters > 0 &&
(m.SimpleName.EqualsAny(
"Parse", "TryParse", "ToString")) &&
!m.Name.Contains("IFormatProvider")
select new {
m,
m.MethodsCallingMe
}
//<Description>
// Globalization is the design and development of applications that support
// localized user interfaces and regional data for users in multiple cultures.
//
// This rule warns about the usage of *non-globalized overloads* of
// the methods **Parse()**, **TryParse()** and **ToString()**,
// of the types **DateTime**, **float**, **double** and **decimal**.
// This is the symptom that your application is *at least partially*
// not globalized.
//
// *Non-globalized overloads* of these methods are the overloads
// that don't take a parameter of type **IFormatProvider**.
//</Description>
//<HowToFix>
// Globalize your applicaton and make sure to use the globalized overloads
// of these methods. In the column **MethodsCallingMe** of this rule result
// are listed the methods of your application that call the
// *non-globalized overloads*.
//
// More information on **Creating Globally Aware Applications** here:
// https://msdn.microsoft.com/en-us/library/cc853414(VS.95).aspx
//</HowToFix>]]></Query>
</Group>
<Group Name="Microsoft.Contracts" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Public methods returning a reference needs a contract to ensure that a non-null reference is returned</Name>
warnif count > 0
let ensureMethods = Application.Methods.WithFullName(
"System.Diagnostics.Contracts.__ContractsRuntime.Ensures(Boolean,String,String)")
from ensureMethod in ensureMethods
from m in ensureMethod.ParentAssembly.ChildMethods where
m.IsPubliclyVisible &&
!m.IsAbstract &&
m.ReturnType != null &&
// Identify that the return type is a reference type
(m.ReturnType.IsClass || m.ReturnType.IsInterface) &&
!m.IsUsing(ensureMethod) &&
// Don't match method not implemented yet!
!m.CreateA("System.NotImplementedException".AllowNoMatch())
select new {
m,
ReturnTypeReference = m.ReturnType
}
//<Description>
// **Code Contracts** are useful to decrease ambiguity between callers and callees.
// Not ensuring that a reference returned by a method is *non-null* leaves ambiguity
// for the caller. This rule matches methods returning an instance of a reference type
// (class or interface) that don't use a **Contract.Ensure()** method.
//
// *Contract.Ensure()* is defined in the **Microsoft Code Contracts for .NET**
// library, and is typically used to write a code contract on returned reference:
// *Contract.Ensures(Contract.Result<ReturnType>() != null, "returned reference is not null");*
// https://visualstudiogallery.msdn.microsoft.com/1ec7db13-3363-46c9-851f-1ce455f66970
//</Description>
//<HowToFix>
// Use *Microsoft Code Contracts for .NET* on the public surface of your API,
// to remove most ambiguity presented to your client. Most of such ambiguities
// are about *null* or *not null* references.
//
// Don't use *null* reference if you need to express that a method might not
// return a result. Use instead the **TryXXX()** pattern exposed for example
// in the *System.Int32.TryParse()* method.
//</HowToFix>]]></Query>
</Group>
<Group Name="Third-Party Code Elements Used" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Third-Party Assemblies Used</Name>
from a in ThirdParty.Assemblies
select new { a, a.AssembliesUsingMe }
//<Description>
// This code query lists *third-party assemblies* used
// by the application analyzed.
//
// For each third-party assembly matched, the column
// *AssembliesUsingMe* of the query result contains
// application assemblies using the assembly.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Third-Party Namespaces Used</Name>
from n in ThirdParty.Namespaces
select new { n, n.NamespacesUsingMe }
//<Description>
// This code query lists *third-party namespaces* used
// by the application analyzed.
//
// For each third-party namespace matched, the column
// *NamespacesUsingMe* of the query result contains
// application namespaces using the namespace.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Third-Party Types Used</Name>
from t in ThirdParty.Types
select new { t, t.TypesUsingMe, t.DerivedTypes, t.TypesThatImplementMe }
//<Description>
// This code query lists *third-party types* used
// by the application analyzed.
//
// For each third-party type matched:
//
// • The column *TypesUsingMe* of the query result contains
// application types using the third-party type.
//
// • The column *DerivedTypes* of the query result contains
// application sub-classes of the third-party class.
//
// • The column *TypesThatImplementMe* of the query result
// contains application classes that implement the third-party interface.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Third-Party Methods Used</Name>
from m in ThirdParty.Methods
select new { m, m.MethodsCallingMe }
//<Description>
// This code query lists *third-party methods* called
// by the application analyzed.
//
// For each third-party method matched, the column
// *MethodsCallingMe* of the query result contains
// application methods that call the method.
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Third-Party Fields Used</Name>
from f in ThirdParty.Fields
where !f.ParentType.IsEnumeration
select new { f, f.MethodsReadingMeButNotAssigningMe, f.MethodsAssigningMe }
//<Description>
// This code query lists *third-party fields* called
// by the application analyzed.
//
// For each third-party field matched, the column
// *MethodsReadingMeButNotAssigningMe* and *MethodsAssigningMe*
// of the query result contains application methods that read
// or assign the field.
//</Description>]]></Query>
</Group>
</Group>
<Group Name="Trend Metrics" Active="True" ShownInReport="False">
<Group Name="Code Size" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Lines of Code" Unit="LoC" />
Application.Assemblies.Sum(a => a.NbLinesOfCode)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Lines of Code (JustMyCode)" Unit="LoC" />
JustMyCode.Methods.Sum(m => m.NbLinesOfCode)
// JustMyCode is defined by code queries prefixed with 'notmycode'
// in the group 'Defining JustMyCode'.
]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Lines of Code (NotMyCode)" Unit="LoC" />
Application.Methods.Where(m => !JustMyCode.Contains(m))
.Sum(m => m.NbLinesOfCode)
// JustMyCode is defined by code queries prefixed with 'notmycode'
// in the group 'Defining JustMyCode'.
]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Lines of Code Added since the Baseline" Unit="LoC" />
( from a in Application.Assemblies
let nbLocAdded = !a.IsPresentInBothBuilds()
? a.NbLinesOfCode
: (a.NbLinesOfCode != null && a.OlderVersion().NbLinesOfCode != null)
? a.NbLinesOfCode - (int)a.OlderVersion().NbLinesOfCode
: 0
select nbLocAdded)
.Sum(loc => loc)
// A value is computed by this Trend Metric query
// only if a Baseline for Comparison is provided.
// See Project Properties > Analysis > Baseline for Comparison
]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Source Files" Unit="Source Files" />
Application.Assemblies.SelectMany(
a => a.SourceDecls.Select(sd => sd.SourceFile.FilePathString.ToLower()))
.Distinct()
.Count()
// If a value 0 is obtained, it means that at analysis time,
// assemblies PDB files were not available.
// http://www.ndepend.com/docs/ndepend-analysis-inputs-explanation
]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# IL Instructions" Unit="IL Instructions" />
Application.Assemblies.Sum(a => a.NbILInstructions)
]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# IL Instructions (NotMyCode)" Unit="IL Instructions" />
Application.Methods.Where(m => !JustMyCode.Contains(m))
.Sum(m => m.NbILInstructions)
// JustMyCode is defined by code queries prefixed with 'notmycode'
// in the group 'Defining JustMyCode'.
]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Lines of Comments" Unit="Lines" />
Application.Assemblies.Sum(a => a.NbLinesOfComment)
// So far comments are only extracted from C# source code.
]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Assemblies" Unit="Assemblies" />
Application.Assemblies.Count()]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Namespaces" Unit="Namespaces" />
Application.Namespaces.Count()]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Types" Unit="Types" />
Application.Types.Count(t => !t.IsGeneratedByCompiler)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Public Types" Unit="Types" />
Application.Types.Where(t => t.IsPubliclyVisible && !t.IsGeneratedByCompiler).Count()]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Classes" Unit="Types" />
Application.Types.Count(t => t.IsClass && !t.IsGeneratedByCompiler)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Abstract Classes" Unit="Types" />
Application.Types.Count(t => t.IsClass && t.IsAbstract && !t.IsGeneratedByCompiler)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Interfaces" Unit="Types" />
Application.Types.Count(t => t.IsInterface)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Structures" Unit="Types" />
Application.Types.Count(t => t.IsStructure && !t.IsGeneratedByCompiler)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Methods" Unit="Methods" />
Application.Methods.Count(m => !m.IsGeneratedByCompiler)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Abstract Methods" Unit="Methods" />
Application.Methods.Count(m => m.IsAbstract)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Concrete Methods" Unit="Methods" />
Application.Methods.Count(m => !m.IsAbstract && !m.IsGeneratedByCompiler)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Fields" Unit="Fields" />
Application.Fields.Count(f =>
!f.IsEnumValue &&
!f.IsGeneratedByCompiler &&
!f.IsLiteral &&
!f.ParentType.IsEnumeration)]]></Query>
</Group>
<Group Name="Maximum and Average" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Max # Lines of Code for Methods (JustMyCode)" Unit="LoC" />
JustMyCode.Methods
.Max(m => m.NbLinesOfCode)
.ToEnumerable().Sum(loc => loc)
// Here is the code query to get the (JustMyCode) method with largest # Lines of Code
// JustMyCode.Methods.OrderByDescending(m => m.NbLinesOfCode).Take(1).Select(m => new {m, m.NbLinesOfCode})]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Average # Lines of Code for Methods" Unit="LoC" />
Application.Methods.Where(m => m.NbLinesOfCode > 0)
.Average(m => m.NbLinesOfCode)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Average # Lines of Code for Methods with at least 3 Lines of Code" Unit="LoC" />
Application.Methods.Where(m => m.NbLinesOfCode >= 3)
.Average(m => m.NbLinesOfCode)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Max # Lines of Code for Types (JustMyCode)" Unit="LoC" />
JustMyCode.Types
.Max(t => t.NbLinesOfCode)
.ToEnumerable().Sum(loc => loc)
// Here is the code query to get the (JustMyCode) type with largest # Lines of Code
// JustMyCode.Types.OrderByDescending(t => t.NbLinesOfCode).Take(1).Select(t => new {t, t.NbLinesOfCode})]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Average # Lines of Code for Types" Unit="LoC" />
Application.Types.Where(t => t.NbLinesOfCode > 0)
.Average(t => t.NbLinesOfCode)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Max Cyclomatic Complexity for Methods" Unit="Paths" />
Application.Methods
.Max(m => m.CyclomaticComplexity)
.ToEnumerable().Sum(loc => loc)
// Here is the code query to get the most complex method, according to Cyclomatic Complexity
// Application.Methods.OrderByDescending(m => m.CyclomaticComplexity).Take(1).Select(m => new {m, m.CyclomaticComplexity})]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Average Cyclomatic Complexity for Methods" Unit="Paths" />
Application.Methods.Where(m => m.NbLinesOfCode> 0)
.Average(m => m.CyclomaticComplexity)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Max IL Cyclomatic Complexity for Methods" Unit="Paths" />
Application.Methods
.Max(m => m.ILCyclomaticComplexity)
.ToEnumerable().Sum(loc => loc)
// Here is the code query to get the most complex method, according to Cyclomatic Complexity computed from IL code.
// Application.Methods.OrderByDescending(m => m.ILCyclomaticComplexity).Take(1).Select(m => new {m, m.CyclomaticComplexity})]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Average IL Cyclomatic Complexity for Methods" Unit="Paths" />
Application.Methods.Where(m => m.NbILInstructions> 0)
.Average(m => m.ILCyclomaticComplexity)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Max IL Nesting Depth for Methods" Unit="Scopes" />
Application.Methods
.Max(m => m.ILNestingDepth)
.ToEnumerable().Sum(loc => loc)
// Here is the code query to get the method with highest ILNestingDepth.
// Application.Methods.OrderByDescending(m => m.ILNestingDepth).Take(1).Select(m => new {m, m.ILNestingDepth})]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Average IL Nesting Depth for Methods" Unit="Scopes" />
Application.Methods.Where(m => m.NbILInstructions> 0)
.Average(m => m.ILNestingDepth)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Max # of Methods for Types" Unit="Methods" />
Application.Types
.Max(t => t.NbMethods)
.ToEnumerable().Sum(loc => loc)
// Here is the code query to get the (JustMyCode) type with largest # of Methods
// JustMyCode.Types.OrderByDescending(t => t.NbMethods).Take(1).Select(t => new {t, t.Methods})]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Average # Methods for Types" Unit="Methods" />
Application.Types.Average(t => t.NbMethods)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Max # of Methods for Interfaces" Unit="Methods" />
Application.Types.Where(t => t.IsInterface)
.Max(t => t.NbMethods)
.ToEnumerable().Sum(loc => loc)
// Here is the code query to get the (JustMyCode) type with largest # of Methods
// JustMyCode.Types.OrderByDescending(t => t.NbMethods).Take(1).Select(t => new {t, t.Methods})]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Average # Methods for Interfaces" Unit="Methods" />
JustMyCode.Types.Where(t => t.IsInterface)
.Average(t => t.NbMethods)]]></Query>
</Group>
<Group Name="Code Coverage" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Percentage Code Coverage" Unit="%" />
((float)Application.Assemblies.Sum(a => a.NbLinesOfCodeCovered) /
Application.Assemblies.Sum(a => a.NbLinesOfCode)
* 100f)
.ToEnumerable().Sum()]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Lines of Code Covered" Unit="LoC" />
Application.Assemblies.Sum(a => a.NbLinesOfCodeCovered)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Lines of Code Not Covered" Unit="LoC" />
Application.Assemblies.Sum(a => a.NbLinesOfCodeNotCovered)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Lines of Code in Types 100% Covered" Unit="LoC" />
Application.Types.Where(t => t.PercentageCoverage == 100)
.Sum(t => t.NbLinesOfCodeCovered)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Lines of Code in Methods 100% Covered" Unit="LoC" />
Application.Methods.Where(m => m.PercentageCoverage == 100)
.Sum(m => m.NbLinesOfCodeCovered)]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Max C.R.A.P Score" />
(from m in JustMyCode.Methods
// Don't match too short methods
where m.NbLinesOfCode > 10
let CC = m.CyclomaticComplexity
let uncov = (100 - m.PercentageCoverage) / 100f
let CRAP = (CC * CC * uncov * uncov * uncov) + CC
where CRAP != null && CRAP > 30 select CRAP)
.Max(CRAP => CRAP)
//<Description>
// **Change Risk Analyzer and Predictor** (i.e. CRAP) is a code metric
// that helps in pinpointing overly complex and untested code.
// Is has been first defined here:
// http://www.artima.com/weblogs/viewpost.jsp?thread=215899
//
// The Formula is: **CRAP(m) = CC(m)^2 * (1 cov(m)/100)^3 + CC(m)**
//
// • where *CC(m)* is the *cyclomatic complexity* of the method *m*
//
// • and *cov(m)* is the *percentage coverage* by tests of the method *m*
//
// Matched methods cumulates two highly *error prone* code smells:
//
// • A complex method, difficult to develop and maintain.
//
// • Non 100% covered code, difficult to refactor without any regression bug.
//
// The highest the CRAP score, the more painful to maintain and error prone is the method.
//
// An arbitrary threshold of 30 is fixed for this code rule as suggested by inventors.
//
// Notice that no amount of testing will keep methods with a Cyclomatic Complexity
// highest than 30, out of CRAP territory.
//
// Notice that CRAP score is not computed for too short methods
// with less than 10 lines of code.
//
// To list methods with highest C.R.A.P scores, please refer to the default rule:
// *Test and Code Coverage* > *C.R.A.P method code metric*
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Average C.R.A.P Score" />
(from m in JustMyCode.Methods
// Don't match too short methods
where m.NbLinesOfCode > 10
let CC = m.CyclomaticComplexity
let uncov = (100 - m.PercentageCoverage) / 100f
let CRAP = (CC * CC * uncov * uncov * uncov) + CC
where CRAP != null && CRAP > 30 select CRAP)
.Average(CRAP => CRAP)
//<Description>
// **Change Risk Analyzer and Predictor** (i.e. CRAP) is a code metric
// that helps in pinpointing overly complex and untested code.
// Is has been first defined here:
// http://www.artima.com/weblogs/viewpost.jsp?thread=215899
//
// The Formula is: **CRAP(m) = CC(m)^2 * (1 cov(m)/100)^3 + CC(m)**
//
// • where *CC(m)* is the *cyclomatic complexity* of the method *m*
//
// • and *cov(m)* is the *percentage coverage* by tests of the method *m*
//
// Matched methods cumulates two highly *error prone* code smells:
//
// • A complex method, difficult to develop and maintain.
//
// • Non 100% covered code, difficult to refactor without any regression bug.
//
// The highest the CRAP score, the more painful to maintain and error prone is the method.
//
// An arbitrary threshold of 30 is fixed for this code rule as suggested by inventors.
//
// Notice that no amount of testing will keep methods with a Cyclomatic Complexity
// highest than 30, out of CRAP territory.
//
// Notice that CRAP score is not computed for too short methods
// with less than 10 lines of code.
//
// To list methods with highest C.R.A.P scores, please refer to the default rule:
// *Test and Code Coverage* > *C.R.A.P method code metric*
//</Description>]]></Query>
</Group>
<Group Name="Third-Party Usage" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Third-Party Assemblies Used" Unit="Assemblies" />
ThirdParty.Assemblies.Count()]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Third-Party Namespaces Used" Unit="Namespaces" />
ThirdParty.Namespaces.Count()]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Third-Party Types Used" Unit="Types" />
ThirdParty.Types.Count()]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Third-Party Methods Used" Unit="Methods" />
ThirdParty.Methods.Count()]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Third-Party Fields Used" Unit="Fields" />
ThirdParty.Fields.Count()]]></Query>
</Group>
</Group>
<Group Name="Defining JustMyCode" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Discard generated Assemblies from JustMyCode</Name>
notmycode
from a in Application.Assemblies where
// Assemblies generated for Xsl IL compilation for example are tagged with this attribute
a.HasAttribute ("System.CodeDom.Compiler.GeneratedCodeAttribute".AllowNoMatch())
select a
//<Description>
// This code query is prefixed with **notmycode**.
// This means that all application assemblies matched by this
// code query are removed from the *code base view* **JustMyCode.Assemblies**.
// It also means that all *namespaces*, *types*, *methods* and
// *fields* contained in a matched assembly are removed from
// the code base view *JustMyCode*.
// The code base view *JustMyCode* is used by most default code queries
// and rules.
//
// So far this query only matches application assemblies tagged
// with *System.CodeDom.Compiler.GeneratedCodeAttribute*.
// Make sure to make this query richer to discard your generated
// assemblies from the NDepend rules results.
//
// *notmycode* queries are executed before running others
// queries and rules. Also modifying a *notmycode* query
// provokes re-run of queries and rules that rely
// on the *JustMyCode* code base view.
//
// Several *notmycode* queries can be written to match *assemblies*,
// in which case this results in cumulative effect.
//
// Online documentation:
// http://www.ndepend.com/docs/cqlinq-syntax#NotMyCode
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Discard generated Types from JustMyCode</Name>
notmycode
from t in Application.Types where
// Resources, Settings, or typed DataSet generated types for example, are tagged with this attribute
t.HasAttribute ("System.CodeDom.Compiler.GeneratedCodeAttribute".AllowNoMatch()) ||
// This attributes identifies a type or member that is not part of the user code for an application.
t.HasAttribute ("System.Diagnostics.DebuggerNonUserCodeAttribute".AllowNoMatch()) ||
// Delegate types are always generated
t.IsDelegate ||
// Discard ASP.NET page types generated by aspnet_compiler.exe
// See: http://www.ndepend.com/FAQ.aspx#ASPNET
t.ParentNamespace.Name.EqualsAny("ASP", "__ASP") ||
// Discard types generated for code contract
t.FullName.StartsWith("System.Diagnostics.Contracts.__ContractsRuntime") ||
t.FullName == "System.Diagnostics.Contracts.RuntimeContractsAttribute" ||
// Discard all types declared in a folder path containing the word "generated"
(t.SourceFileDeclAvailable &&
t.SourceDecls.All(s => s.SourceFile.FilePath.ParentDirectoryPath.ToString().ToLower().Contains("generated")))
select t
//<Description>
// This code query is prefixed with **notmycode**.
// This means that all application types matched by this
// code query are removed from the *code base view* **JustMyCode.Types**.
// It also means that all *methods* and *fields* contained in a
// matched type are removed from the code base view *JustMyCode*.
// The code base view *JustMyCode* is used by most default code queries
// and rules.
//
// So far this query matches several well-identified generated
// types, like the ones tagged with *System.CodeDom.Compiler.GeneratedCodeAttribute*.
// Make sure to make this query richer to discard your generated
// types from the NDepend rules results.
//
// *notmycode* queries are executed before running others
// queries and rules. Also modifying a *notmycode* query
// provokes re-run of queries and rules that rely
// on the *JustMyCode* code base view.
//
// Several *notmycode* queries can be written to match *types*,
// in which case this results in cumulative effect.
//
// Online documentation:
// http://www.ndepend.com/docs/cqlinq-syntax#NotMyCode
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Discard generated and designer Methods from JustMyCode</Name>
notmycode
//
// First define source files paths to discard
//
from a in Application.Assemblies
where a.SourceFileDeclAvailable
let asmSourceFilesPaths = a.SourceDecls.Select(s => s.SourceFile.FilePath)
let sourceFilesPathsToDiscard = (
from filePath in asmSourceFilesPaths
let filePathLower= filePath.ToString().ToLower()
where
filePathLower.EndsWithAny(
".g.cs", // Popular pattern to name generated files.
".g.vb",
".xaml", // notmycode WPF xaml code
".designer.cs", // notmycode C# Windows Forms designer code
".designer.vb") // notmycode VB.NET Windows Forms designer code
||
// notmycode methods in source files in a directory containing generated
filePathLower.Contains("generated")
select filePath
).ToHashSet()
//
// Second: discard methods in sourceFilesPathsToDiscard
//
from m in a.ChildMethods
where (m.SourceFileDeclAvailable &&
sourceFilesPathsToDiscard.Contains(m.SourceDecls.First().SourceFile.FilePath)) ||
// Generated methods might be tagged with this attribute
m.HasAttribute ("System.CodeDom.Compiler.GeneratedCodeAttribute".AllowNoMatch()) ||
// This attributes identifies a type or member that is not part of the user code for an application.
m.HasAttribute ("System.Diagnostics.DebuggerNonUserCodeAttribute".AllowNoMatch())
select new { m, m.NbLinesOfCode }
//<Description>
// This code query is prefixed with **notmycode**.
// This means that all application methods matched by this
// code query are removed from the *code base view* **JustMyCode.Methods**.
// The code base view *JustMyCode* is used by most default code queries
// and rules.
//
// So far this query matches several well-identified generated
// methods, like the ones tagged with *System.CodeDom.Compiler.GeneratedCodeAttribute*,
// or the ones declared in a source file suffixed with *.designer.cs*.
// Make sure to make this query richer to discard your generated
// methods from the NDepend rules results.
//
// *notmycode* queries are executed before running others
// queries and rules. Also modifying a *notmycode* query
// provokes re-run of queries and rules that rely
// on the *JustMyCode* code base view.
//
// Several *notmycode* queries can be written to match *methods*,
// in which case this results in cumulative effect.
//
// Online documentation:
// http://www.ndepend.com/docs/cqlinq-syntax#NotMyCode
//</Description>]]></Query>
<Query Active="True" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Discard generated Fields from JustMyCode</Name>
notmycode
from f in Application.Fields where
f.HasAttribute ("System.CodeDom.Compiler.GeneratedCodeAttribute".AllowNoMatch()) ||
// Eliminate "components" generated in Windows Form Control context
f.Name == "components" && f.ParentType.DeriveFrom("System.Windows.Forms.Control".AllowNoMatch())
select f
//<Description>
// This code query is prefixed with **notmycode**.
// This means that all application fields matched by this
// code query are removed from the *code base view* **JustMyCode.Fields**.
// The code base view *JustMyCode* is used by most default code queries
// and rules.
//
// So far this query only matches application fields tagged
// with *System.CodeDom.Compiler.GeneratedCodeAttribute*, and
// *Windows Form* generated fields named *components*.
// Make sure to make this query richer to discard your generated
// fields from the NDepend rules results.
//
// *notmycode* queries are executed before running others
// queries and rules. Also modifying a *notmycode* query
// provokes re-run of queries and rules that rely
// on the *JustMyCode* code base view.
//
// Several *notmycode* queries can be written to match *fields*,
// in which case this results in cumulative effect.
//
// Online documentation:
// http://www.ndepend.com/docs/cqlinq-syntax#NotMyCode
//</Description>]]></Query>
</Group>
<Group Name="Statistics" Active="True" ShownInReport="False">
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Most used types (Rank)</Name>
(from t in Application.Types
where !t.IsGeneratedByCompiler
orderby t.Rank descending
select new { t, t.Rank, t.TypesUsingMe }).Take(100)
//<Description>
// **TypeRank** values are computed by applying
// the **Google PageRank** algorithm on the
// graph of types' dependencies. Types with
// high *Rank* are the most used ones. Not necessarily
// the ones with the most users types, but the ones
// used by many types, themselves having a lot of
// types users.
//
// See the definition of the TypeRank metric here:
// http://www.ndepend.com/docs/code-metrics#TypeRank
//
// This code query lists the 100 application types
// with the highest rank.
//
// The main consequence of being used a lot for a
// type is that each change (both *syntax change*
// and *behavior change*) will result in potentially
// a lot of **pain** since most types clients will be
// **impacted**.
//
// Hence it is preferable that types with highest
// *TypeRank*, are **interfaces**, that are typically
// less subject changes.
//
// Also interfaces avoid clients relying on
// implementations details. Hence, when the behavior of
// classes implementing an interface changes, this
// shouldn't impact clients of the interface.
// This is *in essence* the
// **Liskov Substitution Principle**.
// http://en.wikipedia.org/wiki/Liskov_substitution_principle
//</Description>]]></Query>
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Most used methods (Rank)</Name>
(from m in Application.Methods
where !m.IsGeneratedByCompiler
orderby m.Rank descending
select new { m, m.Rank, m.MethodsCallingMe }).Take(100)
//<Description>
// **MethodRank** values are computed by applying
// the **Google PageRank** algorithm on the
// graph of methods' dependencies. Methods with
// high *Rank* are the most used ones. Not necessarily
// the ones with the most callers methods, but the ones
// called by many methods, themselves having a lot
// of callers.
//
// See the definition of the MethodRank metric here:
// http://www.ndepend.com/docs/code-metrics#MethodRank
//
// This code query lists the 100 application methods
// with the highest rank.
//
// The main consequence of being used a lot for a
// method is that each change (both *signature change*
// and *behavior change*) will result in potentially
// a lot of **pain** since most methods callers will be
// **impacted**.
//
// Hence it is preferable that methods with highest
// *MethodRank*, are **abstract methods**, that are
// typically less subject to signature changes.
//
// Also abstract methods avoid callers relying on
// implementations details. Hence, when the code
// of a method implementing an abstract method changes,
// this shouldn't impact callers of the abstract method.
// This is *in essence* the
// **Liskov Substitution Principle**.
// http://en.wikipedia.org/wiki/Liskov_substitution_principle
//</Description>]]></Query>
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Most used assemblies (#AssembliesUsingMe)</Name>
(from a in Assemblies orderby a.AssembliesUsingMe.Count() descending
select new { a, a.AssembliesUsingMe }).Take(100)
//<Description>
// This code query lists the 100 *application* and *third-party*
// assemblies, with the highest number of assemblies users.
//</Description>]]></Query>
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Most used namespaces (#NamespacesUsingMe )</Name>
(from n in Namespaces orderby n.NbNamespacesUsingMe descending
select new { n, n.NamespacesUsingMe }).Take(100)
//<Description>
// This code query lists the 100 *application* and *third-party*
// namespaces, with the highest number of namespaces users.
//</Description>]]></Query>
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Most used types (#TypesUsingMe )</Name>
(from t in Types orderby t.NbTypesUsingMe descending
where !t.IsGeneratedByCompiler
select new { t, t.TypesUsingMe }).Take(100)
//<Description>
// This code query lists the 100 *application* and *third-party*
// types, with the highest number of types users.
//</Description>]]></Query>
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Most used methods (#MethodsCallingMe )</Name>
(from m in Methods orderby m.NbMethodsCallingMe
where !m.IsGeneratedByCompiler
select new { m, m.MethodsCallingMe }).Take(100)
//<Description>
// This code query lists the 100 *application* and *third-party*
// methods, with the highest number of methods callers.
//</Description>]]></Query>
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Namespaces that use many other namespaces (#NamespacesUsed )</Name>
(from n in Application.Namespaces orderby n.NbNamespacesUsed descending
select new { n, n.NamespacesUsed }).Take(100)
//<Description>
// This code query lists the 100 *application* namespaces
// with the highest number of namespaces used.
//</Description>]]></Query>
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types that use many other types (#TypesUsed )</Name>
(from t in Application.Types orderby t.NbTypesUsed descending
select new { t, t.TypesUsed, isMyCode = JustMyCode.Contains(t) }).Take(100)
//<Description>
// This code query lists the 100 *application* types
// with the highest number of types used.
//</Description>]]></Query>
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods that use many other methods (#MethodsCalled )</Name>
(from m in Application.Methods orderby m.NbMethodsCalled descending
select new { m, m.MethodsCalled, isMyCode = JustMyCode.Contains(m) }).Take(100)
//<Description>
// This code query lists the 100 *application* methods
// with the highest number of methods called.
//</Description>]]></Query>
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>High-level to low-level assemblies (Level)</Name>
from a in Application.Assemblies orderby a.Level descending
select new { a, a.Level }
//<Description>
// This code query lists assemblies ordered by **Level** values.
// See the definition of the *AssemblyLevel* metric here:
// http://www.ndepend.com/docs/code-metrics#Level
//</Description>]]></Query>
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>High-level to low-level namespaces (Level)</Name>
from n in Application.Namespaces orderby n.Level descending
select new { n, n.Level }
//<Description>
// This code query lists namespaces ordered by **Level** values.
// See the definition of the *NamespaceLevel* metric here:
// http://www.ndepend.com/docs/code-metrics#Level
//</Description>]]></Query>
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>High-level to low-level types (Level)</Name>
from t in Application.Types orderby t.Level descending
select new { t, t.Level }
//<Description>
// This code query lists types ordered by **Level** values.
// See the definition of the *TypeLevel* metric here:
// http://www.ndepend.com/docs/code-metrics#Level
//</Description>]]></Query>
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>High-level to low-level methods (Level)</Name>
from m in Application.Methods orderby m.Level descending
select new { m, m.Level }
//<Description>
// This code query lists methods ordered by **Level** values.
// See the definition of the *MethodLevel* metric here:
// http://www.ndepend.com/docs/code-metrics#Level
//</Description>]]></Query>
</Group>
<Group Name="Samples of Custom rules" Active="False" ShownInReport="False">
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Check that the assembly Asm1 is not using the assembly Asm2</Name>
warnif count > 0 from a in Application.Assemblies where
a.IsUsing ("Asm2".AllowNoMatch().MatchAssembly()) &&
(a.Name == @"Asm1")
select a
//<Description>
// This rule is a *sample rule that can be adapted to your need*.
//
// It shows how to be warned if a particular assembly is using
// another particular assembly.
//
// Such rule can be generated for assemblies **A** and **B**:
//
// • by right clicking the cell in the *Dependency Matrix*
// with **B** in row and **A** in column,
//
// • or by right-clicking the concerned arrow in the *Dependency
// Graph* from **A** to **B**,
//
// and in both cases, click the menu
// **Generate a code rule that warns if this dependency exists**
//
// The generated rule will look like this one.
// It is now up to you to adapt this rule to check exactly
// your needs.
//</Description>
//<HowToFix>
// This is a *sample rule* there is nothing to fix *as is*.
//</HowToFix>]]></Query>
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Check that the namespace N1.N2 is not using the namespace N3.N4.N5</Name>
warnif count > 0 from n in Application.Namespaces where
n.IsUsing ("N3.N4.N5".AllowNoMatch().MatchNamespace()) &&
(n.Name == @"N1.N2")
select n
//<Description>
// This rule is a *sample rule that can be adapted to your need*.
//
// It shows how to be warned if a particular namespace is using
// another particular namespace.
//
// Such rule can be generated for namespaces **A** and **B**:
//
// • by right clicking the cell in the *Dependency Matrix*
// with **B** in row and **A** in column,
//
// • or by right-clicking the concerned arrow in the *Dependency
// Graph* from **A** to **B**,
//
// and in both cases, click the menu
// **Generate a code rule that warns if this dependency exists**
//
// The generated rule will look like this one.
// It is now up to you to adapt this rule to check exactly
// your needs.
//</Description>
//<HowToFix>
// This is a *sample rule* there is nothing to fix *as is*.
//</HowToFix>]]></Query>
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Check that the assembly Asm1 is only using the assemblies Asm2, Asm3 or mscorlib</Name>
warnif count > 0 from a in Application.Assemblies where
( !a.IsUsing ("Asm2".AllowNoMatch().MatchAssembly()) ||
!a.IsUsing ("Asm3".AllowNoMatch().MatchAssembly()) ||
!a.IsUsing ("mscorlib".MatchAssembly()) ||
a.AssembliesUsed.Count() != 3) // Must not be used more than 3 assemblies
&&
(a.Name == @"Asm1")
select new { a, a.AssembliesUsed }
//<Description>
// This rule is a *sample rule that can be adapted to your need*.
//
// It shows how to enforce that a particular assembly
// is only using a particular set of assemblies.
//</Description>
//<HowToFix>
// This is a *sample rule* there is nothing to fix *as is*.
//</HowToFix>]]></Query>
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Check that the namespace N1.N2 is only using the namespaces N3.N4, N5 or System</Name>
warnif count > 0 from n in Application.Namespaces where
( !n.IsUsing("N3.N4".AllowNoMatch().MatchNamespace()) ||
!n.IsUsing("N5".AllowNoMatch().MatchNamespace()) ||
!n.IsUsing("System".MatchNamespace()) ||
n.NamespacesUsed.Count() != 3) // Must not be used more than 3 assemblies
// AsmCe = Efferent Coupling for assembly
&&
(n.Name == @"N1.N2")
select new { n, n.NamespacesUsed }
//<Description>
// This rule is a *sample rule that can be adapted to your need*.
//
// It shows how to enforce that a particular namespace
// **is only using** a particular set of namespaces.
//</Description>
//<HowToFix>
// This is a *sample rule* there is nothing to fix *as is*.
//</HowToFix>]]></Query>
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Check that AsmDrawing is the only assembly that is using System.Drawing</Name>
warnif count> 0 from a in Application.Assemblies where
a.IsUsing ("System.Drawing".AllowNoMatch().MatchAssembly()) &&
!(a.Name == @"AsmDrawing")
select a
//<Description>
// This rule is a *sample rule that can be adapted to your need*.
//
// It shows how to enforce that a particular assembly
// is **only used by** another particular assembly.
//</Description>
//<HowToFix>
// This is a *sample rule* there is nothing to fix *as is*.
//</HowToFix>]]></Query>
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Check that only 3 assemblies are using System.Drawing</Name>
warnif count != 3 from a in Application.Assemblies where
a.IsUsing ("System.Drawing".AllowNoMatch().MatchAssembly())
select a
//<Description>
// This rule is a *sample rule that can be adapted to your need*.
//
// It shows how to enforce that a particular assembly
// is **only used by** 3 any others assemblies.
//</Description>
//<HowToFix>
// This is a *sample rule* there is nothing to fix *as is*.
//</HowToFix>]]></Query>
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Check that all methods that call Foo.Fct1() also call Foo.Fct2(Int32)</Name>
warnif count > 0 from m in Application.Methods where
m.IsUsing ("Foo.Fct1()".AllowNoMatch()) &&
!m.IsUsing ("Foo.Fct2(Int32)".AllowNoMatch())
select m
//<Description>
// This rule is a *sample rule that can be adapted to your need*.
//
// It shows how to enforce that if a method calls a particular method,
// it must call another particular method.
//</Description>
//<HowToFix>
// This is a *sample rule* there is nothing to fix *as is*.
//</HowToFix>]]></Query>
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Check that all types that derive from Foo, also implement IFoo</Name>
warnif count > 0 from t in Application.Types where
t.DeriveFrom ("Foo".AllowNoMatch().MatchType()) &&
!t.Implement ("IFoo".AllowNoMatch().MatchType())
select t
//<Description>
// This rule is a *sample rule that can be adapted to your need*.
//
// It shows how to enforce that all classes that derive from a particular base class,
// also implement a particular interface.
//</Description>
//<HowToFix>
// This is a *sample rule* there is nothing to fix *as is*.
//</HowToFix>]]></Query>
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Check that all types that has the attribute FooAttribute are declared in the namespace N1.N2*</Name>
warnif count > 0 from t in
Application.Namespaces.WithNameWildcardMatchNotIn("N1.N2*").ChildTypes()
where
t.HasAttribute ("FooAttribute".AllowNoMatch())
select t
//<Description>
// This rule is a *sample rule that can be adapted to your need*.
//
// It shows how to enforce that all types that are tagged
// with a particular attribute, are declared in a
// particular namespace.
//</Description>
//<HowToFix>
// This is a *sample rule* there is nothing to fix *as is*.
//</HowToFix>]]></Query>
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Check that all synchronization objects are only used from the namespaces under MyNamespace.Sync</Name>
warnif count > 0 from n in Application.Namespaces
where
(n.IsUsing ("System.Threading.Monitor".AllowNoMatch()) ||
n.IsUsing ("System.Threading.ReaderWriterLock".AllowNoMatch()) ||
n.IsUsing ("System.Threading.Mutex".AllowNoMatch()) ||
n.IsUsing ("System.Threading.EventWaitHandle".AllowNoMatch()) ||
n.IsUsing ("System.Threading.Semaphore".AllowNoMatch()) ||
n.IsUsing ("System.Threading.Interlocked".AllowNoMatch()))
&& !n.NameLike (@"^MyNamespace.Sync")
select n
//<Description>
// This rule is a *sample rule that can be adapted to your need*.
//
// It shows how to enforce that all synchronization objects
// are used from a particular namespace.
//</Description>
//<HowToFix>
// This is a *sample rule* there is nothing to fix *as is*.
//</HowToFix>]]></Query>
<Group Name="Check Coverage on particular Code Elements" Active="False" ShownInReport="False">
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Check that the assembly Asm is 100% covered by tests</Name>
warnif count > 0 from a in Application.Assemblies where
(a.Name == @"Asm") &&
a.PercentageCoverage < 100
select new { a, a.PercentageCoverage }
//<Description>
// This is a sample rule that shows how to check
// if a particular assembly is 100% covered by tests.
// Both the string **@"Asm"** and the threshold **100** can be adapted to your own needs.
//
// To execute this sample rule, coverage data must be imported.
// More info here: http://www.ndepend.com/docs/code-coverage
//</Description>
//<HowToFix>
// This is a *sample rule* there is nothing to fix *as is*.
//</HowToFix>]]></Query>
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Check that the namespace N1.N2 is 100% covered by tests</Name>
warnif count > 0 from n in Application.Namespaces where
(n.Name == @"N1.N2") &&
n.PercentageCoverage < 100
select new { n, n.PercentageCoverage }
//<Description>
// This is a sample rule that shows how to check
// if a particular namespace is 100% covered by tests.
// Both the string **@"N1.N2"** and the threshold **100** can be adapted to your own needs.
//
// To execute this sample rule, coverage data must be imported.
// More info here: http://www.ndepend.com/docs/code-coverage
//</Description>
//<HowToFix>
// This is a *sample rule* there is nothing to fix *as is*.
//</HowToFix>]]></Query>
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Check that the class Namespace.Foo is 100% covered by tests</Name>
warnif count > 0 from t in Application.Types where
(t.FullName == @"Namespace.Foo") &&
t.PercentageCoverage < 100
select new { t, t.PercentageCoverage }
//<Description>
// This is a sample rule that shows how to check
// if a particular class is 100% covered by tests.
// Both the string **@"Namespace.Foo"** and the threshold **100**
// can be adapted to your own needs.
//
// To execute this sample rule, coverage data must be imported.
// More info here: http://www.ndepend.com/docs/code-coverage
//</Description>
//<HowToFix>
// This is a *sample rule* there is nothing to fix *as is*.
//</HowToFix>]]></Query>
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Check that the class Namespace.Foo.Method(Int32) is 100% covered by tests</Name>
warnif count > 0 from t in Application.Types where
(t.FullName == @"Namespace.Foo.Method(Int32)") &&
t.PercentageCoverage < 100
select new { t, t.PercentageCoverage }
//<Description>
// This is a sample rule that shows how to check
// if a particular method is 100% covered by tests.
// Both the string **@"Namespace.Foo.Method(Int32)"** and the threshold **100**
// can be adapted to your own needs.
//
// To execute this sample rule, coverage data must be imported.
// More info here: http://www.ndepend.com/docs/code-coverage
//</Description>
//<HowToFix>
// This is a *sample rule* there is nothing to fix *as is*.
//</HowToFix>]]></Query>
</Group>
<Group Name="Custom Naming Conventions" Active="False" ShownInReport="False">
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Check that all types that derive from Foo, has a name that ends up with Foo</Name>
warnif count > 0 from t in Application.Types where
t.DeriveFrom ("Foo".AllowNoMatch().MatchType()) &&
!t.NameLike (@"Foo$")
select new { t, t.NbLinesOfCode }
//<Description>
// This rule is a *sample rule that can be adapted to your need*.
//
// It shows how to enforce that all classes that derive from
// a particular class, are named with a particular suffix.
//</Description>
//<HowToFix>
// This is a *sample rule* there is nothing to fix *as is*.
//</HowToFix>]]></Query>
<Query Active="False" DisplayList="True" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Check that all namespaces begins with CompanyName.ProductName</Name>
warnif count > 0 from n in Application.Namespaces where
!n.NameLike (@"^CompanyName.ProductName")
select new { n, n.NbLinesOfCode }
//<Description>
// A practice widely adopted is that, in a product source code,
// all namespaces start with "CompanyName.ProductName".
//
// This rule must be adapted with your own **"CompanyName.ProductName"**.
//</Description>
//<HowToFix>
// Update all namespaces definitions in source code to satisfy this rule.
//</HowToFix>]]></Query>
</Group>
</Group>
</Queries>
</NDepend>