4069 lines
214 KiB
XML
4069 lines
214 KiB
XML
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
|
||
<NDepend AppName="NOCQ" Platform="DotNet">
|
||
<OutputDir KeepXmlFiles="False">D:\varanas\NDependOut</OutputDir>
|
||
<Assemblies>
|
||
<Name>NOCQ</Name>
|
||
<Name>NOCQ.Application</Name>
|
||
</Assemblies>
|
||
<FrameworkAssemblies>
|
||
<Name>mscorlib</Name>
|
||
<Name>System.ComponentModel.Composition</Name>
|
||
<Name>System</Name>
|
||
<Name>System.Core</Name>
|
||
<Name>Newtonsoft.Json</Name>
|
||
<Name>Microsoft.CSharp</Name>
|
||
<Name>AE.Net.Mail</Name>
|
||
<Name>csredis</Name>
|
||
</FrameworkAssemblies>
|
||
<Dirs>
|
||
<Dir>D:\varanas\src\NOCQ\bin\Debug</Dir>
|
||
<Dir>D:\varanas\src\NOCQ.Application\bin\Debug</Dir>
|
||
<Dir>C:\Windows\Microsoft.NET\Framework\v4.0.30319</Dir>
|
||
<Dir>C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF</Dir>
|
||
</Dirs>
|
||
<Report Kind="0" SectionsEnabled="45055" XslPath="" Flags="64512" />
|
||
<BuildComparisonSetting ProjectMode="DontCompare" BuildMode="MostRecentAnalysisResultAvailable" ProjectFileToCompareWith="" BuildFileToCompareWith="" NDaysAgo="1" FocusOnRecentRulesViolations="False" />
|
||
<BaselineInUISetting ProjectMode="DontCompare" BuildMode="MostRecentAnalysisResultAvailable" ProjectFileToCompareWith="" BuildFileToCompareWith="" NDaysAgo="1" FocusOnRecentRulesViolations="False" />
|
||
<CoverageFiles 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 />
|
||
<Queries>
|
||
<Group Name="Code Quality" Active="True" ShownInReport="False">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// Types where NbLinesOfCode > 500 are extremely complex
|
||
// and should be split in a smaller group of types.
|
||
// See the definition of the NbLinesOfCode metric here
|
||
// http://www.ndepend.com/Metrics.aspx#NbLinesOfCode
|
||
|
||
// 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/Metrics.aspx#NbILInstructions
|
||
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Methods too complex - critical</Name>
|
||
warnif count > 0 from m in JustMyCode.Methods where
|
||
m.ILCyclomaticComplexity > 40 &&
|
||
m.ILNestingDepth > 5
|
||
orderby m.ILCyclomaticComplexity descending,
|
||
m.ILNestingDepth descending
|
||
select new { m, m.ILCyclomaticComplexity, m.ILNestingDepth }
|
||
|
||
// Methods with ILCyclomaticComplexity > 40 and ILNestingDepth > 4
|
||
// are really too complex and should be split
|
||
// in smaller methods, or even types.
|
||
// See the definition of the ILCyclomaticComplexity metric here
|
||
// http://www.ndepend.com/Metrics.aspx#ILCC
|
||
// See the definition of the ILNestingDepth metric here
|
||
// http://www.NDepend.com/Metrics.aspx#ILNestingDepth]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// Methods with more than 8 parameters might be painful to call
|
||
// and might degrade performance. You should prefer using
|
||
// additional properties/fields to the declaring type to
|
||
// handle numerous states. Another alternative is to provide
|
||
// a class or structure dedicated to handle arguments passing
|
||
// (for example see the class System.Diagnostics.ProcessStartInfo
|
||
// and the method System.Diagnostics.Process.Start(ProcessStartInfo))
|
||
// See the definition of the NbParameters metric here
|
||
// http://www.ndepend.com/Metrics.aspx#NbParameters
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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/Metrics.aspx#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/Metrics.aspx#NbILInstructions
|
||
m.CyclomaticComplexity > 20 || // http://www.ndepend.com/Metrics.aspx#CC
|
||
m.ILCyclomaticComplexity > 50 || // http://www.ndepend.com/Metrics.aspx#ILCC
|
||
m.ILNestingDepth > 5 || // http://www.ndepend.com/Metrics.aspx#ILNestingDepth
|
||
m.NbParameters > 5 || // http://www.ndepend.com/Metrics.aspx#NbParameters
|
||
m.NbVariables > 8 || // http://www.ndepend.com/Metrics.aspx#NbVariables
|
||
m.NbOverloads > 6 // http://www.ndepend.com/Metrics.aspx#NbOverloads
|
||
|
||
select new { m, m.NbLinesOfCode, m.NbILInstructions, m.CyclomaticComplexity,
|
||
m.ILCyclomaticComplexity, m.ILNestingDepth,
|
||
m.NbParameters, m.NbVariables, m.NbOverloads } ]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// Methods where NbLinesOfCode > 30 or NbILInstructions > 200
|
||
// are extremely complex and should be split in smaller methods.
|
||
// See the definition of the NbLinesOfCode metric here
|
||
// http://www.ndepend.com/Metrics.aspx#NbLinesOfCode]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 > 5
|
||
orderby m.CyclomaticComplexity descending,
|
||
m.ILCyclomaticComplexity descending,
|
||
m.ILNestingDepth descending
|
||
select new { m, m.CyclomaticComplexity,
|
||
m.ILCyclomaticComplexity,
|
||
m.ILNestingDepth }
|
||
|
||
// Methods where CyclomaticComplexity > 20
|
||
// or ILCyclomaticComplexity > 40
|
||
// or ILNestingDepth > 4
|
||
// are hard to understand and maintain
|
||
// and should be split in smaller methods.
|
||
// See the definition of the complexity metrics here:
|
||
// http://www.ndepend.com/Metrics.aspx#CC
|
||
// http://www.ndepend.com/Metrics.aspx#ILCC
|
||
// http://www.NDepend.com/Metrics.aspx#ILNestingDepth]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// Methods where %Comment < 20 and that have
|
||
// at least 20 lines of code might need to be more commented.
|
||
// See the definition of the Comments metric here
|
||
// http://www.ndepend.com/Metrics.aspx#PercentageComment
|
||
// http://www.ndepend.com/Metrics.aspx#NbLinesOfComment ]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// Methods where NbParameters > 5 might be painful to call
|
||
// and might degrade performance. You should prefer using
|
||
// additional properties/fields to the declaring type to
|
||
// handle numerous states. Another alternative is to provide
|
||
// a class or structure dedicated to handle arguments passing
|
||
// (for example see the class System.Diagnostics.ProcessStartInfo
|
||
// and the method System.Diagnostics.Process.Start(ProcessStartInfo))
|
||
// See the definition of the NbParameters metric here
|
||
// http://www.ndepend.com/Metrics.aspx#NbParameters]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// Methods where NbVariables > 8 are hard to understand and maintain.
|
||
// Methods where NbVariables > 15 are extremely complex
|
||
// and should be split in smaller methods.
|
||
// See the definition of the Nbvariables metric here
|
||
// http://www.ndepend.com/Metrics.aspx#Nbvariables]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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
|
||
select new { m, m.NbOverloads }
|
||
|
||
// Methods where NbOverloads > 6 might
|
||
// be a problem to maintain and provoke higher coupling
|
||
// than necessary.
|
||
// This might also reveal a potential misused of the
|
||
// C# and VB.NET language that since C#3 and VB9 support
|
||
// object initialization. This feature helps reducing the number
|
||
// of constructors of a class.
|
||
// See the definition of the NbOverloads metric here
|
||
// http://www.ndepend.com/Metrics.aspx#NbOverloads]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types with too many methods</Name>
|
||
warnif count > 0 from t in JustMyCode.Types where
|
||
t.Methods.Count() > 20
|
||
orderby t.Methods.Count() descending
|
||
select new { t, t.InstanceMethods, t.StaticMethods }
|
||
|
||
// Types where Methods.Count() > 20 might be hard to
|
||
// understand and maintain
|
||
// but there might be cases where it is relevant
|
||
// to have a high number of methods.
|
||
// For example, the System.Windows.Forms.DataGridView
|
||
// standard class has more than 1000 methods.
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types with too many fields</Name>
|
||
warnif count > 0 from t in JustMyCode.Types where
|
||
t.Fields.Count() > 20 &&
|
||
!t.IsEnumeration
|
||
orderby t.Fields.Count() descending
|
||
select new { t, t.InstanceFields, t.StaticFields, t.SizeOfInst }
|
||
|
||
// Types where Fields.Count() > 20 and not IsEnumeration
|
||
// might be hard to understand and maintain
|
||
// but there might be cases where it is relevant
|
||
// to have a high number of fields.
|
||
// For example, the System.Windows.Forms.Control
|
||
// standard class has more than 200 fields.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// Types where LCOM > 0.8 and NbFields > 10
|
||
// and NbMethods >10 might be problematic.
|
||
// However, it is very hard to avoid such
|
||
// non-cohesive types. The LCOMHS metric
|
||
// is often considered as more efficient to
|
||
// detect non-cohesive types.
|
||
// See the definition of the LCOM metric here
|
||
// http://www.ndepend.com/Metrics.aspx#LCOM]]></Query>
|
||
</Group>
|
||
<Group Name="Code Quality Regression" Active="True" ShownInReport="False">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 new or modified methods since Baseline for Comparison ***
|
||
(m.WasAdded() || m.CodeWasChanged()) &&
|
||
|
||
// Low Quality methods// Metrics' definitions
|
||
( m.NbLinesOfCode > 30 || // http://www.ndepend.com/Metrics.aspx#NbLinesOfCode
|
||
m.NbILInstructions > 200 || // http://www.ndepend.com/Metrics.aspx#NbILInstructions
|
||
m.CyclomaticComplexity > 20 || // http://www.ndepend.com/Metrics.aspx#CC
|
||
m.ILCyclomaticComplexity > 50 || // http://www.ndepend.com/Metrics.aspx#ILCC
|
||
m.ILNestingDepth > 4 || // http://www.ndepend.com/Metrics.aspx#ILNestingDepth
|
||
m.NbParameters > 5 || // http://www.ndepend.com/Metrics.aspx#NbParameters
|
||
m.NbVariables > 8 || // http://www.ndepend.com/Metrics.aspx#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/Metrics.aspx#NbOverloads
|
||
|
||
|
||
// This rule warns if a method with
|
||
// low-quality has been added or refactored.
|
||
// With NDepend and such rule, you can
|
||
// Ensure Quality From Now! as explained here:
|
||
// http://codebetter.com/blogs/patricksmacchia/archive/2008/01/01/ensure-the-quality-of-the-code-that-will-be-developed-this-year.aspx
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 modified 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 &&
|
||
|
||
// Low Quality types Metrics' definitions are available here:
|
||
// http://www.ndepend.com/Metrics.aspx#MetricsOnTypes
|
||
( // Types with too many methods
|
||
t.NbMethods > 20 ||
|
||
|
||
// Types with too many fields
|
||
t.NbFields > 20 ||
|
||
|
||
// Complex Types that use more than 50 other types
|
||
t.NbTypesUsed > 50
|
||
)
|
||
select new { t, t.Methods, t.Fields, t.TypesUsed }
|
||
|
||
|
||
// This rule warns if a type with
|
||
// low-quality has been added or refactored.
|
||
// With NDepend and such rule, you can
|
||
// Ensure Quality From Now! as explained here:
|
||
// http://codebetter.com/blogs/patricksmacchia/archive/2008/01/01/ensure-the-quality-of-the-code-that-will-be-developed-this-year.aspx
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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
|
||
|
||
// Match methods new or modified 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 }
|
||
|
||
// Having types 100% covered by tests is a good idea because
|
||
// the small portion of code hard to cover, is also the
|
||
// portion of code that is the most likely to contain bugs.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid decreasing code coverage by tests of types</Name>
|
||
// 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
|
||
|
||
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,
|
||
}
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid making complex methods even more complex (Source CC)</Name>
|
||
// 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
|
||
|
||
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,
|
||
}
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid making complex methods even more complex (IL CC)</Name>
|
||
// 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
|
||
|
||
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,
|
||
}
|
||
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid making large methods even larger</Name>
|
||
// 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
|
||
|
||
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,
|
||
}
|
||
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid adding methods to a type that already had many methods</Name>
|
||
// 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
|
||
|
||
warnif count > 0
|
||
from t in JustMyCode.Types where
|
||
t.IsPresentInBothBuilds() &&
|
||
t.Methods.Count() > t.OlderVersion().Methods.Count() &&
|
||
t.OlderVersion().Methods.Count() > 10
|
||
|
||
let newMethods = t.Methods.Where(m => m.WasAdded())
|
||
let removedMethods = t.OlderVersion().Methods.Where(m => m.WasRemoved())
|
||
|
||
select new { t,
|
||
oldNbMethods = t.OlderVersion().NbMethods,
|
||
newNbMethods = t.NbMethods,
|
||
newMethods,
|
||
removedMethods }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[//<Name>Avoid transforming an immutable type into a mutable one</Name>
|
||
// Users of an immutable type often rely on the fact that the type is immutable.
|
||
// If an immutable type becomes mutable, there are chances that this will break its users.
|
||
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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid transforming an immutable field into a mutable one</Name>
|
||
// Users of an immutable field often rely on the fact that the type is immutable.
|
||
// If an immutable field becomes mutable, there are chances that this will break its users.
|
||
warnif count > 0
|
||
from f in Application.Fields where
|
||
f.IsPresentInBothBuilds() &&
|
||
f.OlderVersion().IsImmutable &&
|
||
!f.IsImmutable
|
||
select f]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid adding instance fields to a type that already had many instance fields</Name>
|
||
// 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
|
||
|
||
warnif count > 0
|
||
from t in JustMyCode.Types where
|
||
t.IsPresentInBothBuilds() &&
|
||
!t.IsStatic
|
||
|
||
let oldNbInstanceFields = t.OlderVersion().InstanceFields
|
||
let newNbInstanceFields = t.InstanceFields
|
||
where
|
||
newNbInstanceFields.Count() > oldNbInstanceFields .Count() &&
|
||
oldNbInstanceFields.Count() > 6
|
||
|
||
let newInstanceFields = t.InstanceFields.Where(f => f.WasAdded())
|
||
|
||
select new { t,
|
||
oldCount = oldNbInstanceFields.Count() ,
|
||
newCount = newNbInstanceFields.Count() ,
|
||
newInstanceFields }]]></Query>
|
||
</Group>
|
||
<Group Name="Object Oriented Design" Active="True" ShownInReport="True">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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()
|
||
|
||
// Warn for classes with 3 or more base classes.
|
||
// Notice that we don't count third-party classes
|
||
// because this rule concerns your code design,
|
||
// not third-party libraries consumed design.
|
||
where baseClasses.Count() >= 3
|
||
|
||
select new { t, baseClasses,
|
||
// The metric value DepthOfInheritance takes account
|
||
// of third-party base classes
|
||
t.DepthOfInheritance }
|
||
|
||
// Branches too long in the derivation should be avoided.
|
||
// See the definition of the DepthOfInheritance metric here
|
||
// http://www.ndepend.com/Metrics.aspx#DIT
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Overrides of Method() should call base.Method()</Name>
|
||
// Overrides of Method() should refine the behavior of base.Method().
|
||
// If 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 design provides valid reasons
|
||
// that advocates that the base behavior must be replaced and not refined.
|
||
//
|
||
// Discussions on this topic are available here:
|
||
// http://stackoverflow.com/questions/1107022/should-i-call-the-base-class-implementation-when-overriding-a-method-in-c-sharp
|
||
// http://stackoverflow.com/questions/2945147/make-sure-base-method-gets-called-in-c-sharp
|
||
|
||
warnif count > 0
|
||
from t in Types // Take account of third-party types too
|
||
|
||
// 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 }
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Do not hide base class methods</Name>
|
||
// 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.
|
||
|
||
// More on hiding vs. virtual usefulness here:
|
||
// http://www.artima.com/intv/nonvirtual.html
|
||
// http://blogs.msdn.com/b/ericlippert/archive/2008/05/21/method-hiding-apologia.aspx
|
||
|
||
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 classe.
|
||
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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>A stateless class or structure might be turned into a static type</Name>
|
||
// This rule indicates stateless types that might
|
||
// eventually be turned into static classes.
|
||
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
|
||
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Non-static classes should be instantiated or turned to static</Name>
|
||
// Notice that classes only instantiated through reflection, like plug-in root classes
|
||
// are matched by this rules.
|
||
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
|
||
!t.DeriveFrom("System.MarshalByRefObject".AllowNoMatch()) // Types instantiated through remoting infrastructure
|
||
|
||
// 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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods should be declared static if possible</Name>
|
||
warnif count > 0
|
||
|
||
// When an instance method can be safely declared as static you should declare it as static.
|
||
// Since it doesn't use any instance data and method of its type and base-types,
|
||
// you should consider if such a method could be moved to a static utility class
|
||
// or if it is strongly related enough to its current declaring type to stay in it.
|
||
//
|
||
// Turning an instance method into a static method is a micro performance optimization
|
||
// since a static method is a bit cheaper to invoke than an instance method.
|
||
|
||
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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Constructor should not call a virtual methods</Name>
|
||
|
||
// Returns constructor of a non-sealed type calling virtual methods.
|
||
// In such a situation, if a derived class overrides the method,
|
||
// then the override method will be called before the derived constructor.
|
||
// This makes the class fragile to derive from.
|
||
//
|
||
// Violations reported can be solved by re-designing object initialisation
|
||
// or by marking the parent class as sealed, if possible.
|
||
|
||
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.ParentType == t ||
|
||
t.DeriveFrom(mCalled.ParentType))
|
||
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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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() }
|
||
|
||
// The Singleton pattern consists in syntactically enforcing that a class
|
||
// has just one unique instance.
|
||
// At first glance, this pattern looks appealing and it is widely used.
|
||
// However, we discourage you from using singleton classes because experience
|
||
// shows that singletons often result in less testable and less maintainable code.
|
||
// 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/
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Don't assign static fields from instance methods</Name>
|
||
// Assigning static fields from instance methods leads to
|
||
// poorly maintainable and non thread-safe code.
|
||
// It is advised to assign static fields inline or from class constructor.
|
||
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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// Interfaces define members that provide a behavior
|
||
// or usage contract. The functionality 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
|
||
// interface's members. An empty interface does not
|
||
// define any members, and as such, 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 of
|
||
// identifying a group of types. If this identification
|
||
// will occur at runtime, the correct way to accomplish
|
||
// this is to use a custom attribute. Use the presence
|
||
// or absence of the attribute, or the attribute's
|
||
// properties, to identify the target types. If the
|
||
// identification must occurs at compile time, then using
|
||
// an empty interface is acceptable.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid types initialization cycles</Name>
|
||
warnif count > 0
|
||
|
||
// 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 explicitely declared in C# or VB.NET, to exist in compiled IL code.
|
||
// Having a static field inline initialization is enought to have
|
||
// the cctor implicitely 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://msmvps.com/blogs/jon_skeet/archive/2012/04/07/1808561.aspx
|
||
|
||
// 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.
|
||
|
||
// 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
|
||
}]]></Query>
|
||
</Group>
|
||
<Group Name="Design" Active="True" ShownInReport="False">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid custom delegates</Name>
|
||
|
||
// Prefer using one of the standard generic delegate type in
|
||
// Predicate<T> Func<T0,T1,..,TResult> Action<T0,T1,..>
|
||
// instead of creating your own delegate type.
|
||
// 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 delegate that are consumed by DllImport extern methods
|
||
// must not be converted, else this could provoke marshalling issues.
|
||
|
||
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 canot 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" ?
|
||
"Predicate" + signature3 : resultTypeName == "" ?
|
||
"Action" + signature3 :
|
||
"Func" + signature3.Replace(">", "," + resultTypeName + ">")
|
||
|
||
|
||
select new { t, replaceWith }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types with disposable instance fields must be disposable</Name>
|
||
warnif count > 0
|
||
|
||
let iDisposable = ThirdParty.Types.WithFullName("System.IDisposable").FirstOrDefault()
|
||
where iDisposable != null // iDisposable can be null if the code base doesn't use at all System.IDisposable
|
||
|
||
from t in Application.Types where
|
||
!t.Implement(iDisposable) &&
|
||
!t.IsGeneratedByCompiler
|
||
|
||
let instanceFieldsDisposable =
|
||
t.InstanceFields.Where(f => f.FieldType != null &&
|
||
f.FieldType.Implement(iDisposable))
|
||
|
||
where instanceFieldsDisposable.Count() > 0
|
||
select new { t, instanceFieldsDisposable }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Disposable types with unmanaged resources should declare finalizer</Name>
|
||
// warnif count > 0
|
||
let iDisposable = ThirdParty.Types.WithFullName("System.IDisposable").SingleOrDefault()
|
||
where iDisposable != null // iDisposable can be null if the code base doesn't use at all System.IDisposable
|
||
|
||
let disposableTypes = Application.Types.ThatImplement(iDisposable)
|
||
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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Classes that are candidate to be turned into structures</Name>
|
||
//
|
||
// CAUTION: Before applying this rule, make sure to understand
|
||
// the implication of transforming a class into a structure.
|
||
// http://msdn.microsoft.com/en-us/library/aa664471(v=vs.71).aspx
|
||
//
|
||
// Int32, Double or Boolean are structures and not classes.
|
||
// Structure are particularly suited to implement lightweight values.
|
||
// Hence a class is candidate to be turned into a structure
|
||
// when its instances are lightweight values.
|
||
//
|
||
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 have no base class
|
||
t.DepthOfDeriveFrom("System.Object".AllowNoMatch()) == 1
|
||
|
||
select new { t, t.SizeOfInst, t.InstanceFields } // Must derive directly from System.Object
|
||
|
||
// && 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.
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid namespaces with few types</Name>
|
||
warnif count > 0 from n in JustMyCode.Namespaces
|
||
let types = n.ChildTypes.Where(t => !t.IsGeneratedByCompiler)
|
||
where
|
||
types.Count() < 5
|
||
orderby types.Count() ascending
|
||
select new { n, types }
|
||
|
||
// Make sure that there is a logical organization
|
||
// to each of your namespaces, and that there is a
|
||
// valid reason for putting 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]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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.NbLinesOfCode, t.Visibility }
|
||
|
||
|
||
// 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.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Declare types in namespaces</Name>
|
||
warnif count > 0 from n in Application.Namespaces where
|
||
n.Name == ""
|
||
select new { n, n.ChildTypes, n.NbLinesOfCode }
|
||
|
||
// 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. If an anonymous namespace can be found,
|
||
// it means that it contains types outside of namespaces.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// Types where SizeOfInst > 64 might degrade performance
|
||
// (depending on the number of instances created at runtime)
|
||
// and might be hard to maintain. However it is not a rule
|
||
// since sometime there is no alternative (the size of
|
||
// instances of the System.Net.NetworkInformation.SystemIcmpV6Statistics
|
||
// standard class is 2064 bytes).
|
||
// 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/Metrics.aspx#SizeOfInst]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// Thanks to generics, boxing and unboxing should be rare.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// 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.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Equals() should be overridden by types implementing the '==' operator</Name>
|
||
warnif count > 0
|
||
from m in Application.Methods where
|
||
m.IsOperator &&
|
||
m.SimpleName == "op_Equality"
|
||
let equalsMethod = m.ParentType.InstanceMethods.Where(m0 => m0.Name == "Equals(Object)").SingleOrDefault()
|
||
where equalsMethod == null
|
||
select m
|
||
]]></Query>
|
||
</Group>
|
||
<Group Name="Architecture and Layering" Active="True" ShownInReport="False">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Avoid namespaces mutually dependent</Name>
|
||
warnif count > 0
|
||
// Foreach pair of namespace mutually dependent, this rule lists pairs.
|
||
// 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.
|
||
// It means the first namespace is certainly at a lower level in the architecture than the second.
|
||
//
|
||
// To explore the coupling between two namespaces mutually dependent:
|
||
// 1) 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 cell
|
||
// 4) in the matrix command bar, click the button: Remove empty Row(s) en Column(s)
|
||
// At this point, the dependency matrix shows types involved into the coupling.
|
||
//
|
||
// Following this rule is useful to avoid namespaces dependency cycles.
|
||
// More on this in our white books relative to partitioning code.
|
||
// http://www.ndepend.com/WhiteBooks.aspx
|
||
|
||
|
||
// 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, shouldntUse = second, typesOfFirstUsedBySecond, typesOfSecondUsedByFirst }
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid namespaces dependency cycles</Name>
|
||
warnif count > 0
|
||
// This query lists all application namespace dependency cycles.
|
||
// Each row shows a different cycle, prefixed with a 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 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 but efficient solution to architecture for your code base.
|
||
// http://www.ndepend.com/WhiteBooks.aspx
|
||
|
||
|
||
// 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 }
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// Each .NET Assembly represents one or several physical file.
|
||
// 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:
|
||
// - Much faster compilation time
|
||
// (compilation time divided by 10 wouldn't be surprising)
|
||
// - Faster startup time for your program
|
||
// - Easier deploiement thanks to less files to manage.
|
||
// - If you are developing a Framework,
|
||
// less .NET assemblies to reference and manage for your users
|
||
//
|
||
// More on this in our white books relative to partitioning code.
|
||
// http://www.ndepend.com/WhiteBooks.aspx ]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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
|
||
}]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Assemblies with poor cohesion (RelationalCohesion)</Name>
|
||
warnif count > 0 from a in Application.Assemblies where
|
||
a.NbTypes > 20 && // Relational Cohesion metrics is relevant only if there are several types
|
||
(a.RelationalCohesion < 1.5 ||
|
||
a.RelationalCohesion > 4.0)
|
||
select new { a, a.NbTypes, a.RelationalCohesion }
|
||
|
||
// As classes inside an assembly should be strongly related,
|
||
// the cohesion should be high. On the other hand, a value
|
||
// which is too high may indicate over-coupling. A good range
|
||
// for RelationalCohesion is 1.5 to 4.0.
|
||
// See the definition of the RelationalCohesion metric here
|
||
// http://www.ndepend.com/Metrics.aspx#RelationalCohesion]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// See the definition of the NormDistFromMainSeq metric here
|
||
// http://www.ndepend.com/Metrics.aspx#DitFromMainSeq]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Example of custom rule to check for dependency</Name>
|
||
|
||
// To define a rule that warns if a particular dependency exist or not,
|
||
// from a code element A to a code element B
|
||
// A and B being an Assembly Namespace Type Method or Field,
|
||
// A and B not necessarily two Assemblies or two Namespaces...
|
||
// you can first, right click the cell in the Dependency Matrix
|
||
// with B in row and A in column,
|
||
// or right-click the concerned arrow in the Dependency Graph
|
||
// from A to B, and choose to:
|
||
// "Generate a code rule that warns if this dependency exists"
|
||
//
|
||
// The generated rule will look like the one below.
|
||
// It is now up to you to adapt this rule to check your needs.
|
||
|
||
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)
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Higher cohesion - lower coupling</Name>
|
||
// It is deemed as a good software architecture practice to clearly separate
|
||
// 'abstract' namespaces containing only abstractions (interfaces, enumerations, delegates)
|
||
// from other 'concrete' namespaces, that contains 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 following code query, define sets of abstract and concrete namespaces
|
||
// and shows for each concrete namespaces, which concrete and abstract namespaces are used.
|
||
//
|
||
// This query can be adapted to a rule, depending on how much you want
|
||
// your code architecture being decoupled.
|
||
//
|
||
|
||
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 }]]></Query>
|
||
</Group>
|
||
<Group Name="API Breaking Changes" Active="True" ShownInReport="True">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>API Breaking Changes: Types</Name>
|
||
// This rule warns if a publicly visible type is
|
||
// not publicly visible anymore or if it has been removed.
|
||
// Such type can break the code of your clients.
|
||
|
||
warnif count > 0 from t in codeBase.OlderVersion().Application.Types
|
||
where t.IsPubliclyVisible &&
|
||
|
||
// The type has been removed and its parent assembly hasn't been removed ...
|
||
( (t.WasRemoved() && !t.ParentAssembly.WasRemoved()) ||
|
||
|
||
// ... or the type is not publicly visible anymore
|
||
!t.WasRemoved() && !t.NewerVersion().IsPubliclyVisible)
|
||
|
||
select new { t,
|
||
NewVisibility = (t.WasRemoved() ? " " : t.NewerVersion().Visibility.ToString()) }
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>API Breaking Changes: Methods</Name>
|
||
// This rule warns if a publicly visible method is
|
||
// not publicly visible anymore or if it has been removed.
|
||
// Such method can break the code of your clients.
|
||
|
||
warnif count > 0 from m in codeBase.OlderVersion().Application.Methods
|
||
where m.IsPubliclyVisible &&
|
||
|
||
// The method has been removed and its parent type hasn't been removed ...
|
||
( (m.WasRemoved() && !m.ParentType.WasRemoved()) ||
|
||
|
||
// ... or the method is not publicly visible anymore
|
||
!m.WasRemoved() && !m.NewerVersion().IsPubliclyVisible)
|
||
|
||
select new { m,
|
||
NewVisibility = (m.WasRemoved() ? " " : m.NewerVersion().Visibility.ToString()) }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>API Breaking Changes: Fields</Name>
|
||
// This rule warns if a publicly visible field is
|
||
// not publicly visible anymore or if it has been removed.
|
||
// Such field can break the code of your clients.
|
||
|
||
warnif count > 0 from f in codeBase.OlderVersion().Application.Fields
|
||
where f.IsPubliclyVisible &&
|
||
|
||
// The field has been removed and its parent type hasn't been removed ...
|
||
( (f.WasRemoved() && !f.ParentType.WasRemoved()) ||
|
||
|
||
// ... or the field is not publicly visible anymore
|
||
!f.WasRemoved() && !f.NewerVersion().IsPubliclyVisible)
|
||
|
||
select new { f,
|
||
NewVisibility = (f.WasRemoved() ? " " : f.NewerVersion().Visibility.ToString()) }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>API Breaking Changes: Interfaces and Abstract Classes</Name>
|
||
// 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.
|
||
// This can break the code of clients
|
||
// that implement such interface or derive from such abstract class.
|
||
|
||
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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Broken serializable types</Name>
|
||
// Find breaking changes in types marked with SerializableAttribute.
|
||
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 }
|
||
|
||
// 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.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Avoid transforming immutable types into mutable types</Name>
|
||
|
||
// Immutability is a strong property on a type.
|
||
// Breaking immutability can result in serious problem for an algorithm consummer
|
||
// that has been written taking account of the type immutability.
|
||
|
||
// 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
|
||
|
||
warnif count > 0
|
||
from t in Application.Types where
|
||
t.IsPresentInBothBuilds() &&
|
||
!t.IsStatic &&
|
||
!t.IsImmutable &&
|
||
t.OlderVersion().IsImmutable
|
||
|
||
let mutableFields = from f in t.InstanceFields where !f.IsImmutable select f
|
||
|
||
select new { t, mutableFields }
|
||
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Avoid changing enumerations Flags status</Name>
|
||
|
||
// Being tagged with the Flags attribute is a strong property for an enumeration.
|
||
// Changing the Flags status of an enumeration has significant impact for its client.
|
||
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 isFlags = t.HasAttribute(newFlags)
|
||
let wasFlags = t.OlderVersion().HasAttribute(oldFlags)
|
||
where isFlags != wasFlags
|
||
select new { t, isFlags, wasFlags }
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>API: New publicly visible types</Name>
|
||
// List types that are new in the public surface of your assemblies
|
||
|
||
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()) }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>API: New publicly visible methods</Name>
|
||
// List methods that are new in the public surface of your assemblies
|
||
|
||
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()) }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>API: New publicly visible fields</Name>
|
||
// List fields that are new in the public surface of your assemblies
|
||
|
||
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()) }]]></Query>
|
||
</Group>
|
||
<Group Name="Code Diff Summary" Active="True" ShownInReport="True">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>New assemblies</Name>
|
||
from a in Application.Assemblies where a.WasAdded()
|
||
select new { a, a.NbLinesOfCode }
|
||
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Assemblies removed</Name>
|
||
from a in codeBase.OlderVersion().Application.Assemblies where a.WasRemoved()
|
||
select new { a, a.NbLinesOfCode }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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() }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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() }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>New types</Name>
|
||
from t in Application.Types where
|
||
!t.ParentNamespace.WasAdded() &&
|
||
t.WasAdded()
|
||
select new { t, t.NbLinesOfCode }
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types removed</Name>
|
||
from t in codeBase.OlderVersion().Application.Types where
|
||
!t.ParentNamespace.WasRemoved() &&
|
||
t.WasRemoved()
|
||
select new { t, t.NbLinesOfCode }
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types where code was changed</Name>
|
||
// 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
|
||
|
||
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 }
|
||
/*from t in Application.Types where t.CodeWasChanged() && t.IsPresentInBothBuild
|
||
select new { t, t.NbLinesOfCode,
|
||
oldNbLinesOfCode = t.OlderVersion().NbLinesOfCode ,
|
||
delta = (int) t.NbLinesOfCode - t.OlderVersion().NbLinesOfCode }*/]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>New methods</Name>
|
||
from m in Application.Methods where
|
||
!m.ParentType.WasAdded() &&
|
||
m.WasAdded()
|
||
select new { m, m.NbLinesOfCode }
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods removed</Name>
|
||
from m in codeBase.OlderVersion().Application.Methods where
|
||
!m.ParentType.WasRemoved() &&
|
||
m.WasRemoved()
|
||
select new { m, m.NbLinesOfCode }
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods where code was changed</Name>
|
||
// 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
|
||
|
||
from m in Application.Methods where m.CodeWasChanged()
|
||
select new { m, m.NbLinesOfCode,
|
||
oldNbLinesOfCode = m.OlderVersion().NbLinesOfCode ,
|
||
delta = (int?) m.NbLinesOfCode - m.OlderVersion().NbLinesOfCode }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>New fields</Name>
|
||
from f in Application.Fields where
|
||
!f.ParentType.WasAdded() &&
|
||
f.WasAdded()
|
||
select new { f }
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Fields removed</Name>
|
||
from f in codeBase.OlderVersion().Application.Fields where
|
||
!f.ParentType.WasRemoved() &&
|
||
f.WasRemoved()
|
||
select new { f }
|
||
]]></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 }
|
||
]]></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 }
|
||
]]></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 }]]></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}]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }]]></Query>
|
||
</Group>
|
||
<Group Name="Test and Code Coverage" Active="True" ShownInReport="True">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>C.R.A.P method code metric</Name>
|
||
// Change Risk Analyzer and Predictor (i.e. CRAP) code metric
|
||
// This code metric helps in pinpointing overly complex and untested code.
|
||
// Reference: http://www.artima.com/weblogs/viewpost.jsp?thread=215899
|
||
// Formula: CRAP(m) = comp(m)^2 * (1 – cov(m)/100)^3 + comp(m)
|
||
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, uncoveredPercentage = uncov*100, m.NbLinesOfCode }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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/Metrics.aspx#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 } ]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Method changed poorly covered</Name>
|
||
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 }
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Method added poorly covered</Name>
|
||
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 }
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types 95% to 99% covered</Name>
|
||
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 }
|
||
|
||
// Having types 100% covered by tests is a good idea because
|
||
// the small portion of code hard to cover, is also the
|
||
// portion of code that is the most likely to contain bugs.
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Namespaces 95% to 99% covered</Name>
|
||
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 }
|
||
|
||
// Having types 100% covered by tests is a good idea because
|
||
// the small portion of code hard to cover, is also the
|
||
// portion of code that is the most likely to contain bugs.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// By using a FullCoveredAttribute, you can signify to developers
|
||
// that a class is, and must remain in the future, 100% covered by tests.
|
||
// If you don't want to link NDepend.API.dll,
|
||
// you can use your own attribute and adapt this rule.
|
||
|
||
// Having types 100% covered by tests is a good idea because
|
||
// the small portion of code hard to cover, is also the
|
||
// portion of code that is the most likely to contain bugs.
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// By using a FullCoveredAttribute, you can signify to developers
|
||
// that a class is, and must remain in the future, 100% covered by tests.
|
||
// If you don't want to link NDepend.API.dll, you can use your own attribute and adapt this rule.
|
||
|
||
// Having types 100% covered by tests is a good idea because
|
||
// the small portion of code hard to cover, is also the
|
||
// portion of code that is the most likely to contain bugs.
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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}
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Test Methods</Name>
|
||
|
||
// We advise to not include test assemblies in code analyzed by NDepend.
|
||
// But if you wish the current query to run properly,
|
||
// you'll need to consider test assemblies in your list of application assemblies analyzed by NDepend..
|
||
|
||
let testAttr = ThirdParty.Types.WithNameIn("TestAttribute", "TestCaseAttribute")
|
||
let testMethods = Methods.TaggedWithAnyAttributes(testAttr)
|
||
from m in testMethods
|
||
select m
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods directly called by test Methods</Name>
|
||
|
||
// Lists all methods directly called by tests methods.
|
||
// Overrides of virtual and absract 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.
|
||
// But if you wish the current query to run properly,
|
||
// you'll need to consider test assemblies in your list of application assemblies analyzed by NDepend..
|
||
|
||
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
|
||
}]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods directly and indirectly called by test Methods</Name>
|
||
|
||
// Lists all methods called directly or indirectly by tests methods.
|
||
// Overrides of virtual and absract 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.
|
||
// But if you wish the current query to run properly,
|
||
// you'll need to consider test assemblies in your list of application assemblies analyzed by NDepend.
|
||
|
||
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,
|
||
// --- Uncomment this line if your project import some coverage data ---
|
||
// m.PercentageCoverage
|
||
}]]></Query>
|
||
</Group>
|
||
<Group Name="Dead Code" Active="True" ShownInReport="True">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 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, t.TypesUsingMe, depth = deadTypesMetric[t] }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 explicitely calls explicit interface methods implementation.
|
||
!m.IsClassConstructor && // The IL code never explicitely calls class constructors.
|
||
!m.IsFinalizer && // The IL code never explicitely 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 explicitely 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 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, m.MethodsCallingMe, depth = deadMethodsMetric[m] }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 explicitely uses literal fields.
|
||
!f.IsEnumValue && // The IL code never explicitely uses enumeration value.
|
||
f.Name != "value__" && // Field named 'value__' are relative to enumerations and the IL code never explicitely 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 this rule.
|
||
select f]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Wrong usage of IsNotDeadCodeAttribute</Name>
|
||
|
||
// This IsNotDeadCodeAttribute can be used to signify that
|
||
// despite a member could be removed without provoking any syntax error
|
||
// (we also say it is dead code), your intention is to not remove this member.
|
||
// Default 'Dead Code' code rules take account of this attribute.
|
||
// IsNotDeadCodeAttribute is defined in NDepend.API.dll
|
||
// If you don't want to link NDepend.API.dll, you can use
|
||
// your own IsNotDeadCodeAttribute and adapt this rule.
|
||
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.NbTypesUsingMe > 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.NbMethodsCallingMe > 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.NbMethodsUsingMe > 0
|
||
select f
|
||
|
||
where types.Count() > 0 || methods.Count() > 0 || fields.Count() > 0
|
||
select new { tAttr, types , methods, fields }]]></Query>
|
||
</Group>
|
||
<Group Name="Visibility" Active="True" ShownInReport="False">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 this rule.
|
||
!t.HasAttribute("NDepend.Attributes.CannotDecreaseVisibilityAttribute".AllowNoMatch()) &&
|
||
!t.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".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)
|
||
|
||
select new { t, t.Visibility ,
|
||
CouldBeDeclared = t.OptimalVisibility,
|
||
t.TypesUsingMe }
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 this rule.
|
||
|
||
select new { f,
|
||
f.Visibility ,
|
||
CouldBeDeclared = f.OptimalVisibility,
|
||
f.MethodsUsingMe }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Types that could be declared as private, nested in a parent type</Name>
|
||
// The conditions for a type to be nested into a parent type
|
||
// is that the parent type is the only type using it,
|
||
// and that the parent type is declared in the same namespace.
|
||
//
|
||
// Declaring a type as private into a parent type helps enforcing encapsulation.
|
||
// But since nested private types are hardly testable, this rule might
|
||
// not be applied for types used directly by tests.
|
||
|
||
warnif count > 0
|
||
from t in Application.Types
|
||
where !t.IsGeneratedByCompiler &&
|
||
!t.IsNested &&
|
||
!t.IsPubliclyVisible &&
|
||
// 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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid publicly visible constant fields</Name>
|
||
|
||
// Match constant fields which are visible outside their parent assembly.
|
||
// Such field, when used outside its parent assembly,
|
||
// has its constant value hard-coded into the client assembly.
|
||
// Changing the field's value requires to recompile all assemblies
|
||
// that use the field.
|
||
|
||
// Declare the field as static readonly instead. This way, the value can be
|
||
// safely changed, without the need to recompile client assemblies.
|
||
|
||
warnif count > 0
|
||
from f in Application.Fields
|
||
where f.IsLiteral && f.IsPubliclyVisible
|
||
&& !f.IsEnumValue // This situation also applies to enumeration values
|
||
// but to avoid too many false positives, per default
|
||
// this rule doesn't match these particular fields.
|
||
select f]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Fields should be declared as private</Name>
|
||
// Fields should be considered as implementation details
|
||
// and as a consequence they should be declared as private.
|
||
// When a field is private, the caller cannot get
|
||
// inappropriate direct access to the field.
|
||
|
||
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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Constructors of abstract classes should be declared as protected or private</Name>
|
||
// Constructors of an abstract class can be accessed only from its class and derived class.
|
||
// Declaring such a constructor with another visibility level is useless and potentially misleading.
|
||
|
||
// Notice that if a constructor of an abstract class is declared as private,
|
||
// it can only be accessed from derived classes nested in the abstract class.
|
||
|
||
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 }
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[//<Name>Avoid public methods not publicly visible</Name>
|
||
// Matched methods are declared public but are not publicly visible by assemblies consumers.
|
||
// Their visibility level must be decreased.
|
||
|
||
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
|
||
select m]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods that should be declared as 'public' in C#, 'Public' in VB.NET</Name>
|
||
// The condition 'ShouldBePublic' shows code elements
|
||
// declared as 'internal\Friend' that are used outside
|
||
// of their assembly thanks to the Attribute
|
||
// System.Runtime.CompilerServices.InternalsVisibleToAttribute
|
||
|
||
from m in Application.Methods where
|
||
m.ShouldBePublic
|
||
let usedInAssemblies = m.MethodsCallingMe.ParentAssemblies().Except(m.ParentAssembly)
|
||
select new { m, m.ParentAssembly, usedInAssemblies, m.MethodsCallingMe }
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Wrong usage of CannotDecreaseVisibilityAttribute</Name>
|
||
|
||
// The CannotDecreaseVisibilityAttribute can be used to signify that despite
|
||
// a member could have a lower visibility without provoking any syntax error,
|
||
// your intention is to not change this member visibility.
|
||
// Default Visibility code rules take account of this attribute.
|
||
// CannotDecreaseVisibilityAttribute is defined in NDepend.API.dll
|
||
// If you don't want to link NDepend.API.dll, you can use
|
||
// your own CannotDecreaseVisibilityAttribute and adapt this rule.
|
||
|
||
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.Where(f => !f.IsLiteral).Any())
|
||
)
|
||
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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 &&
|
||
|
||
// A method is considered as 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 }
|
||
// This rule implementation relies on the facts that:
|
||
// -> A method name contains the type of its parameters.
|
||
// -> All EventArgs derived types have the suffix "EventArgs".]]></Query>
|
||
</Group>
|
||
<Group Name="Purity - Immutability - Side-Effects" Active="True" ShownInReport="False">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 &&
|
||
!f.IsGeneratedByCompiler &&
|
||
!f.IsEventDelegateObject
|
||
select new { f, f.SizeOfInst }
|
||
|
||
// 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 throught the lifetime
|
||
// of the object.
|
||
// For a static field, this means its value will
|
||
// remain constant throught the lifetime of the
|
||
// program.
|
||
// In both cases, such field can safely be marked
|
||
// with the C# readonly keyword
|
||
// (ReadOnly in VB.NET).
|
||
|
||
// The condition IsInitOnly matches fields that
|
||
// are marked with the C# readonly keyword
|
||
// (ReadOnly in VB.NET).
|
||
|
||
|
||
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// It is deemed as a good practice to make
|
||
// your structure immutable.
|
||
// An object is immutable if its state doesn’t
|
||
// change once the object has been created.
|
||
// Consequently, a structure is immutable if
|
||
// its instances are immutable.
|
||
// Immutable types naturally simplify code by
|
||
// limiting side-effects.
|
||
// See some explanations on immutability and
|
||
// how NDepend supports it here:
|
||
// http://codebetter.com/blogs/patricksmacchia/archive/2008/01/13/immutable-types-understand-them-and-use-them.aspx
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Property Getters should be immutable</Name>
|
||
warnif count > 0 from m in Application.Methods where
|
||
m.IsPropertyGetter &&
|
||
( ( !m.IsStatic && m.ChangesObjectState) ||
|
||
( m.IsStatic && m.ChangesTypeState) )
|
||
|
||
let fieldsAssigned = m.FieldsAssigned
|
||
|
||
select new { m, m.NbLinesOfCode, fieldsAssigned }
|
||
|
||
// This rule might be violated in the case of object lazy initialized
|
||
// when the property getter is accessed the first time.
|
||
// But in general, the callers of a property
|
||
// doesn't expect to change any state through the call.
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// As explained in this blog post
|
||
// http://codebetter.com/patricksmacchia/2011/05/04/back-to-basics-usage-of-static-members
|
||
// static fields should be used to hold only constant and immutable states.
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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.IsPrivate && !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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 &&
|
||
(f.IsPrivate || f.IsProtected) // Don't match fields assigned outside their classes.
|
||
|
||
// 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 f.MethodsAssigningMe.Count() >= (!f.IsStatic ? 4 : 2)
|
||
select new { f,
|
||
f.MethodsAssigningMe,
|
||
f.MethodsReadingMeButNotAssigningMe,
|
||
f.MethodsUsingMe,
|
||
f.ParentType }
|
||
|
||
// A field assigned from many methods is a symptom of bug-prone code.
|
||
// This situation makes harder to anticipate the field state during runtime.
|
||
// The code is then hard 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, this potentially involves global random accesses from different parts of the application.
|
||
// This is why this rule provides a lower threshold 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 sent.
|
||
//
|
||
// 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).
|
||
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// This rule is violated when a public or internal
|
||
// type contains a public or internal read-only field
|
||
// that 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, rather than the object.
|
||
//
|
||
// To fix a violation of this rule, remove the read-only
|
||
// modifier or, if a breaking change is acceptable,
|
||
// replace the field with an immutable type.
|
||
//
|
||
// An object is immutable if its state doesn’t
|
||
// change once the object has been created.
|
||
// Consequently, a class or a structure is
|
||
// immutable if its instances are immutable.
|
||
// Immutable types naturally simplify code by
|
||
// limiting side-effects.
|
||
]]></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 }
|
||
|
||
// 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.
|
||
//
|
||
// 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. For more information,
|
||
// see System.Collections.Generic.IReadOnlyList<T>
|
||
// System.Collections.Generic.IReadOnlyCollection<T>
|
||
// System.Collections.ReadOnlyCollectionBase.
|
||
//
|
||
// -> 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.
|
||
]]></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 }
|
||
|
||
// Types matched by this query are immutable but
|
||
// are not tagged with the ImmutableAttribute.
|
||
// The benefit of tagging them with the ImmutableAttribute
|
||
// is that the rule 'Regression on immutable types'
|
||
// will warn when the immutability of a type gets broken.
|
||
|
||
// NDepend.Attributes.ImmutableAttribute is defined in the
|
||
// redistributable assembly $NDependInstallDir$\Lib\NDepend.API.dll
|
||
// You can define your own attribute to tag 'immutable' types.
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// A method is pure if its execution doesn’t change
|
||
// the value of any instance or static field.
|
||
// Pure methods naturally simplify code by limiting
|
||
// side-effects.
|
||
// See some explanations on immutability - purity and
|
||
// how NDepend supports it here:
|
||
// http://codebetter.com/blogs/patricksmacchia/archive/2008/01/13/immutable-types-understand-them-and-use-them.aspx
|
||
|
||
// NDepend.Attributes.PureAttribute is defined in the
|
||
// redistributable assembly $NDependInstallDir$\Lib\NDepend.API.dll
|
||
// You can define your own attribute to tag 'pure' methods
|
||
// or you can use as well System.Diagnostics.Contract.PureAttribute
|
||
// introduced with .NET v4.0.
|
||
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Pure methods should be tagged with PureAttribute</Name>
|
||
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
|
||
select new { m, m.NbLinesOfCode }
|
||
|
||
// Methods matched by this query are pure but
|
||
// are not tagged with the PureAttribute.
|
||
// The benefit of tagging them with the PureAttribute
|
||
// is that the rule 'Regression on pure methods'
|
||
// will warn when the purity of a method gets broken.
|
||
|
||
// NDepend.Attributes.PureAttribute is defined in the
|
||
// redistributable assembly $NDependInstallDir$\Lib\NDepend.API.dll
|
||
// You can define your own attribute to tag 'pure' methods
|
||
// or you can use as well System.Diagnostics.Contract.PureAttribute
|
||
// introduced with .NET v4.0.]]></Query>
|
||
</Group>
|
||
<Group Name="Naming Conventions" Active="True" ShownInReport="False">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// This naming convention provokes debate.
|
||
// Don't hesitate to customize the regex of
|
||
// NameLike to your preference.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// This naming convention provokes debate.
|
||
// Don't hesitate to customize the regex of
|
||
// NameLike to your preference.
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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
|
||
|
||
// 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
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 &&
|
||
|
||
// equivalent to: DepthOfDeriveFrom "System.Object" == 1
|
||
t.DepthOfInheritance == 1 &&
|
||
|
||
((!t.IsGeneric && !t.NameLike (@"Base$")) ||
|
||
( t.IsGeneric && !t.NameLike (@"Base<")))
|
||
select new { t, t.DepthOfInheritance }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 &&
|
||
!t.NameLike (@"Exception$") &&
|
||
!t.NameLike (@"ExceptionBase$") // Allow the second suffix Base
|
||
// for base exception classes.
|
||
select new { t, t.NbLinesOfCode }
|
||
|
||
// The name of an exception class should end with 'Exception'.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 new { t, t.NbLinesOfCode }
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 or ...
|
||
!t.IsSpecialName &&
|
||
!t.IsGeneratedByCompiler
|
||
|
||
|
||
select new { t, t.NbLinesOfCode }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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
|
||
|
||
// The name of a regular method should
|
||
// begin with an Upper letter.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 new { f, f.SizeOfInst }
|
||
|
||
// 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.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid methods with name too long</Name>
|
||
warnif count > 0 from m in Application.Methods where
|
||
!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 }
|
||
|
||
// The regex matches methods with name longer
|
||
// than 35 characters.
|
||
// Method Name doesn't contain the type and namespace
|
||
// prefix, FullName does.
|
||
// The regex computes the method name length from
|
||
// the beginning until the first open parenthesis
|
||
// or first lower than (for generic methods).
|
||
// Explicit Interface Implementation methods are
|
||
// discarded because their names are prefixed
|
||
// with the interface name.
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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
|
||
|
||
// The regex matches fields with name longer
|
||
// than 35 characters.
|
||
// Field Name doesn't contain the type and
|
||
// namespace prefix, FullName does.
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Avoid having different types with same name</Name>
|
||
// Such practice typically creates confusion,
|
||
// and type naming collision inside a source file.
|
||
|
||
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
|
||
}]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid prefixing type name with parent namespace name</Name>
|
||
|
||
// For example a type named "RuntimeEnvironment"
|
||
// declared in a namespace named "Foo.Runtime"
|
||
// should be named "Environment"
|
||
|
||
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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid naming types and namespaces with the same identifier</Name>
|
||
|
||
// Not only this can provoke compiler resolution collision,
|
||
// but also, this makes code less maintainable because
|
||
// concepts are not concisely identified.
|
||
|
||
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) }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Don't call your method Dispose</Name>
|
||
// Methods shouldn't be called Dispose,
|
||
// this syntax is reserved for System.IDisposable type usage.
|
||
from m in Application.Methods.WithSimpleName("Dispose")
|
||
where !m.ParentType.Implement("System.IDisposable".AllowNoMatch())
|
||
select m]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Methods prefixed with 'Try' should return a boolean</Name>
|
||
// and such TryXXX method can eventually have out parameters to return results.
|
||
// Get inspired from the API design of:
|
||
// System.Int32.TryParse(int,out string):bool
|
||
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 }]]></Query>
|
||
</Group>
|
||
<Group Name="Source Files Organization" Active="True" ShownInReport="False">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid referencing source file out of Visual Studio project directory</Name>
|
||
|
||
// A source file located outside of the VS project directory can be add through:
|
||
// > Add > Existing Items... > Add As Link
|
||
//
|
||
// Don't use this possibility to share code accross several assemblies.
|
||
// This provoques type duplication at binary level.
|
||
// Hence maintainability is degraded and subtle versioning bug can appear.
|
||
// Prefer sharing types through classic libraries.
|
||
//
|
||
// This practice can be tolerated for certain types shared accross executable assemblies.
|
||
// Such type can be responsible for particular and not shareable startup related concerns,
|
||
// such as registering custom assembly resolving handlers or
|
||
// checking the .NET Framework version before loading any custom library.
|
||
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() }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Avoid duplicating a type definition across assemblies</Name>
|
||
// Prefer to factorize type definition in a library assembly.
|
||
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()
|
||
}]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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,
|
||
types = @group as IEnumerable<IType>,
|
||
sourceFile.FilePathString }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Namespace name should correspond to file location</Name>
|
||
|
||
// For a good code organization,
|
||
// do mirror the namespaces hierarchy and the source files directories tree.
|
||
|
||
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 } ]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 )
|
||
|
||
// 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
|
||
|
||
select new { mainNamespace,
|
||
|
||
// Typically a type in typesOutOfMainNamespace ...
|
||
// 1) ... is contained in the wrong namespace but its source file(s) is stored in the right directory.
|
||
// --> In such situation the type should be contained in mainNamespace.
|
||
// 2) ... is contained in the right namespace but its source file(s) is stored in the wrong directory
|
||
// --> In such situation the source file of type must be moved to the parent namespace directory.
|
||
// 3) ... is declared in multiple source files, stored in different directories.
|
||
// --> It would be preferable that all source files are stored in a single directory.
|
||
typesOutOfMainNamespace }
|
||
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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
|
||
|
||
select new { @namespace,
|
||
|
||
// Typically a type in typesDeclaredOutOfMainDir ...
|
||
// 1) ... is contained in the wrong namespace but its source file(s) is stored in the right directory.
|
||
// --> In such situation the type parent namespace should be the namespace corresponding to the directory.
|
||
// 2) ... is contained in the right namespace but its source file(s) is stored in the wrong directory
|
||
// --> In such situation the type source file should be moved to mainDir.
|
||
// 3) ... is declared in multiple source files, stored in different directories.
|
||
// --> It would be preferable that all source files are stored in a single directory.
|
||
typesDeclaredOutOfMainDir ,
|
||
|
||
mainDir = mainDir.ToString() }]]></Query>
|
||
</Group>
|
||
<Group Name=".NET Framework Usage" Active="True" ShownInReport="False">
|
||
<Group Name="System" Active="True" ShownInReport="False">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Mark ISerializable types with SerializableAttribute</Name>
|
||
from t in Application.Types where
|
||
t.IsPublic &&
|
||
!t.IsDelegate &&
|
||
t.Implement ("System.Runtime.Serialization.ISerializable".AllowNoMatch()) &&
|
||
!t.HasAttribute ("System.SerializableAttribute".AllowNoMatch())
|
||
select new { t, t.NbLinesOfCode }
|
||
|
||
// 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.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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
|
||
|
||
// 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.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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
|
||
|
||
// 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.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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
|
||
|
||
// 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
|
||
// an entire 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.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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
|
||
|
||
// When defining a custom attribute, mark it using
|
||
// AttributeUsageAttribute to indicate where in the
|
||
// source code the custom attribute can be applied.
|
||
// An attribute's meaning and intended usage will
|
||
// determine its valid locations in code. For example,
|
||
// if you are defining an attribute that identifies
|
||
// the person responsible for maintaining and enhancing
|
||
// each type in a library, and responsibility is
|
||
// always assigned at the type level, compilers should
|
||
// allow the attribute on classes, enumerations,
|
||
// and interfaces, but should not allow it on methods,
|
||
// events, or properties. Organizational policies and
|
||
// procedures would dictate whether the attribute
|
||
// should be allowed on assemblies.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Remove calls to GC.Collect()</Name>
|
||
warnif count > 0
|
||
|
||
let gcCollectMethods = ThirdParty.Methods.WithFullNameIn(
|
||
"System.GC.Collect()",
|
||
"System.GC.Collect(Int32)",
|
||
"System.GC.Collect(Int32,GCCollectionMode)")
|
||
|
||
from m in Application.Methods.UsingAny(gcCollectMethods)
|
||
select m
|
||
|
||
// It is preferrable to avoid calling GC.Collect()
|
||
// explicitely in order to avoid some performance pitfall.
|
||
// More in information on this here:
|
||
// http://blogs.msdn.com/ricom/archive/2004/11/29/271829.aspx]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Don't call GC.Collect() without calling GC.WaitForPendingFinalizers()</Name>
|
||
warnif count > 0
|
||
|
||
let gcCollectMethods = ThirdParty.Methods.WithFullNameIn(
|
||
"System.GC.Collect()",
|
||
"System.GC.Collect(Int32)",
|
||
"System.GC.Collect(Int32,GCCollectionMode)")
|
||
|
||
from m in Application.Methods.UsingAny(gcCollectMethods) where
|
||
!m.IsUsing ("System.GC.WaitForPendingFinalizers()".AllowNoMatch())
|
||
select new { m, m.NbLinesOfCode }
|
||
|
||
// It is preferrable to avoid calling GC.Collect()
|
||
// explicitely in order to avoid some performance
|
||
// pitfall. But if you wish to call GC.Collect(),
|
||
// 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.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// 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 there
|
||
// is no significant performance gain in using
|
||
// a data type smaller than Int32. If you cannot
|
||
// use the default data type, you should use one
|
||
// of the CLS-compliant integral types, Byte,
|
||
// Int16, Int32, or Int64, to ensure that all of
|
||
// the enumeration's values are representable in
|
||
// CLS-compliant programming languages.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Use integral or string argument for indexers</Name>
|
||
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
|
||
|
||
// 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.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// A field which name end with 'Uri' is deemed
|
||
// as representing a uri. Such field should be of
|
||
// type System.Uri.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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
|
||
|
||
// For .NET Framework version 1, 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.]]></Query>
|
||
</Group>
|
||
<Group Name="System.Collection" Active="True" ShownInReport="False">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Collection properties should be read only</Name>
|
||
|
||
// 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
|
||
// (with a call to the ICollection.Clear() method) and then re-populate the collection.
|
||
|
||
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_"))
|
||
.SingleOrDefault()
|
||
|
||
where propSetter != null &&
|
||
!propSetter.IsPrivate
|
||
|
||
select new { propSetter, propGetter.ReturnType }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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")
|
||
where forbiddenTypes.Count() > 0
|
||
from m in Application.Methods.ThatCreateAny(forbiddenTypes)
|
||
select m
|
||
|
||
// You can be forced to use HashTable or ArrayList
|
||
// because you are using third party code that requires
|
||
// working with these classes or because you are
|
||
// coding with .NET 1.x.]]></Query>
|
||
<Query Active="False" 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
|
||
|
||
// The cost of checking if a list contains an
|
||
// object is proportional to the size of the list
|
||
// (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() takes a constant
|
||
// time (O(0) operation).]]></Query>
|
||
<Query Active="False" 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 }
|
||
|
||
// Most of the time, clients of a method doesn't
|
||
// need to know the exact implementation of the
|
||
// collection returned. It is preferrable to return
|
||
// a collection interface such as IList<T>,
|
||
// ICollection<T> or IEnumerable<T>.]]></Query>
|
||
</Group>
|
||
<Group Name="System.Runtime.InteropServices" Active="True" ShownInReport="False">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// Methods marked with the DllImportAttribute
|
||
// attribute (or methods defined using the
|
||
// Declare keyword in Visual Basic) use
|
||
// Platform Invocation Services to access unmanaged
|
||
// code. Such methods should not be exposed. Keeping
|
||
// these methods private or internal ensures
|
||
// that your library cannot be used to breach
|
||
// security by allowing callers access to
|
||
// unmanaged APIs they could not call otherwise.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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
|
||
|
||
// Platform Invocation methods, such as those marked
|
||
// with the System.Runtime.InteropServices.DllImportAttribute
|
||
// attribute, or methods 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 do a full security review
|
||
// to ensure 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 do a full security
|
||
// review to ensure that the usage is secure because
|
||
// no stack walk will be performed.
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
// Native Methods' classes are declared as internal
|
||
// (Friend, in Visual Basic) and static.
|
||
]]></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) }
|
||
|
||
// Mutable object states are instance fields that
|
||
// can be modifed throught the lifetime of the object.
|
||
// Mutable type states are static fields that can be
|
||
// modifed throught 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.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Don't create threads explicitely</Name>
|
||
warnif count > 0 from m in Application.Methods where
|
||
m.CreateA ("System.Threading.Thread".AllowNoMatch())
|
||
select m
|
||
|
||
// Prefer using the thread pool instead of
|
||
// creating manually your own thread.
|
||
// Threads are costly objects.
|
||
// They take approximately 200,000 cycles to
|
||
// create and about 100,000 cycles to destroy.
|
||
// By default they reserve 1 megabyte of virtual
|
||
// memory for its stack and use 2,000-8,000
|
||
// cycles for each context switch.
|
||
// As a consequence, it is preferrable to let
|
||
// the thread pool recycle threads.
|
||
|
||
// Creating custom thread can also be the
|
||
// sign of flawed design, where tasks and
|
||
// threads have affinity. It is preferrable
|
||
// to code tasks that can be ran on any thread.]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Don't use dangerous threading methods</Name>
|
||
warnif count > 0
|
||
|
||
let wrongMethods = ThirdParty.Methods.WithFullNameIn(
|
||
|
||
// Usage of Thread.Abort() is dangerous.
|
||
// More information on this here:
|
||
// http://www.interact-sw.co.uk/iangblog/2004/11/12/cancellation
|
||
"System.Threading.Thread.Abort()",
|
||
"System.Threading.Thread.Abort(Object)",
|
||
|
||
// 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
|
||
"System.Threading.Thread.Sleep(Int32)",
|
||
|
||
// 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
|
||
"System.Threading.Thread.Suspend()",
|
||
"System.Threading.Thread.Resume()"
|
||
)
|
||
|
||
from m in Application.Methods.UsingAny(wrongMethods)
|
||
select new { m, calls = m.MethodsCalled.Intersect(wrongMethods) }
|
||
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Monitor TryEnter/Exit must be both called within the same method</Name>
|
||
// Else you expose yourself to complex error-prone scenarios.
|
||
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) }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>ReaderWriterLock Acquire[Reader/Writer]Lock/ReleaseLock must be both called within the same method</Name>
|
||
// Else you expose yourself to complex error-prone scenarios.
|
||
warnif count > 0
|
||
|
||
let acquireLockMethods = ThirdParty.Methods.WithFullNameWildcardMatch(
|
||
"System.Threading.ReaderWriterLock.AcquireReaderLock(*")
|
||
|
||
from m in Application.Methods.UsingAny(acquireLockMethods) where
|
||
!m.IsUsing ("System.Threading.ReaderWriterLock.ReleaseReaderLock()".AllowNoMatch()) &&
|
||
!m.IsUsing ("System.Threading.ReaderWriterLock.ReleaseLock()".AllowNoMatch())
|
||
select new { m, acquireLockMethods = m.MethodsCalled.Intersect(acquireLockMethods) }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="True"><![CDATA[// <Name>Don't tag instance fields with ThreadStaticAttribute</Name>
|
||
// The ThreadStaticAttribute is designed to only works when it tags static fields.
|
||
warnif count > 0
|
||
from f in Application.Fields
|
||
where !f.IsStatic &&
|
||
f.HasAttribute ("System.ThreadStaticAttribute".AllowNoMatch())
|
||
select new { f, f.SizeOfInst }]]></Query>
|
||
</Group>
|
||
<Group Name="System.Xml" Active="True" ShownInReport="False">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Method should not return concrete XmlNode</Name>
|
||
warnif count > 0
|
||
|
||
let concreteXmlTypes = ThirdParty.Types.WithFullNameIn(
|
||
"System.Xml.XmlDocument",
|
||
"System.Xml.XmlAttribute",
|
||
"System.Xml.XmlDocumentFragment",
|
||
"System.Xml.XmlEntity",
|
||
"System.Xml.XmlLinkedNode",
|
||
"System.Xml.XmlNotation",
|
||
"System.Xml.XmlNode")
|
||
|
||
from m in Application.Methods.WithReturnTypeIn(concreteXmlTypes)
|
||
select new { m, m.ReturnType }
|
||
|
||
// The class System.Xml.XmlNode implements the interface
|
||
// System.Xml.Xpath.IXPathNavigable. It is preferrable
|
||
// to return this interface instead of a concrete class.
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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
|
||
|
||
// Do not create a subclass of XmlDocument if you want
|
||
// to create an XML view of an underlying object
|
||
// model or data source.]]></Query>
|
||
</Group>
|
||
<Group Name="System.Globalization" Active="True" ShownInReport="False">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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 == "Parse" ||
|
||
m.SimpleName == "ToString") &&
|
||
!m.Name.Contains("IFormatProvider")
|
||
select new { m, m.MethodsCallingMe }]]></Query>
|
||
</Group>
|
||
<Group Name="Microsoft.Contracts" Active="True" ShownInReport="False">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Public methods returning a reference needs a contract to ensure that a non-null reference is returned</Name>
|
||
|
||
// Contracts are useful to decrease ambiguity between callers and callees.
|
||
// Not ensuring that a reference returned is not-null leaves ambiguity for the caller.
|
||
// Contract.Ensures(Contract.Result<***ReturnType***>() != null, "returned object is not null");
|
||
|
||
// We advise to use the {TryXXX} pattern exposed in the System.Int32.TryParse() method for example
|
||
// if you need to express that a method might return a null reference.
|
||
|
||
warnif count > 0
|
||
from a in Application.Assemblies where a.IsUsing ("Microsoft.Contracts".AllowNoMatch().MatchAssembly())
|
||
let ensureMethod = a.ChildMethods.WithFullName("System.Diagnostics.Contracts.__ContractsRuntime.Ensures(Boolean,String,String)").SingleOrDefault()
|
||
where ensureMethod != null
|
||
|
||
from m in a.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 )
|
||
|
||
select new { m, ReturnTypeReference = m.ReturnType }]]></Query>
|
||
</Group>
|
||
<Group Name="Third-Party Code Elements Used" Active="True" ShownInReport="False">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Third-Party Assemblies Used</Name>
|
||
ThirdParty.Assemblies.Select(a => a)]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Third-Party Namespaces Used</Name>
|
||
ThirdParty.Namespaces.Select(n => n)]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Third-Party Types Used</Name>
|
||
ThirdParty.Types.Select(t => t)]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Third-Party Methods Used</Name>
|
||
ThirdParty.Methods.Select(m => m)]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Third-Party Fields Used</Name>
|
||
ThirdParty.Fields.Select(f => f)]]></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="True" 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="True" 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="True" 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="True" 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="True" 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/Doc_CI_Inputs.aspx
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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="True" 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="True" 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="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Assemblies" Unit="Assemblies" />
|
||
Application.Assemblies.Count()
|
||
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Namespaces" Unit="Namespaces" />
|
||
Application.Namespaces.Count()]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Types" Unit="Types" />
|
||
Application.Types.Count()]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Public Types" Unit="Types" />
|
||
Application.Types.Where(t => t.IsPubliclyVisible).Count()]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Abstract Classes" Unit="Types" />
|
||
Application.Types.Count(t => t.IsClass && t.IsAbstract)
|
||
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Interfaces" Unit="Types" />
|
||
Application.Types.Count(t => t.IsInterface)]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Structures" Unit="Types" />
|
||
Application.Types.Count(t => t.IsStructure)]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Methods" Unit="Methods" />
|
||
Application.Methods.Count(m => !m.IsGeneratedByCompiler)]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Abstract Methods" Unit="Methods" />
|
||
Application.Methods.Count(m => m.IsAbstract)]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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="True" 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="True" 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="True" 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="True" 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="True" 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="True" 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="True" 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="True" 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="True" 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="True" 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="True" 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="True" 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="True" 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="True" 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="True" 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="True" 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="True" 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="True" 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="True" 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="True" 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="True" 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="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Max C.R.A.P Score" />
|
||
// Change Risk Analyzer and Predictor (i.e. CRAP) code metric
|
||
// This code metric helps in pinpointing overly complex and untested code.
|
||
// Reference: http://www.artima.com/weblogs/viewpost.jsp?thread=215899
|
||
// Formula: CRAP(m) = comp(m)^2 * (1 - cov(m)/100)^3 + comp(m)
|
||
|
||
(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)
|
||
|
||
// 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
|
||
]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="Average C.R.A.P Score" />
|
||
// Change Risk Analyzer and Predictor (i.e. CRAP) code metric
|
||
// This code metric helps in pinpointing overly complex and untested code.
|
||
// Reference: http://www.artima.com/weblogs/viewpost.jsp?thread=215899
|
||
// Formula: CRAP(m) = comp(m)^2 * (1 - cov(m)/100)^3 + comp(m)
|
||
|
||
(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)
|
||
|
||
// 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
|
||
]]></Query>
|
||
</Group>
|
||
<Group Name="Third-Party Usage" Active="True" ShownInReport="False">
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Third-Party Assemblies Used" Unit="Assemblies" />
|
||
ThirdParty.Assemblies.Count()]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Third-Party Namespaces Used" Unit="Namespaces" />
|
||
ThirdParty.Namespaces.Count()]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Third-Party Types Used" Unit="Types" />
|
||
ThirdParty.Types.Count()]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <TrendMetric Name="# Third-Party Methods Used" Unit="Methods" />
|
||
ThirdParty.Methods.Count()]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" 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="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Discard generated Assemblies from JustMyCode</Name>
|
||
// --- Make sure to make this query richer to discard generated assemblies from NDepend rules results---
|
||
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 new { a, a.NbILInstructions }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Discard generated Types from JustMyCode</Name>
|
||
// --- Make sure to make this query richer to discard generated types from NDepend rules results ---
|
||
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 generated type ContractException
|
||
t.Name == "__ContractsRuntime+ContractException" ||
|
||
t.FullName == "System.Diagnostics.Contracts.RuntimeContractsAttribute" ||
|
||
t.FullName == "System.Diagnostics.Contracts.__ContractsRuntime" ||
|
||
|
||
// 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 new { t, t.NbLinesOfCode }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Discard generated and designer Methods from JustMyCode</Name>
|
||
// --- Make sure to make this query richer to discard generated methods from NDepend rules results ---
|
||
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 }]]></Query>
|
||
<Query Active="True" DisplayList="True" DisplayStat="True" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Discard generated Fields from JustMyCode</Name>
|
||
// --- Make sure to make this query richer to discard generated fields from NDepend rules results ---
|
||
notmycode
|
||
from f in Application.Fields where
|
||
f.HasAttribute ("System.CodeDom.Compiler.GeneratedCodeAttribute".AllowNoMatch()) ||
|
||
|
||
// Eliminate "components" generated in Windows Form Conrol context
|
||
f.Name == "components" && f.ParentType.DeriveFrom("System.Windows.Forms.Control".AllowNoMatch())
|
||
select f]]></Query>
|
||
</Group>
|
||
<Group Name="Statistics" Active="False" 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 orderby t.Rank descending
|
||
select new { t, t.Rank }).Take(50)
|
||
|
||
// 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.
|
||
// See the definition of the TypeRank metric here:
|
||
// http://www.ndepend.com/Metrics.aspx#TypeRank]]></Query>
|
||
<Query Active="True" DisplayList="False" DisplayStat="False" DisplaySelectionView="False" IsCriticalRule="False"><![CDATA[// <Name>Most used methods (Rank)</Name>
|
||
(from m in Application.Methods orderby m.Rank descending
|
||
select new { m, m.Rank }).Take(50)
|
||
|
||
// 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. See the definition of
|
||
// the MethodRank metric here:
|
||
// http://www.ndepend.com/Metrics.aspx#MethodRank]]></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(50)]]></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
|
||
select new { t, t.TypesUsingMe }).Take(50)]]></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 descending
|
||
select new { m, m.MethodsCallingMe }).Take(50)]]></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(50)]]></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 }).Take(50)]]></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 }).Take(50)]]></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 }
|
||
|
||
// Classify assemblies by their Level values.
|
||
// See the definition of the AssemblyLevel metric here:
|
||
// http://www.ndepend.com/Metrics.aspx#Level]]></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 }
|
||
|
||
// Classify namespaces by their Level values.
|
||
// See the definition of the NamespaceLevel metric here:
|
||
// http://www.ndepend.com/Metrics.aspx#Level]]></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 }
|
||
|
||
// Classify types by their Level values.
|
||
// See the definition of the TypeLevel metric here:
|
||
// http://www.ndepend.com/Metrics.aspx#Level]]></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 }
|
||
|
||
// Classify methods by their Level values.
|
||
// See the definition of the MethodLevel metric here:
|
||
// http://www.ndepend.com/Metrics.aspx#Level]]></Query>
|
||
</Group>
|
||
<Group Name="Samples of Custom rules" Active="False" ShownInReport="False">
|
||
<Query Active="False" DisplayList="True" DisplayStat="True" 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
|
||
]]></Query>
|
||
<Query Active="False" DisplayList="True" DisplayStat="True" 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().MatchAssembly()) &&
|
||
(n.Name == @"N1.N2")
|
||
select n
|
||
]]></Query>
|
||
<Query Active="False" DisplayList="True" DisplayStat="True" 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 }
|
||
]]></Query>
|
||
<Query Active="False" DisplayList="True" DisplayStat="True" 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 }
|
||
]]></Query>
|
||
<Query Active="False" DisplayList="True" DisplayStat="True" 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
|
||
]]></Query>
|
||
<Query Active="False" DisplayList="True" DisplayStat="True" 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
|
||
]]></Query>
|
||
<Query Active="False" DisplayList="True" DisplayStat="True" 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
|
||
]]></Query>
|
||
<Query Active="False" DisplayList="True" DisplayStat="True" 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
|
||
]]></Query>
|
||
<Query Active="False" DisplayList="True" DisplayStat="True" 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]]></Query>
|
||
<Query Active="False" DisplayList="True" DisplayStat="True" 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
|
||
]]></Query>
|
||
<Group Name="Check Coverage on particular Code Elements" Active="False" ShownInReport="False">
|
||
<Query Active="False" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
|
||
// To run this rule properly coverage data
|
||
// must be gathered from NCover™ or Visual Studio™ Coverage.
|
||
// This can be done throught the menu:
|
||
// NDepend -> Coverage -> Import Coverage Files
|
||
// This can be done at analysis time throught the menu:
|
||
// Project Properties -> Analysis -> Code Coverage
|
||
// More information on how to import coverage data here:
|
||
// http://www.ndepend.com/Coverage.aspx
|
||
|
||
]]></Query>
|
||
<Query Active="False" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
|
||
// To run this rule properly coverage data
|
||
// must be gathered from NCover™ or Visual Studio™ Coverage.
|
||
// This can be done throught the menu:
|
||
// NDepend -> Coverage -> Import Coverage Files
|
||
// This can be done at analysis time throught the menu:
|
||
// Project Properties -> Analysis -> Code Coverage
|
||
// More information on how to import coverage data here:
|
||
// http://www.ndepend.com/Coverage.aspx
|
||
|
||
]]></Query>
|
||
<Query Active="False" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
|
||
// To run this rule properly coverage data
|
||
// must be gathered from NCover™ or Visual Studio™ Coverage.
|
||
// This can be done throught the menu:
|
||
// NDepend -> Coverage -> Import Coverage Files
|
||
// This can be done at analysis time throught the menu:
|
||
// Project Properties -> Analysis -> Code Coverage
|
||
// More information on how to import coverage data here:
|
||
// http://www.ndepend.com/Coverage.aspx
|
||
|
||
]]></Query>
|
||
<Query Active="False" DisplayList="True" DisplayStat="True" 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 }
|
||
|
||
|
||
// To run this rule properly coverage data
|
||
// must be gathered from NCover™ or Visual Studio™ Coverage.
|
||
// This can be done throught the menu:
|
||
// NDepend -> Coverage -> Import Coverage Files
|
||
// This can be done at analysis time throught the menu:
|
||
// Project Properties -> Analysis -> Code Coverage
|
||
// More information on how to import coverage data here:
|
||
// http://www.ndepend.com/Coverage.aspx
|
||
|
||
]]></Query>
|
||
</Group>
|
||
<Group Name="Custom Naming Conventions" Active="False" ShownInReport="False">
|
||
<Query Active="False" DisplayList="True" DisplayStat="True" 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 }
|
||
]]></Query>
|
||
<Query Active="False" DisplayList="True" DisplayStat="True" 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 } ]]></Query>
|
||
</Group>
|
||
</Group>
|
||
</Queries>
|
||
<WarnFilter />
|
||
</NDepend> |