diff --git a/.gitignore b/.gitignore index 3499559..60b7e8d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,4 +5,6 @@ bin/ obj/ *.mdb .git/ - +NDependOut/ +.nuget/ +.nuget diff --git a/NOCQ.ndproj b/NOCQ.ndproj new file mode 100644 index 0000000..e22296d --- /dev/null +++ b/NOCQ.ndproj @@ -0,0 +1,4069 @@ + + + D:\varanas\NDependOut + + NOCQ + NOCQ.Application + + + mscorlib + System.ComponentModel.Composition + System + System.Core + Newtonsoft.Json + Microsoft.CSharp + AE.Net.Mail + csredis + + + D:\varanas\src\NOCQ\bin\Debug + D:\varanas\src\NOCQ.Application\bin\Debug + C:\Windows\Microsoft.NET\Framework\v4.0.30319 + C:\Windows\Microsoft.NET\Framework\v4.0.30319\WPF + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Types too big - critical +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 + +]]> + Methods too complex - critical +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]]> + Methods with too many parameters - critical +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 +]]> + Quick summary of methods to refactor +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 } ]]> + Methods too big +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]]> + Methods too complex +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]]> + Methods potentially poorly commented +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 ]]> + Methods with too many parameters +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]]> + Methods with too many local variables +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]]> + Methods with too many overloads +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]]> + Types with too many methods +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. +]]> + Types with too many fields +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.]]> + Types with poor cohesion +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]]> + + + From now, all methods added or refactored should respect basic quality principles +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 +]]> + From now, all types added or refactored should respect basic quality principles +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 +]]> + From now, all types added or refactored should be 100% covered by tests +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.]]> + Avoid decreasing code coverage by tests of types +// 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, +} +]]> + Types that used to be 100% covered but not anymore +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 }]]> + Avoid making complex methods even more complex (Source CC) +// 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, +} +]]> + Avoid making complex methods even more complex (IL CC) +// 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, +} + +]]> + Avoid making large methods even larger +// 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, +} + +]]> + Avoid adding methods to a type that already had many methods +// 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 }]]> + Avoid transforming an immutable type into a mutable one +// 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 }]]> + Avoid transforming an immutable field into a mutable one +// 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]]> + Avoid adding instance fields to a type that already had many instance fields +// 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 }]]> + + + Base class should not use derivatives +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 }]]> + Class shouldn't be too deep in inheritance tree +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 +]]> + Class with no descendant should be sealed if possible +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 }]]> + Overrides of Method() should call base.Method() +// 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 } +]]> + Do not hide base class methods +// 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 }]]> + A stateless class or structure might be turned into a static type +// 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 + +]]> + Non-static classes should be instantiated or turned to static +// 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 }]]> + Methods should be declared static if possible +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 }]]> + Constructor should not call a virtual methods + +// 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 }]]> + Avoid the Singleton pattern +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/ +]]> + Don't assign static fields from instance methods +// 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 }]]> + Avoid empty interfaces +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.]]> + Avoid types initialization cycles +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 +}]]> + + + Avoid custom delegates + +// Prefer using one of the standard generic delegate type in +// Predicate Func Action +// 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 }]]> + Types with disposable instance fields must be disposable +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 }]]> + Disposable types with unmanaged resources should declare finalizer +// 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 }]]> + Classes that are candidate to be turned into structures +// +// 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. + ]]> + Avoid namespaces with few types +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]]> + Nested types should not be visible +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.]]> + Declare types in namespaces +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.]]> + Empty static constructor can be discarded +warnif count > 0 from m in Application.Methods where + m.IsClassConstructor && + m.NbLinesOfCode == 0 +select m + ]]> + Instances size shouldn't be too big +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]]> + Boxing/unboxing should be avoided +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.]]> + Attribute classes should be sealed +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.]]> + Don't use obsolete types, methods or fields +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 }]]> + Don't forget to implement methods that throw NotImplementedException +warnif count > 0 +from m in Application.Methods +where m.CreateA("System.NotImplementedException".AllowNoMatch()) +select m]]> + Equals() should be overridden by types implementing the '==' operator +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 +]]> + + + Avoid namespaces mutually dependent +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() + +// 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 } +]]> + Avoid namespaces dependency cycles +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() + + +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 } +]]> + Avoid partitioning the code base through many small library Assemblies +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 ]]> + UI layer shouldn't use directly DB types +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 }]]> + UI layer shouldn't use directly DAL layer +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 +}]]> + Assemblies with poor cohesion (RelationalCohesion) +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]]> + Assemblies that don't satisfy the Abstractness/Instability principle +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]]> + Example of custom rule to check for dependency + +// 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) +]]> + Higher cohesion - lower coupling +// 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 }]]> + + + API Breaking Changes: Types +// 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()) } +]]> + API Breaking Changes: Methods +// 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()) }]]> + API Breaking Changes: Fields +// 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()) }]]> + API Breaking Changes: Interfaces and Abstract Classes +// 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 }]]> + Broken serializable types +// 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.]]> + Avoid transforming immutable types into mutable types + +// 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 } + +]]> + Avoid changing enumerations Flags status + +// 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 } +]]> + API: New publicly visible types +// 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()) }]]> + API: New publicly visible methods +// 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()) }]]> + API: New publicly visible fields +// 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()) }]]> + + + New assemblies +from a in Application.Assemblies where a.WasAdded() +select new { a, a.NbLinesOfCode } + +]]> + Assemblies removed +from a in codeBase.OlderVersion().Application.Assemblies where a.WasRemoved() +select new { a, a.NbLinesOfCode }]]> + Assemblies where code was changed +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() }]]> + New namespaces +from n in Application.Namespaces where + !n.ParentAssembly.WasAdded() && + n.WasAdded() +select new { n, n.NbLinesOfCode } +]]> + Namespaces removed +from n in codeBase.OlderVersion().Application.Namespaces where + !n.ParentAssembly.WasRemoved() && + n.WasRemoved() +select new { n, n.NbLinesOfCode } +]]> + Namespaces where code was changed +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() }]]> + New types +from t in Application.Types where + !t.ParentNamespace.WasAdded() && + t.WasAdded() +select new { t, t.NbLinesOfCode } +]]> + Types removed +from t in codeBase.OlderVersion().Application.Types where + !t.ParentNamespace.WasRemoved() && + t.WasRemoved() +select new { t, t.NbLinesOfCode } +]]> + Types where code was changed +// 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 }*/]]> + Heuristic to find types moved from one namespace or assembly to another +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]]> + Types directly using one or several types changed +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 }]]> + Types indirectly using one or several types changed +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 }]]> + New methods +from m in Application.Methods where + !m.ParentType.WasAdded() && + m.WasAdded() +select new { m, m.NbLinesOfCode } +]]> + Methods removed +from m in codeBase.OlderVersion().Application.Methods where + !m.ParentType.WasRemoved() && + m.WasRemoved() +select new { m, m.NbLinesOfCode } +]]> + Methods where code was changed +// 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 }]]> + Methods directly calling one or several methods changed +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 }]]> + Methods indirectly calling one or several methods changed +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 }]]> + New fields +from f in Application.Fields where + !f.ParentType.WasAdded() && + f.WasAdded() +select new { f } +]]> + Fields removed +from f in codeBase.OlderVersion().Application.Fields where + !f.ParentType.WasRemoved() && + f.WasRemoved() +select new { f } +]]> + Third party types that were not used and that are now used +from t in ThirdParty.Types where t.IsUsedRecently() +select new { t, t.Methods, t.Fields, t.TypesUsingMe } +]]> + Third party types that were used and that are not used anymore +from t in codeBase.OlderVersion().Types where t.IsNotUsedAnymore() +select new { t, t.Methods, t.Fields, TypesThatUsedMe = t.TypesUsingMe } +]]> + Third party methods that were not used and that are now used +from m in ThirdParty.Methods where + m.IsUsedRecently() && + !m.ParentType.IsUsedRecently() +select new { m, m.MethodsCallingMe }]]> + Third party methods that were used and that are not used anymore +from m in codeBase.OlderVersion().Methods where + m.IsNotUsedAnymore() && + !m.ParentType.IsNotUsedAnymore() +select new { m, MethodsThatCalledMe = m.MethodsCallingMe}]]> + Third party fields that were not used and that are now used +from f in ThirdParty.Fields where + f.IsUsedRecently() && + !f.ParentType.IsUsedRecently() +select new { f, f.MethodsUsingMe }]]> + Third party fields that were used and that are not used anymore +from f in codeBase.OlderVersion().Fields where + f.IsNotUsedAnymore() && + !f.ParentType.IsNotUsedAnymore() +select new { f, MethodsThatUsedMe = f.MethodsUsingMe }]]> + + + C.R.A.P method code metric +// 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 }]]> + Complex methods partially covered by tests should be 100% covered +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 } ]]> + Method changed poorly covered +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 } +]]> + Method added poorly covered +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 } +]]> + Types 95% to 99% covered +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. +]]> + Namespaces 95% to 99% covered +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.]]> + Types tagged with FullCoveredAttribute should be 100% covered +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. +]]> + Types 100% covered should be tagged with FullCoveredAttribute +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. +]]> + Types not covered at all +from t in Application.Types where + t.PercentageCoverage == 0 + orderby t.NbLinesOfCode descending +select new { t, t.NbLinesOfCode } +]]> + Namespaces not covered at all +from n in Application.Namespaces where + n.PercentageCoverage == 0 + orderby n.NbLinesOfCode descending +select new { n, n.NbLinesOfCode} +]]> + Test Methods + +// 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 +]]> + Methods directly called by test Methods + +// 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 +}]]> + Methods directly and indirectly called by test Methods + +// 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 +}]]> + + + Potentially dead Types +warnif count > 0 +// Filter procedure for types that should'nt be considered as dead +let canTypeBeConsideredAsDeadProc = new Func( + 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] }]]> + Potentially dead Methods +warnif count > 0 +// Filter procedure for methods that should'nt be considered as dead +let canMethodBeConsideredAsDeadProc = new Func( + 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] }]]> + Potentially dead Fields +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]]> + Wrong usage of IsNotDeadCodeAttribute + +// 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 }]]> + + + Methods that could have a lower visibility +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 }]]> + Types that could have a lower visibility +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 } +]]> + Fields that could have a lower visibility +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 }]]> + Types that could be declared as private, nested in a parent type +// 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 }]]> + Avoid publicly visible constant fields + +// 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]]> + Fields should be declared as private +// 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 }]]> + Constructors of abstract classes should be declared as protected or private +// 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 } +]]> + Avoid public methods not publicly visible +// 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]]> + Methods that should be declared as 'public' in C#, 'Public' in VB.NET +// 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 } +]]> + Wrong usage of CannotDecreaseVisibilityAttribute + +// 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 }]]> + Event handler methods should be declared private +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".]]> + + + Fields should be marked as ReadOnly when possible +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). + + + +]]> + Structures should be immutable +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 +]]> + Property Getters should be immutable +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. + ]]> + Avoid static fields with a mutable field type +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. +]]> + A field must not be assigned from outside its parent hierarchy types +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 }]]> + Don't assign a field from many methods +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). + +]]> + Do not declare read only mutable reference types +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. +]]> + Array fields should not be read only +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 +// System.Collections.Generic.IReadOnlyCollection +// 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. +]]> + Types immutable should be tagged with ImmutableAttribute +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. +]]> + Methods tagged with PureAttribute must be pure +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. + +]]> + Pure methods should be tagged with PureAttribute +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.]]> + + + Instance fields should be prefixed with a 'm_' +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.]]> + Static fields should be prefixed with a 's_' +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. +]]> + Interface name should begin with a 'I' +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 +]]> + Abstract base class should be suffixed with 'Base' +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 }]]> + Exception class name should be suffixed with 'Exception' +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'.]]> + Attribute class name should be suffixed with 'Attribute' +warnif count > 0 from t in Application.Types where + t.IsAttributeClass && + !t.NameLike (@"Attribute$") +select new { t, t.NbLinesOfCode } +]]> + Types name should begin with an Upper character +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 }]]> + Methods name should begin with an Upper character +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.]]> + Do not name enum values 'Reserved' +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.]]> + Avoid types with name too long +warnif count > 0 from t in Application.Types +where !t.IsGeneratedByCompiler + +where t.SimpleName.Length > 35 +select new { t, t.SimpleName } + + ]]> + Avoid methods with name too long +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. + ]]> + Avoid fields with name too long +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. + ]]> + Avoid having different types with same 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 + }]]> + Avoid prefixing type name with parent namespace 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 }]]> + Avoid naming types and namespaces with the same identifier + +// 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) }]]> + Don't call your method Dispose +// 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]]> + Methods prefixed with 'Try' should return a boolean +// 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 }]]> + + + Avoid referencing source file out of Visual Studio project directory + +// 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() }]]> + Avoid duplicating a type definition across assemblies +// 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() + }]]> + Avoid defining multiple types in a source file +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, + sourceFile.FilePathString }]]> + Namespace name should correspond to file location + +// 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 } ]]> + Types with source files stored in the same directory, should be declared in the same namespace +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 } + +]]> + Types declared in the same namespace, should have their source files stored in the same directory +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() }]]> + + + + Mark ISerializable types with SerializableAttribute +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.]]> + Mark assemblies with assembly version +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.]]> + Mark assemblies with CLSCompliant +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.]]> + Mark assemblies with ComVisible +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.]]> + Mark attributes with AttributeUsageAttribute +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.]]> + Remove calls to GC.Collect() +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]]> + Don't call GC.Collect() without calling GC.WaitForPendingFinalizers() +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.]]> + Enum Storage should be Int32 +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.]]> + Do not raise too general exception types +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 +]]> + Do not raise reserved exception types +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 }]]> + Use integral or string argument for indexers +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.]]> + Uri fields should be of type System.Uri +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.]]> + Types should not extend System.ApplicationException +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.]]> + + + Collection properties should be read only + +// 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") +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 }]]> + Don't use .NET 1.x HashTable and ArrayList +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.]]> + Caution with List.Contains() +let containsMethods = ThirdParty.Methods.WithFullNameIn( + "System.Collections.Generic.List.Contains(T)", + "System.Collections.Generic.IList.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 class +// where calls to Contains() takes a constant +// time (O(0) operation).]]> + Prefer return collection abstraction instead of implementation +let implTypes = ThirdParty.Types.WithFullNameIn( + "System.Collections.Generic.List", + "System.Collections.Generic.HashSet", + "System.Collections.Generic.Dictionary") + +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, +// ICollection or IEnumerable.]]> + + + P/Invokes should be static and not be visible +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.]]> + Move P/Invokes to NativeMethods class +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. +]]> + NativeMethods class should be static and internal +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. +]]> + + + Method non-synchronized that read mutable states +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.]]> + Don't create threads explicitely +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.]]> + Don't use dangerous threading methods +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) } + +]]> + Monitor TryEnter/Exit must be both called within the same method +// 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) }]]> + ReaderWriterLock Acquire[Reader/Writer]Lock/ReleaseLock must be both called within the same method +// 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) }]]> + Don't tag instance fields with ThreadStaticAttribute +// 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 }]]> + + + Method should not return concrete XmlNode +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. +]]> + Types should not extend System.Xml.XmlDocument +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.]]> + + + Float and Date Parsing must be culture aware +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 }]]> + + + Public methods returning a reference needs a contract to ensure that a non-null reference is returned + +// 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 }]]> + + + Third-Party Assemblies Used +ThirdParty.Assemblies.Select(a => a)]]> + Third-Party Namespaces Used +ThirdParty.Namespaces.Select(n => n)]]> + Third-Party Types Used +ThirdParty.Types.Select(t => t)]]> + Third-Party Methods Used +ThirdParty.Methods.Select(m => m)]]> + Third-Party Fields Used +ThirdParty.Fields.Select(f => f)]]> + + + + + +Application.Assemblies.Sum(a => a.NbLinesOfCode)]]> + +JustMyCode.Methods.Sum(m => m.NbLinesOfCode) + +// JustMyCode is defined by code queries prefixed with 'notmycode' +// in the group 'Defining JustMyCode'. +]]> + +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'. +]]> + +( 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 +]]> + +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 +]]> + +Application.Assemblies.Sum(a => a.NbILInstructions) +]]> + +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'. +]]> + +Application.Assemblies.Sum(a => a.NbLinesOfComment) + +// So far comments are only extracted from C# source code. +]]> + +Application.Assemblies.Count() + +]]> + +Application.Namespaces.Count()]]> + +Application.Types.Count()]]> + +Application.Types.Where(t => t.IsPubliclyVisible).Count()]]> + +Application.Types.Count(t => t.IsClass && !t.IsGeneratedByCompiler) +]]> + +Application.Types.Count(t => t.IsClass && t.IsAbstract) + +]]> + +Application.Types.Count(t => t.IsInterface)]]> + +Application.Types.Count(t => t.IsStructure)]]> + +Application.Methods.Count(m => !m.IsGeneratedByCompiler)]]> + +Application.Methods.Count(m => m.IsAbstract)]]> + +Application.Methods.Count(m => !m.IsAbstract && !m.IsGeneratedByCompiler) +]]> + +Application.Fields.Count(f => + !f.IsEnumValue && + !f.IsGeneratedByCompiler && + !f.IsLiteral && + !f.ParentType.IsEnumeration) + +]]> + + + +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}) +]]> + +Application.Methods.Where(m => m.NbLinesOfCode > 0) + .Average(m => m.NbLinesOfCode)]]> + +Application.Methods.Where(m => m.NbLinesOfCode >= 3) + .Average(m => m.NbLinesOfCode) +]]> + +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}) + +]]> + +Application.Types.Where(t => t.NbLinesOfCode > 0) + .Average(t => t.NbLinesOfCode)]]> + +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}) +]]> + +Application.Methods.Where(m => m.NbLinesOfCode> 0) + .Average(m => m.CyclomaticComplexity)]]> + +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}) +]]> + +Application.Methods.Where(m => m.NbILInstructions> 0) + .Average(m => m.ILCyclomaticComplexity) +]]> + +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}) +]]> + +Application.Methods.Where(m => m.NbILInstructions> 0) + .Average(m => m.ILNestingDepth) +]]> + +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}) + +]]> + +Application.Types.Average(t => t.NbMethods)]]> + +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})]]> + +JustMyCode.Types.Where(t => t.IsInterface) + .Average(t => t.NbMethods) +]]> + + + +((float)Application.Assemblies.Sum(a => a.NbLinesOfCodeCovered) / + Application.Assemblies.Sum(a => a.NbLinesOfCode) + * 100f) +.ToEnumerable().Sum() +]]> + +Application.Assemblies.Sum(a => a.NbLinesOfCodeCovered)]]> + +Application.Assemblies.Sum(a => a.NbLinesOfCodeNotCovered)]]> + +Application.Types.Where(t => t.PercentageCoverage == 100) + .Sum(t => t.NbLinesOfCodeCovered)]]> + +Application.Methods.Where(m => m.PercentageCoverage == 100) + .Sum(m => m.NbLinesOfCodeCovered)]]> + +// 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 +]]> + +// 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 +]]> + + + +ThirdParty.Assemblies.Count()]]> + +ThirdParty.Namespaces.Count()]]> + +ThirdParty.Types.Count()]]> + +ThirdParty.Methods.Count()]]> + +ThirdParty.Fields.Count()]]> + + + + Discard generated Assemblies from JustMyCode +// --- 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 }]]> + Discard generated Types from JustMyCode +// --- 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 }]]> + Discard generated and designer Methods from JustMyCode +// --- 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 }]]> + Discard generated Fields from JustMyCode +// --- 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]]> + + + Most used types (Rank) +(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]]> + Most used methods (Rank) +(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]]> + Most used namespaces (#NamespacesUsingMe ) +(from n in Namespaces orderby n.NbNamespacesUsingMe descending + select new { n, n.NamespacesUsingMe }).Take(50)]]> + Most used types (#TypesUsingMe ) +(from t in Types orderby t.NbTypesUsingMe descending + select new { t, t.TypesUsingMe }).Take(50)]]> + Most used methods (#MethodsCallingMe ) +(from m in Methods orderby m.NbMethodsCallingMe descending + select new { m, m.MethodsCallingMe }).Take(50)]]> + Namespaces that use many other namespaces (#NamespacesUsed ) +(from n in Application.Namespaces orderby n.NbNamespacesUsed descending + select new { n, n.NamespacesUsed }).Take(50)]]> + Types that use many other types (#TypesUsed ) +(from t in Application.Types orderby t.NbTypesUsed descending + select new { t, t.TypesUsed }).Take(50)]]> + Methods that use many other methods (#MethodsCalled ) +(from m in Application.Methods orderby m.NbMethodsCalled descending + select new { m, m.MethodsCalled }).Take(50)]]> + High-level to low-level assemblies (Level) +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]]> + High-level to low-level namespaces (Level) +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]]> + High-level to low-level types (Level) +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]]> + High-level to low-level methods (Level) +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]]> + + + Check that the assembly Asm1 is not using the assembly Asm2 +warnif count > 0 from a in Application.Assemblies where + a.IsUsing ("Asm2".AllowNoMatch().MatchAssembly()) && + (a.Name == @"Asm1") +select a +]]> + Check that the namespace N1.N2 is not using the namespace N3.N4.N5 +warnif count > 0 from n in Application.Namespaces where + n.IsUsing ("N3.N4.N5".AllowNoMatch().MatchAssembly()) && + (n.Name == @"N1.N2") +select n +]]> + Check that the assembly Asm1 is only using the assemblies Asm2, Asm3 or mscorlib +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 } +]]> + Check that the namespace N1.N2 is only using the namespaces N3.N4, N5 or System +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 } +]]> + Check that AsmDrawing is the only assembly that is using System.Drawing +warnif count> 0 from a in Application.Assemblies where + a.IsUsing ("System.Drawing".AllowNoMatch().MatchAssembly()) && + !(a.Name == @"AsmDrawing") +select a +]]> + Check that only 3 assemblies are using System.Drawing +warnif count != 3 from a in Application.Assemblies where + a.IsUsing ("System.Drawing".AllowNoMatch().MatchAssembly()) +select a +]]> + Check that all methods that call Foo.Fct1() also call Foo.Fct2(Int32) +warnif count > 0 from m in Application.Methods where + m.IsUsing ("Foo.Fct1()".AllowNoMatch()) && + !m.IsUsing ("Foo.Fct2(Int32)".AllowNoMatch()) +select m +]]> + Check that all types that derive from Foo, also implement IFoo +warnif count > 0 from t in Application.Types where + t.DeriveFrom ("Foo".AllowNoMatch().MatchType()) && + !t.Implement ("IFoo".AllowNoMatch().MatchType()) +select t +]]> + Check that all types that has the attribute FooAttribute are declared in the namespace N1.N2* +warnif count > 0 from t in + Application.Namespaces.WithNameWildcardMatchNotIn("N1.N2*").ChildTypes() + where + t.HasAttribute ("FooAttribute".AllowNoMatch()) +select t]]> + Check that all synchronization objects are only used from the namespaces under MyNamespace.Sync +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 +]]> + + Check that the assembly Asm is 100% covered by tests +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 + +]]> + Check that the namespace N1.N2 is 100% covered by tests +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 + +]]> + Check that the class Namespace.Foo is 100% covered by tests +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 + +]]> + Check that the class Namespace.Foo.Method(Int32) is 100% covered by tests +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 + +]]> + + + Check that all types that derive from Foo, has a name that ends up with Foo +warnif count > 0 from t in Application.Types where + t.DeriveFrom ("Foo".AllowNoMatch().MatchType()) && + !t.NameLike (@"Foo$") +select new { t, t.NbLinesOfCode } +]]> + Check that all namespaces begins with CompanyName.ProductName +warnif count > 0 from n in Application.Namespaces where + !n.NameLike (@"^CompanyName.ProductName") +select new { n, n.NbLinesOfCode } ]]> + + + + + \ No newline at end of file diff --git a/NOCQ.sln b/NOCQ.sln index b10310e..38a986d 100644 --- a/NOCQ.sln +++ b/NOCQ.sln @@ -1,10 +1,19 @@  -Microsoft Visual Studio Solution File, Format Version 11.00 -# Visual Studio 2010 +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30501.0 +MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NOCQ", "src\NOCQ\NOCQ.csproj", "{83651B7D-B58F-46B8-BFE2-BCC0A6C92C7A}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NOCQ.Application", "src\NOCQ.Application\NOCQ.Application.csproj", "{DF8CD7EA-76FC-4B57-B24A-52C6373A8EDF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NOCQ.UnitTest", "src\NOCQ.UnitTest\NOCQ.UnitTest.csproj", "{D9435B3A-C005-49C2-9C0D-B87330B006D7}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{37EF7652-8519-4B19-89DB-9C9916F53908}" + ProjectSection(SolutionItems) = preProject + .nuget\packages.config = .nuget\packages.config + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -19,8 +28,18 @@ Global {DF8CD7EA-76FC-4B57-B24A-52C6373A8EDF}.Debug|Any CPU.Build.0 = Debug|Any CPU {DF8CD7EA-76FC-4B57-B24A-52C6373A8EDF}.Release|Any CPU.ActiveCfg = Release|Any CPU {DF8CD7EA-76FC-4B57-B24A-52C6373A8EDF}.Release|Any CPU.Build.0 = Release|Any CPU + {D9435B3A-C005-49C2-9C0D-B87330B006D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D9435B3A-C005-49C2-9C0D-B87330B006D7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D9435B3A-C005-49C2-9C0D-B87330B006D7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D9435B3A-C005-49C2-9C0D-B87330B006D7}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution StartupItem = src\NOCQ.Application\NOCQ.Application.csproj EndGlobalSection + GlobalSection(NDepend) = preSolution + Project = ".\NOCQ.ndproj" + EndGlobalSection EndGlobal diff --git a/NOCQ.v12.suo b/NOCQ.v12.suo new file mode 100644 index 0000000..dbedcda Binary files /dev/null and b/NOCQ.v12.suo differ diff --git a/src/NOCQ.UnitTest/Class1.cs b/src/NOCQ.UnitTest/Class1.cs new file mode 100644 index 0000000..a01af0b --- /dev/null +++ b/src/NOCQ.UnitTest/Class1.cs @@ -0,0 +1,18 @@ +using NUnit.Framework; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NOCQ.UnitTest +{ + [TestFixture] + public class Class1 + { + public static void Success() + { + Assert.Pass(); + } + } +} diff --git a/src/NOCQ.UnitTest/NOCQ.UnitTest.csproj b/src/NOCQ.UnitTest/NOCQ.UnitTest.csproj new file mode 100644 index 0000000..219da77 --- /dev/null +++ b/src/NOCQ.UnitTest/NOCQ.UnitTest.csproj @@ -0,0 +1,59 @@ + + + + + Debug + AnyCPU + {D9435B3A-C005-49C2-9C0D-B87330B006D7} + Library + Properties + NOCQ.UnitTest + NOCQ.UnitTest + v4.5 + 512 + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + ..\..\packages\NUnit.2.6.3\lib\nunit.framework.dll + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/NOCQ.UnitTest/Properties/AssemblyInfo.cs b/src/NOCQ.UnitTest/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..1a40912 --- /dev/null +++ b/src/NOCQ.UnitTest/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("NOCQ.UnitTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("NOCQ.UnitTest")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("1e338b42-4312-4561-9d92-91057b434f5d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/NOCQ.UnitTest/packages.config b/src/NOCQ.UnitTest/packages.config new file mode 100644 index 0000000..d4e241a --- /dev/null +++ b/src/NOCQ.UnitTest/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file