Methods that could have a lower visibility

Rule ND1800 Visibility

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

Publicly visible methods are not matched (the rule cannot know if public was intentional); matched public methods are declared in non-public types. Public methods that could be internal in a non-public type are caught by Avoid public methods not publicly visible instead.

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

How to fix

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

If you publish an API, many matched public methods should stay public: either add && !m.IsPubliclyVisible to the rule (coarse), or tag each method 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>Methods that could have a lower visibility</Name>
// <Id>ND1800:MethodsThatCouldHaveALowerVisibility</Id>

warnif count > 0 from m in JustMyCode.Methods where 
   m.Visibility != m.OptimalVisibility &&

   // Don't match potentially dead methods, candidate for removal
   m.MethodsCallingMe.Any() &&

  // Eliminate public methods visible outside of their assembly
  // because the rule cannot determine if the developer left the method public 
  // intentionally or not.
  !m.IsPubliclyVisible && 

  // Avoid matching public methods declared in a non-public type, 
  // that could have the visibility internal, because
  // such situation is caught by the rule 'Avoid public methods not publicly visible'.
  !(m.Visibility == Visibility.Public && 
    m.ParentType.Visibility != Visibility.Public && 
    m.OptimalVisibility == Visibility.Internal) &&

  !m.HasAttribute("NDepend.Attributes.CannotDecreaseVisibilityAttribute".AllowNoMatch()) &&
  !m.HasAttribute("NDepend.Attributes.IsNotDeadCodeAttribute".AllowNoMatch()) &&
  !m.HasAttribute("JetBrains.Annotations.UsedImplicitlyAttribute".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.
  
  // methods of serializable type must remain public.
  !m.ParentType.TypesUsed.Any(t1 => t1.IsAttributeClass && 
    (t1.ParentNamespace.Name == "Newtonsoft.Json" || t1.ParentNamespace.Name == "System.Text.Json")
   ) &&
  !m.ParentType.HasAttribute("System.Runtime.Serialization.DataContractAttribute".AllowNoMatch()) &&
  !m.ParentType.HasAttribute("System.Xml.Serialization.XmlRootAttribute".AllowNoMatch()) &&
  !m.ParentType.HasAttribute("System.SerializableAttribute".AllowNoMatch()) &&
  
  // ASP.NET MVC Controller classes and methods are used only by the infrastructure and must be public
  !m.ParentType.SimpleName.EndsWith("Controller") &&
  !m.ParentType.DeriveFrom("Microsoft.AspNetCore.Mvc.Controller".AllowNoMatch()) &&
  !m.ParentType.DeriveFrom("Microsoft.AspNetCore.Mvc.ControllerBase".AllowNoMatch()) && 
  !m.ParentType.HasAttribute("Microsoft.AspNetCore.Mvc.ControllerAttribute".AllowNoMatch()) &&
  
  // Such class is instantiated dynamically
  !(m.IsConstructor && m.ParentType.DeriveFrom("System.ComponentModel.EnumConverter".AllowNoMatch())) &&
    
  // Don't advise to reduce visibility of property getters/setters 
  // of classes that implement INotifyPropertyChanged
  !((m.IsPropertyGetter || m.IsPropertySetter) &&
     m.ParentType.Implement("System.ComponentModel.INotifyPropertyChanged".AllowNoMatch())) &&

  // Don't decrease the visibility of Main() methods.
  !m.IsEntryPoint 
  
select new { 
   m, 
   m.Visibility , 
   CouldBeDeclared = m.OptimalVisibility,
   m.MethodsCallingMe,

   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 method's accessibility 
// to the smallest possible scope.
//</Expl>

//<Desc>
// Warns about methods declarable with a lower visibility (*private* <
// *internal* < *public*) without compilation error. **Narrowing visibility**
// **promotes encapsulation** by minimizing access scope.
//
// Publicly visible methods are not matched (the rule cannot know if public
// was intentional); matched public methods are declared in non-public types.
// *Public* methods that could be *internal* in a non-public type are caught
// by *Avoid public methods not publicly visible* instead.
//
// Methods tagged *CannotDecreaseVisibilityAttribute* or
// *IsNotDeadCodeAttribute* (from *NDepend.API.dll*, or declared in your own
// code) are not matched.
//</Desc>

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

Visibility: all 12 items