views:

257

answers:

6

I have a C# project for which I need to find the all private methods which are not called from any other public method directly or indirectly.

In addition, for each private method which is called from a public method, I need to know which public method it is. Then I will detemine if that method is really called from a client of the class and if not I will be able to remove it.

In the past I used a code from Lutz Rorder which is the base of Reflector - it had an option to analyze IL code and gave object model on top of it. I cannot find this code now.

Any suggestion? Maybe a point to that Lutz Rorder code?

Saar

+1  A: 

Not sure if it will fulfil all these needs, but FXCop will do most of these for you and will help do a whole lot of coding standards as well. It's worth running over your c# code either way.

FXCop (MSDN)

Tim Joseph
FxCop will find unused private methods. As for those that are used, right-click on the method and choose "Find All References" from the menu.
Pedro
+2  A: 

Well, the only method to do this (at least the only one that I know...) implies the use of a commercial (but not so expensive) tool, namely NDepend.

Among many other things, you can write SQL-like queries against your compiled assemblies, which allows for very fine-grained analysis. They call it CQL, the syntax is self-explaining, and NDepend's IntelliSense/auto-completion support as well as the general help/documentation are quite good.

(AFAIK they also provide a fully-featured trial, if that helps you...)

HTH!

Thomas Weller
A: 

Reflector still has this functionality. Just right click the method, and click Analyze. Follow the links in the tree view.

You do not say whether you need to do it in code, or just 'visually'.

If needed for code, I am sure you can use the Reflector SDK.

leppie
A: 

I might be able to help you with this one using the Query Editor in the PowerCommands for Reflector addin (http://powercommands.codeplex.com) ... I'll see what I can come up with tonight, then let you know tomorrow.

Otherwise you could also write a quick application using the CCI or Cecil that would do it.

Query:

from a in AssemblyManager.Assemblies.Cast<IAssembly>()
where a.Name != "mscorlib" 
    && !a.Name.Contains("System")
from m in a.Modules.Cast<IModule>()
from t in m.Types.Cast<ITypeDeclaration>()
from mt in t.Methods.Cast<IMethodDeclaration>()
where mt.Visibility == MethodVisibility.Public 
    && !mt.RuntimeSpecialName 
    && !mt.SpecialName 
    && mt.Body is IMethodBody
from i in ((IMethodBody)mt.Body).Instructions.Cast<IInstruction>()
where i != null 
    && i.Value != null 
    && i.Value is IMethodReference 
    && ((IMethodReference)i.Value).Resolve() != null
    && ((IMethodReference)i.Value).Resolve().Visibility == MethodVisibility.Private
select new { 
    CallingMethod=t.Namespace + "." + t.Name + "." + mt.Name, 
    PrivateReferencedMethod=((ITypeReference)((IMemberReference)((IMethodReference)i.Value).Resolve()).DeclaringType).Namespace + "."
     + ((ITypeReference)((IMemberReference)((IMethodReference)i.Value).Resolve()).DeclaringType).Name + "."
     + ((IMethodReference)i.Value).ToString()
}
Jason Haley
After reading your question for the second time - if you are interested in writihg a utility that does what you are looking for - then check out: http://ccimetadata.codeplex.com/
Jason Haley
If you want to try getting the information from Reflector, you can use the following query to get all the private methods that are called by a public method.NOTE: The assumption is all of your dependencies are loaded into Reflector already ... if you get a dialog asking to locate an assembly you'll have to click 'Skip' or it will kill Reflector.
Jason Haley
I have also added filtering to ignore mscorlib or any System assembly as well as ignore special named methods (constructors and property methods).
Jason Haley
I have a blog entry that introduces the Query Editor here: http://www.jasonhaley.com/blog/post/2009/10/18/Getting-Started-with-Query-Editor.aspx, I should have another one with more detail out in the next day or two.
Jason Haley
+1  A: 

You should check out Nitriq Static Code Analysis for .Net - They have a free community edition and their full blown license is pretty reasonable.

Stan Marsh
A: 

As pointed Thomas the tool NDepend can help you to do that with 3 Code query Language (CQL) queries to match unused methods, fields and types. These 3 queries can be customized to your exact need. Notice also how these queries are prefixed with WARN IF Count > 0 IN, that transform them from CQL queries to CQL rules. These CQL rules can be checked in your CI environment, or even, interactively in VS itself while you are coding:

// <Name>Potentially unused methods</Name>
WARN IF Count > 0 IN SELECT METHODS WHERE
  MethodCa == 0 AND            // Ca=0 -> No Afferent Coupling -> The method is not used in the context of this application.
  !IsPublic AND                // Public methods might be used by client applications of your assemblies.
  !IsEntryPoint AND            // Main() method is not used by-design.
  !IsExplicitInterfaceImpl AND // The IL code never explicitely calls explicit interface methods implementation.
  !IsClassConstructor AND      // The IL code never explicitely calls class constructors.
  !IsFinalizer AND             // The IL code never explicitely calls finalizers.
  !IsVirtual AND
  !IsEventAdder AND
  !IsEventRemover 

// <Name>Potentially unused types</Name>
WARN IF Count > 0 IN SELECT TYPES WHERE  
 TypeCa == 0 AND // Ca=0 -> No Afferent Coupling -> The type
                 // is not used in the context of this application.
 !IsPublic  AND  // Public types might be used by client applications of
                 // your assemblies.   
 !NameIs "Program" 

// <Name>Potentially unused fields</Name>
WARN IF Count > 0 IN SELECT FIELDS 
WHERE 
 FieldCa == 0 AND  // Ca=0 -> No Afferent Coupling -> The field is not used in the context of this application.
 !IsPublic AND     // Although not recommended, public fields might be used by client applications of your assemblies.
 !IsLiteral AND    // The IL code never explicitely uses literal fields.
 !IsEnumValue AND  // The IL code never explicitely uses enumeration value.
 !NameIs "value__" // Field named 'value__' are relative to enumerations and the IL code never explicitely uses them."
Patrick Smacchia - NDepend dev