Potentially Dead Fields

Rule ND1702 Dead Code

Lists potentially dead fields: removable because never used by the program.

Public fields are not matched by default (client code not analyzed by NDepend might use them); easily changed — rules in the Visibility group warn about public fields. Enumeration values and literal constants are also not matched: their usage is lost in the compiled IL code NDepend analyzes.

Usages from test code (the TestCode view) keep a field alive: a field used only from tests is not matched here but by the rule Code used only by tests, disabled by default.

How to fix

Static analysis cannot list dead fields exactly: fields can be read or assigned dynamically (like through reflection). Investigate each match; if truly unused, remove it to avoid maintaining a useless code element.

If a used field is still matched, tag it with IsNotDeadCodeAttribute from NDepend.API.dll (or your own attribute, adapting the rule).

Debt is 10 minutes (quick investigation); Annual Interest is 8 minutes per unused field matched.

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>Potentially Dead Fields</Name>
// <Id>ND1702:PotentiallyDeadFields</Id>

warnif count > 0
from f in JustMyCode.Fields where
   // Usages from test code count: a field used only from tests is not dead, see the rule 'Code used only by tests'
   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.HasAttribute("JetBrains.Annotations.UsedImplicitlyAttribute".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.
   
   // Don't warn for fields tagged with these Unity specific attribute
   !f.HasAttribute("UnityEngine.SerializeField".AllowNoMatch()) &&
   !f.HasAttribute("UnityEngine.SerializeReference".AllowNoMatch()) &&
   
   // Don't warn about InlineArrayAttribute's field
   !f.ParentType.HasAttribute("System.Runtime.CompilerServices.InlineArrayAttribute".AllowNoMatch())

select new {
   f,
   Debt = 10.ToMinutes().ToDebt(),
   AnnualInterest = 8.ToMinutes().ToAnnualInterest()
}

//<Expl>
// The *{0}* is not read not assigned within the context of analyzed code and may be a candidate for removal.
//</Expl>

//<Desc>
// Lists *potentially* **dead fields**: removable because never used by the
// program.
//
// *Public* fields are not matched by default (client code not analyzed by
// NDepend might use them); easily changed — rules in the *Visibility* group
// warn about public fields. Enumeration values and literal constants are
// also not matched: their usage is lost in the compiled IL code NDepend
// analyzes.
//
// Usages from **test code** (the *TestCode* view) keep a field alive: a
// field used only from tests is not matched here but by the rule *Code used
// only by tests*, disabled by default.
//</Desc>

//<HowToFix>
// *Static analysis* cannot list dead fields *exactly*: fields can be read or
// assigned *dynamically* (like through reflection). Investigate each match;
// if truly unused, remove it to avoid maintaining a useless code element.
//
// If a used field is still matched, tag it with **IsNotDeadCodeAttribute**
// from *NDepend.API.dll* (or your own attribute, adapting the rule).
//
// **Debt** is 10 minutes (quick investigation); **Annual Interest** is 8
// minutes per unused field matched.
//</HowToFix>

Dead Code: all 5 items