Types that could have a lower visibility

Rule ND1801 Visibility

Warns about types declarable with a lower visibility (private < internal < public) without compilation error. Narrowing visibility promotes encapsulation by minimizing consumption scope.

Publicly visible types are not matched (the rule cannot know if public was intentional); matched public types are nested in non-public types.

Types tagged CannotDecreaseVisibilityAttribute or IsNotDeadCodeAttribute (from NDepend.API.dll, or declared in your own code) are not matched.

How to fix

Declare each matched type with the optimal visibility shown in the CouldBeDeclared column.

If you publish an API, many matched public types should stay public: either add && !m.IsPubliclyVisible to the rule (coarse), or tag each type with CannotDecreaseVisibilityAttribute (finer).

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>Types that could have a lower visibility</Name>
// <Id>ND1801:TypesThatCouldHaveALowerVisibility</Id>

warnif count > 0 from t in JustMyCode.Types where 

  t.Visibility != t.OptimalVisibility &&

  // Don't match potentially dead types, candidate for removal
   t.TypesUsingMe.Any() &&

  // 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()) &&
  !t.HasAttribute("JetBrains.Annotations.UsedImplicitlyAttribute".AllowNoMatch()) &&
  
  // JSON, XML and binary serialized type must remain public.
  !t.IsUsing("Newtonsoft.Json".MatchNamespace().AllowNoMatch()) &&
  !t.IsUsing("System.Text.Json".MatchNamespace().AllowNoMatch()) &&
  !t.HasAttribute("System.Xml.Serialization.XmlRootAttribute".AllowNoMatch()) &&
  !t.HasAttribute("System.SerializableAttribute".AllowNoMatch()) &&

  // Eliminate public types visible outside of their assembly
  // because the rule cannot know if the developer left the type public 
  // intentionally or not.
  !t.IsPubliclyVisible && 

  // 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,

   Debt = 30.ToSeconds().ToDebt(), // It is fast to change the method visibility
   Severity = Severity.Medium
}

//<Expl>
// In the context of analyzed code, the *{0}* can be declared as *{2}*.
// Doing so **improves encapsulation** by constraining this type's accessibility 
// to the smallest possible scope.
//</Expl>

//<Desc>
// Warns about types declarable with a lower visibility (*private* <
// *internal* < *public*) without compilation error. **Narrowing visibility**
// **promotes encapsulation** by minimizing consumption scope.
//
// Publicly visible types are not matched (the rule cannot know if public
// was intentional); matched public types are nested in non-public types.
//
// Types tagged *CannotDecreaseVisibilityAttribute* or
// *IsNotDeadCodeAttribute* (from *NDepend.API.dll*, or declared in your own
// code) are not matched.
//</Desc>

//<HowToFix>
// Declare each matched type with the *optimal visibility* shown in the
// *CouldBeDeclared* column.
//
// If you publish an API, many matched public types should stay public:
// either add *&& !m.IsPubliclyVisible* to the rule (coarse), or tag each
// type with *CannotDecreaseVisibilityAttribute* (finer).
//</HowToFix>

Visibility: all 12 items