Avoid making complex methods even more complex

Rule ND1104 Code Smells Regression

Runs only in diff mode (baseline defined). Warns when an already complex method (Cyclomatic Complexity > 6) became even more complex since baseline. Metric: https://www.ndepend.com/docs/code-metrics#CC

Requires PDB files and source code at analysis time. See: https://www.ndepend.com/docs/ndepend-analysis-inputs-explanation

To visualize changes, right-click a matched method and select:

- Compare older and newer versions of source file

- or Compare older and newer versions decompiled with Reflector

How to fix

Split into smaller methods or even new classes. Question each local variable's scope: it may become an instance field of the new class(es). Large switch…case can become types implementing a common interface, polymorphism replacing the switch cases tests. Write tests for each method before extracting it.

Estimated Debt: linear from 15 to 60 minutes with the extra complexity. Severity is High: fix these issues now, before such code gets released in production.

CQLinq source

This rule is written in CQLinq, C# LINQ over the NDepend code model. It ships with every new NDepend project: you can read it, adapt it, or use it as a starting point for your own custom rules.

// <Name>Avoid making complex methods even more complex</Name>
// <Id>ND1104:AvoidMakingComplexMethodsEvenMoreComplex</Id>
warnif count > 0 

let complexityScoreProc = new Func<IMethod, double>(m => 
    (m.CyclomaticComplexity + m.ILCyclomaticComplexity/3 + 2*m.ILNestingDepth).Value)

from m in JustMyCode.Methods where

  // Don't match async methods here to avoid 
  // false positives because of special compiler tricks.
 !m.IsAsync &&

 !m.IsAbstract &&
  m.IsPresentInBothBuilds() &&
  m.CodeWasChanged() &&
  m.OlderVersion().CyclomaticComplexity > 6 

let complexityScore = complexityScoreProc(m)
let oldComplexityScore = complexityScoreProc(m.OlderVersion())
where complexityScore > oldComplexityScore 

let complexityScoreDiff = complexityScoreProc(m) - complexityScoreProc(m.OlderVersion())
orderby complexityScoreDiff descending

select new { 
   m,
   oldComplexityScore ,
   complexityScore ,
   diff= complexityScoreDiff,

   Debt = complexityScoreDiff.Linear(1, 15,   50, 60).ToMinutes().ToDebt(),

   // The annual interest varies linearly from interest for severity Medium for a tiny complexity increment
   // to interest for severity critical for 2000 loc
   AnnualInterest = complexityScoreDiff.Linear(1,     Severity.High.AnnualInterestThreshold().Value.TotalMinutes, 
                                               50, 4*(Severity.High.AnnualInterestThreshold().Value.TotalMinutes)).ToMinutes().ToAnnualInterest()
   
}

//<Expl>
// The *{0}* was already too complex in the baseline
// and is now even more complex.
// Consider splitting it into smaller, less complex methods.
//</Expl>

//<Desc>
// Runs only in *diff mode* (baseline defined). Warns when an already complex
// method (*Cyclomatic Complexity* > 6) became even more complex since
// baseline. Metric: https://www.ndepend.com/docs/code-metrics#CC
//
// Requires PDB files and source code at analysis time. See:
// https://www.ndepend.com/docs/ndepend-analysis-inputs-explanation
//
// To visualize changes, right-click a matched method and select:
//
// - Compare older and newer versions of source file
//
// - or Compare older and newer versions decompiled with Reflector
//</Desc>

//<HowToFix>
// Split into smaller methods or even new classes. Question each local
// variable's scope: it may become an instance field of the new class(es).
// Large *switch…case* can become types implementing a common interface,
// polymorphism replacing the *switch cases tests*. Write tests for each
// method before extracting it.
//
// Estimated Debt: linear from 15 to 60 minutes with the extra complexity.
// Severity is **High**: fix these issues **now**, before such code gets
// released in production.
//</HowToFix>

Code Smells Regression: all 9 items