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 } ]]>