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