• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Sholtee / proxygen / 1070

04 May 2025 02:36PM UTC coverage: 92.846% (+0.05%) from 92.794%
1070

push

appveyor

Sholtee
document internal classes IV, ITypeSymbolExtensions refactor

4815 of 5186 relevant lines covered (92.85%)

0.93 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.98
/SRC/Private/Extensions/Metadata/MethodInfoExtensions.cs
1
/********************************************************************************
2
* MethodInfoExtensions.cs                                                       *
3
*                                                                               *
4
* Author: Denes Solti                                                           *
5
********************************************************************************/
6
using System;
7
using System.Collections.Generic;
8
using System.Diagnostics;
9
using System.Linq;
10
using System.Linq.Expressions;
11
using System.Reflection;
12

13
namespace Solti.Utils.Proxy.Internals
14
{
15
    using Properties;
16

17
    /// <summary>
18
    /// Defines helper methods for the <see cref="MethodBase"/> and <see cref="MethodInfo"/> classes.
19
    /// </summary>
20
    internal static class MethodInfoExtensions
21
    {
22
        /// <summary>
23
        /// Calculates the <see cref="AccessModifiers"/> value for the given method.
24
        /// </summary>
25
        /// <exception cref="InvalidOperationException">When the access modifier can not be determined</exception>
26
        public static AccessModifiers GetAccessModifiers(this MethodBase src) => src switch
1✔
27
        {
1✔
28
            { IsFamily: true } => AccessModifiers.Protected,
1✔
29
            { IsAssembly: true } => AccessModifiers.Internal,
1✔
30
            { IsFamilyOrAssembly: true } => AccessModifiers.Protected | AccessModifiers.Internal,
1✔
31
            { IsFamilyAndAssembly: true } => AccessModifiers.Protected | AccessModifiers.Private,
1✔
32
            { IsPublic: true } => AccessModifiers.Public,
1✔
33
            { IsPrivate: true } when src.GetImplementedInterfaceMethods().Any() => AccessModifiers.Explicit,
1✔
34
            { IsPrivate: true} => AccessModifiers.Private,
1✔
35
            _ => throw new InvalidOperationException(Resources.UNDETERMINED_ACCESS_MODIFIER)
×
36
        };
1✔
37

38
        /// <summary>
39
        /// Returns the interfaces that declare the given method:
40
        /// <code>
41
        /// typeof(List&lt;int&gt;).GetProperty("Count").GetGetMethod().GetDeclaringInterfaces();  // [typeof(ICollection), typeof(IReadOnyCollection&lt;int&gt;)] 
42
        /// </code>
43
        /// <paramref name="src"/> should belong to a class method.
44
        /// </summary>
45
        public static IEnumerable<Type> GetDeclaringInterfaces(this MethodBase src) => src.ReflectedType.IsInterface
1✔
46
            ? []
1✔
47
            : src
1✔
48
                .GetImplementedInterfaceMethods()
1✔
49
                .Select(static m => m.ReflectedType);
1✔
50

51
        /// <summary>
52
        /// Returns the interface methods that are implemented by the given implementation. <paramref name="src"/> should belong to a class method.
53
        /// </summary>
54
        public static IEnumerable<MethodInfo> GetImplementedInterfaceMethods(this MethodBase src)
55
        {
1✔
56
            //
57
            // As of C# 11 interfaces may have static abstract methods... We don't deal with
58
            // the implementors.
59
            //
60

61
            if (src.IsStatic || src is not MethodInfo method /*ctor*/)
1✔
62
                yield break;
1✔
63

64
            Type reflectedType = src.ReflectedType;
1✔
65
            if (reflectedType.IsInterface)
1✔
66
                yield break;
1✔
67

68
            foreach (Type iface in reflectedType.GetInterfaces())
1✔
69
            {
1✔
70
                //
71
                // https://docs.microsoft.com/en-us/dotnet/api/system.type.getinterfacemap?view=netcore-3.1#exceptions
72
                //
73

74
                if (iface.IsGenericType && reflectedType.IsArray)
1✔
75
                    continue;
×
76

77
                InterfaceMapping mapping = reflectedType.GetInterfaceMap(iface);
1✔
78

79
                int mapIndex = mapping
1✔
80
                    .TargetMethods
1✔
81
                    .IndexOf(method);
1✔
82

83
                if (mapIndex >= 0) 
1✔
84
                    yield return mapping.InterfaceMethods[mapIndex];
1✔
85
            }
1✔
86
        }
1✔
87

88
        /// <summary>
89
        /// Gets the immediate method (in contrast of <see cref="MethodInfo.GetBaseDefinition"/>) that has been overridden by the given method (using "new" or "override" keyword). Returns null if the base method could not be determined.
90
        /// </summary>
91
        public static MethodInfo? GetOverriddenMethod(this MethodInfo method)
92
        {
1✔
93
            /*
94
            if (method.IsVirtual)
95
            {
96
                MethodInfo overriddenMethod = method.GetBaseDefinition();
97
                return overriddenMethod != method
98
                    ? overriddenMethod
99
                    : null;
100
            }
101
            */
102

103
            Debug.Assert(!method.IsGenericMethod || method.IsGenericMethodDefinition, "The original method cannot be closed generic");
1✔
104

105
            //
106
            // GetBaseDefinition() won't work for "new" override as well as it always return the declaring
107
            // method instead of the overridden one.
108
            //
109

110
            Type[] paramz = [..method.GetParameters().Select(static p => p.ParameterType)];
1✔
111

112
            foreach (Type baseType in method.ReflectedType.GetBaseTypes())
1✔
113
            {
1✔
114
                //
115
                // baseType.GetMethod(method.Name, types: paramz) won't work for generic methods
116
                //
117

118
                foreach(MethodInfo baseMethod in  baseType.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.NonPublic | (method.IsStatic ? BindingFlags.Static : BindingFlags.Instance)))
1✔
119
                {
1✔
120
                    if (baseMethod.Name != method.Name)
1✔
121
                        continue;
1✔
122

123
                    if (baseMethod.IsGenericMethod)
1✔
124
                    {
1✔
125
                        Debug.Assert(baseMethod.IsGenericMethodDefinition, "The inspected method cannot be closed generic");
1✔
126

127
                        //
128
                        // We don't need to compare the generic parameters, just check the arity
129
                        //
130

131
                        if (!method.IsGenericMethod || baseMethod.GetGenericArguments().Length != method.GetGenericArguments().Length)
×
132
                            continue;
×
133
                    }
1✔
134

135
                    if (baseMethod.GetParameters().Select(static p => p.ParameterType).SequenceEqual(paramz, TypeComparer.Instance))
1✔
136
                        return baseMethod;
1✔
137
                }
1✔
138
            }
1✔
139

140
            return null;
1✔
141
        }
1✔
142

143
        //
144
        // Similar logic is provided by Solti.Utils.Primitives, too. But we don't want to ship
145
        // that library with our source generator, so reimplement it.
146
        //
147

148
        /// <summary>
149
        /// Extracts the method info referenced by the given expression.
150
        /// </summary>
151
        public static MethodInfo ExtractFrom<T>(Expression<Action<T>> expression) => ((MethodCallExpression) expression.Body).Method;
1✔
152

153
        /// <summary>
154
        /// Returns true if the given method can be overridden (not sealed virtual)
155
        /// </summary>
156
        public static bool IsVirtual(this MethodBase method) =>
157
            method.IsVirtual && !method.IsFinal && !method.ReflectedType.IsInterface;
1✔
158
    }
159
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc