Search code examples
ndepend

Getting list of types that are effected by an extension method in cqlinq


How to get the list of types that are extended by a extension method in ndepend cqlinq? Using reflection to code this seems a bit of donkey work where ndepend is already there.


Solution

  • NDepend code model doesn't have a straight way to resolve the method parameter type. So we can come up with a satisfying answer with code query relying on string formatting extended type name, extracted from the method name. But this query is overly complex and there are edge cases where it won't work properly (explained below).

    Here is the code query, it runs fast even on large code base thanks to the use of a dictionary:

    //
    // First let build dicoExtensionMethods 
    let dicoExtensionMethods =
    (from m in Application.Methods
    where m.IsExtensionMethod
    
    // extract extended type simple name (with generic parameters like "IEnumerable<String>")
    let beginIndex = m.Name.IndexOf("(") + 1
    let endIndex = m.Name.IndexOf(',', beginIndex) > 0 ? m.Name.IndexOf(',', beginIndex) : m.Name.IndexOf(")", beginIndex) 
    let extendedTypeSimpleName1 = m.Name.Substring(beginIndex, endIndex - beginIndex)
    
    // Take care of generic type first char, like "IEnumerable<"
    let extendedTypeSimpleName2 = extendedTypeSimpleName1.IndexOf('<') == -1 ? extendedTypeSimpleName1 :
                                  extendedTypeSimpleName1.Substring(0, extendedTypeSimpleName1.IndexOf('<') + 1 )
    select new { m, extendedTypeSimpleName2 })
    .ToLookup(pair => pair.extendedTypeSimpleName2)
    .ToDictionary(g => g.Key, g=> g.Select(p =>p.m))
    
    //
    // Second for each type get extension methods from dicoExtensionMethods 
    from t in Types
    // Format type name like  "IEnumerable<"
    let typeName = !t.IsGeneric ? t.SimpleName : t.Name.Substring(0, t.Name.IndexOf('<') + 1 )
    where dicoExtensionMethods.ContainsKey(typeName) 
    let methods = dicoExtensionMethods[typeName]
    select new { t, methods } 
    

    As written it is a complex query because of type name formatting, and it works fine most of the time.

    NDepend Code Query Result Extension Methods

    However when it comes to extending generic types, it says for example that IEnumerable<T> is extended by both methods that extend IEnumerable<String> and IEnumerable<Int32>. This is acceptable, but it is not 100% correct.

    NDepend Code Query Result Extension Methods

    Also if you extend several types with same name but various generic arity (like Func<T1,T2> and Func<T1,T2,T3>), then this code query won't work properly.

    The same if you extend several types with same name, declared in different assemblies or namespace (which is a code smell anyway).

    Hope this helps!