< Summary

Information
Class: DotNetApiDiff.ApiExtraction.TypeAnalyzer
Assembly: DotNetApiDiff
File(s): /home/runner/work/dotnet-api-diff/dotnet-api-diff/src/DotNetApiDiff/ApiExtraction/TypeAnalyzer.cs
Line coverage
65%
Covered lines: 246
Uncovered lines: 128
Coverable lines: 374
Total lines: 680
Line coverage: 65.7%
Branch coverage
44%
Covered branches: 78
Total branches: 176
Branch coverage: 44.3%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)50%44100%
AnalyzeType(...)66.66%6680.95%
AnalyzeMethods(...)70%111082.35%
AnalyzeProperties(...)70%111081.81%
AnalyzeFields(...)75%131281.81%
AnalyzeEvents(...)60%111075.75%
AnalyzeConstructors(...)75%131281.81%
GetMemberType(...)62.5%11862.5%
GetTypeAccessibility(...)14.28%921426.31%
GetMethodAccessibility(...)10%501026.31%
GetPropertyAccessibility(...)25%13856.25%
GetFieldAccessibility(...)10%501026.31%
GetEventAccessibility(...)50%2285.71%
GetMostAccessible(...)100%11100%
CompareAccessibilityLevels(...)16.66%411241.66%
GetTypeAttributes(...)100%1163.63%
GetMemberAttributes(...)100%1145.45%
IsCompilerGeneratedAttribute(...)0%2040%
IsCompilerGeneratedAttribute(...)50%44100%
IsPublicOrOverride(...)37.5%14853.84%
IsPublicOrOverrideProperty(...)75%171683.33%
IsPublicOrOverrideEvent(...)18.75%351658.33%

File(s)

/home/runner/work/dotnet-api-diff/dotnet-api-diff/src/DotNetApiDiff/ApiExtraction/TypeAnalyzer.cs

#LineLine coverage
 1// Copyright DotNet API Diff Project Contributors - SPDX Identifier: MIT
 2using System.Reflection;
 3using DotNetApiDiff.Interfaces;
 4using DotNetApiDiff.Models;
 5using Microsoft.Extensions.Logging;
 6
 7namespace DotNetApiDiff.ApiExtraction;
 8
 9/// <summary>
 10/// Analyzes .NET types and their members to extract API information
 11/// </summary>
 12public class TypeAnalyzer : ITypeAnalyzer
 13{
 14    private readonly IMemberSignatureBuilder _signatureBuilder;
 15    private readonly ILogger<TypeAnalyzer> _logger;
 16
 17    /// <summary>
 18    /// Creates a new instance of the TypeAnalyzer
 19    /// </summary>
 20    /// <param name="signatureBuilder">Signature builder for creating normalized signatures</param>
 21    /// <param name="logger">Logger for diagnostic information</param>
 1622    public TypeAnalyzer(IMemberSignatureBuilder signatureBuilder, ILogger<TypeAnalyzer> logger)
 1623    {
 1624        _signatureBuilder = signatureBuilder ?? throw new ArgumentNullException(nameof(signatureBuilder));
 1625        _logger = logger ?? throw new ArgumentNullException(nameof(logger));
 1626    }
 27
 28    /// <summary>
 29    /// Analyzes a type and returns its API member representation
 30    /// </summary>
 31    /// <param name="type">Type to analyze</param>
 32    /// <returns>ApiMember representing the type</returns>
 33    public ApiMember AnalyzeType(Type type)
 334    {
 335        if (type == null)
 136        {
 137            throw new ArgumentNullException(nameof(type));
 38        }
 39
 40        try
 241        {
 242            var member = new ApiMember
 243            {
 244                Name = type.Name,
 245                FullName = type.FullName ?? type.Name,
 246                Namespace = type.Namespace ?? string.Empty,
 247                Signature = _signatureBuilder.BuildTypeSignature(type),
 248                Attributes = GetTypeAttributes(type),
 249                Type = GetMemberType(type),
 250                Accessibility = GetTypeAccessibility(type)
 251            };
 52
 253            return member;
 54        }
 055        catch (Exception ex)
 056        {
 057            _logger.LogError(ex, "Error analyzing type {TypeName}", type.Name);
 058            throw;
 59        }
 260    }
 61
 62    /// <summary>
 63    /// Analyzes all methods of a type
 64    /// </summary>
 65    /// <param name="type">Type to analyze methods for</param>
 66    /// <returns>Collection of method API members</returns>
 67    public IEnumerable<ApiMember> AnalyzeMethods(Type type)
 168    {
 169        if (type == null)
 070        {
 071            throw new ArgumentNullException(nameof(type));
 72        }
 73
 74        try
 175        {
 176            var methods = new List<ApiMember>();
 77
 78            // Get all methods, excluding property accessors, event accessors, and constructors
 179            var methodInfos = type.GetMethods(BindingFlags.Public | BindingFlags.NonPublic |
 180                                             BindingFlags.Instance | BindingFlags.Static |
 181                                             BindingFlags.DeclaredOnly)
 1082                                  .Where(m => !m.IsSpecialName);
 83
 984            foreach (var method in methodInfos)
 385            {
 86                // Skip non-public methods unless they're overrides of public methods
 387                if (!IsPublicOrOverride(method))
 188                {
 189                    continue;
 90                }
 91
 292                var member = new ApiMember
 293                {
 294                    Name = method.Name,
 295                    FullName = $"{type.FullName}.{method.Name}",
 296                    Namespace = type.Namespace ?? string.Empty,
 297                    DeclaringType = type.FullName ?? type.Name,
 298                    Signature = _signatureBuilder.BuildMethodSignature(method),
 299                    Attributes = GetMemberAttributes(method),
 2100                    Type = MemberType.Method,
 2101                    Accessibility = GetMethodAccessibility(method)
 2102                };
 103
 2104                methods.Add(member);
 2105            }
 106
 1107            return methods;
 108        }
 0109        catch (Exception ex)
 0110        {
 0111            _logger.LogError(ex, "Error analyzing methods for type {TypeName}", type.Name);
 0112            return Enumerable.Empty<ApiMember>();
 113        }
 1114    }
 115
 116    /// <summary>
 117    /// Analyzes all properties of a type
 118    /// </summary>
 119    /// <param name="type">Type to analyze properties for</param>
 120    /// <returns>Collection of property API members</returns>
 121    public IEnumerable<ApiMember> AnalyzeProperties(Type type)
 1122    {
 1123        if (type == null)
 0124        {
 0125            throw new ArgumentNullException(nameof(type));
 126        }
 127
 128        try
 1129        {
 1130            var properties = new List<ApiMember>();
 131
 1132            var propertyInfos = type.GetProperties(BindingFlags.Public | BindingFlags.NonPublic |
 1133                                                 BindingFlags.Instance | BindingFlags.Static |
 1134                                                 BindingFlags.DeclaredOnly);
 135
 7136            foreach (var property in propertyInfos)
 2137            {
 138                // Skip non-public properties unless they're overrides of public properties
 2139                if (!IsPublicOrOverrideProperty(property))
 1140                {
 1141                    continue;
 142                }
 143
 1144                var member = new ApiMember
 1145                {
 1146                    Name = property.Name,
 1147                    FullName = $"{type.FullName}.{property.Name}",
 1148                    Namespace = type.Namespace ?? string.Empty,
 1149                    DeclaringType = type.FullName ?? type.Name,
 1150                    Signature = _signatureBuilder.BuildPropertySignature(property),
 1151                    Attributes = GetMemberAttributes(property),
 1152                    Type = MemberType.Property,
 1153                    Accessibility = GetPropertyAccessibility(property)
 1154                };
 155
 1156                properties.Add(member);
 1157            }
 158
 1159            return properties;
 160        }
 0161        catch (Exception ex)
 0162        {
 0163            _logger.LogError(ex, "Error analyzing properties for type {TypeName}", type.Name);
 0164            return Enumerable.Empty<ApiMember>();
 165        }
 1166    }
 167
 168    /// <summary>
 169    /// Analyzes all fields of a type
 170    /// </summary>
 171    /// <param name="type">Type to analyze fields for</param>
 172    /// <returns>Collection of field API members</returns>
 173    public IEnumerable<ApiMember> AnalyzeFields(Type type)
 1174    {
 1175        if (type == null)
 0176        {
 0177            throw new ArgumentNullException(nameof(type));
 178        }
 179
 180        try
 1181        {
 1182            var fields = new List<ApiMember>();
 183
 1184            var fieldInfos = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic |
 1185                                          BindingFlags.Instance | BindingFlags.Static |
 1186                                          BindingFlags.DeclaredOnly);
 187
 13188            foreach (var field in fieldInfos)
 5189            {
 190                // Skip non-public fields and compiler-generated backing fields
 5191                if (!field.IsPublic || field.Name.StartsWith("<"))
 4192                {
 4193                    continue;
 194                }
 195
 1196                var member = new ApiMember
 1197                {
 1198                    Name = field.Name,
 1199                    FullName = $"{type.FullName}.{field.Name}",
 1200                    Namespace = type.Namespace ?? string.Empty,
 1201                    DeclaringType = type.FullName ?? type.Name,
 1202                    Signature = _signatureBuilder.BuildFieldSignature(field),
 1203                    Attributes = GetMemberAttributes(field),
 1204                    Type = MemberType.Field,
 1205                    Accessibility = GetFieldAccessibility(field)
 1206                };
 207
 1208                fields.Add(member);
 1209            }
 210
 1211            return fields;
 212        }
 0213        catch (Exception ex)
 0214        {
 0215            _logger.LogError(ex, "Error analyzing fields for type {TypeName}", type.Name);
 0216            return Enumerable.Empty<ApiMember>();
 217        }
 1218    }
 219
 220    /// <summary>
 221    /// Analyzes all events of a type
 222    /// </summary>
 223    /// <param name="type">Type to analyze events for</param>
 224    /// <returns>Collection of event API members</returns>
 225    public IEnumerable<ApiMember> AnalyzeEvents(Type type)
 1226    {
 1227        if (type == null)
 0228        {
 0229            throw new ArgumentNullException(nameof(type));
 230        }
 231
 232        try
 1233        {
 1234            var events = new List<ApiMember>();
 235
 1236            var eventInfos = type.GetEvents(BindingFlags.Public | BindingFlags.NonPublic |
 1237                                          BindingFlags.Instance | BindingFlags.Static |
 1238                                          BindingFlags.DeclaredOnly);
 239
 5240            foreach (var eventInfo in eventInfos)
 1241            {
 242                // Skip non-public events unless they're overrides of public events
 1243                if (!IsPublicOrOverrideEvent(eventInfo))
 0244                {
 0245                    continue;
 246                }
 247
 1248                var member = new ApiMember
 1249                {
 1250                    Name = eventInfo.Name,
 1251                    FullName = $"{type.FullName}.{eventInfo.Name}",
 1252                    Namespace = type.Namespace ?? string.Empty,
 1253                    DeclaringType = type.FullName ?? type.Name,
 1254                    Signature = _signatureBuilder.BuildEventSignature(eventInfo),
 1255                    Attributes = GetMemberAttributes(eventInfo),
 1256                    Type = MemberType.Event,
 1257                    Accessibility = GetEventAccessibility(eventInfo)
 1258                };
 259
 1260                events.Add(member);
 1261            }
 262
 1263            return events;
 264        }
 0265        catch (Exception ex)
 0266        {
 0267            _logger.LogError(ex, "Error analyzing events for type {TypeName}", type.Name);
 0268            return Enumerable.Empty<ApiMember>();
 269        }
 1270    }
 271
 272    /// <summary>
 273    /// Analyzes all constructors of a type
 274    /// </summary>
 275    /// <param name="type">Type to analyze constructors for</param>
 276    /// <returns>Collection of constructor API members</returns>
 277    public IEnumerable<ApiMember> AnalyzeConstructors(Type type)
 1278    {
 1279        if (type == null)
 0280        {
 0281            throw new ArgumentNullException(nameof(type));
 282        }
 283
 284        try
 1285        {
 1286            var constructors = new List<ApiMember>();
 287
 1288            var constructorInfos = type.GetConstructors(BindingFlags.Public | BindingFlags.NonPublic |
 1289                                                      BindingFlags.Instance | BindingFlags.Static |
 1290                                                      BindingFlags.DeclaredOnly);
 291
 7292            foreach (var constructor in constructorInfos)
 2293            {
 294                // Skip non-public constructors
 2295                if (!constructor.IsPublic && !constructor.IsFamily)
 1296                {
 1297                    continue;
 298                }
 299
 1300                var member = new ApiMember
 1301                {
 1302                    Name = constructor.Name,
 1303                    FullName = $"{type.FullName}.{constructor.Name}",
 1304                    Namespace = type.Namespace ?? string.Empty,
 1305                    DeclaringType = type.FullName ?? type.Name,
 1306                    Signature = _signatureBuilder.BuildConstructorSignature(constructor),
 1307                    Attributes = GetMemberAttributes(constructor),
 1308                    Type = MemberType.Constructor,
 1309                    Accessibility = GetMethodAccessibility(constructor)
 1310                };
 311
 1312                constructors.Add(member);
 1313            }
 314
 1315            return constructors;
 316        }
 0317        catch (Exception ex)
 0318        {
 0319            _logger.LogError(ex, "Error analyzing constructors for type {TypeName}", type.Name);
 0320            return Enumerable.Empty<ApiMember>();
 321        }
 1322    }
 323
 324    /// <summary>
 325    /// Gets the member type for a .NET type
 326    /// </summary>
 327    /// <param name="type">Type to get member type for</param>
 328    /// <returns>Member type</returns>
 329    private MemberType GetMemberType(Type type)
 2330    {
 2331        if (type.IsInterface)
 1332        {
 1333            return MemberType.Interface;
 334        }
 1335        else if (type.IsEnum)
 0336        {
 0337            return MemberType.Enum;
 338        }
 1339        else if (type.IsValueType)
 0340        {
 0341            return MemberType.Struct;
 342        }
 1343        else if (type.IsSubclassOf(typeof(MulticastDelegate)))
 0344        {
 0345            return MemberType.Delegate;
 346        }
 347        else
 1348        {
 1349            return MemberType.Class;
 350        }
 2351    }
 352
 353    /// <summary>
 354    /// Gets the accessibility level for a type
 355    /// </summary>
 356    /// <param name="type">Type to get accessibility for</param>
 357    /// <returns>Accessibility level</returns>
 358    private AccessibilityLevel GetTypeAccessibility(Type type)
 2359    {
 2360        if (type.IsNestedPublic || type.IsPublic)
 2361        {
 2362            return AccessibilityLevel.Public;
 363        }
 0364        else if (type.IsNestedFamily)
 0365        {
 0366            return AccessibilityLevel.Protected;
 367        }
 0368        else if (type.IsNestedFamORAssem)
 0369        {
 0370            return AccessibilityLevel.ProtectedInternal;
 371        }
 0372        else if (type.IsNestedFamANDAssem)
 0373        {
 0374            return AccessibilityLevel.ProtectedPrivate;
 375        }
 0376        else if (type.IsNestedAssembly || type.IsNotPublic)
 0377        {
 0378            return AccessibilityLevel.Internal;
 379        }
 380        else
 0381        {
 0382            return AccessibilityLevel.Private;
 383        }
 2384    }
 385
 386    /// <summary>
 387    /// Gets the accessibility level for a method or constructor
 388    /// </summary>
 389    /// <param name="method">Method to get accessibility for</param>
 390    /// <returns>Accessibility level</returns>
 391    private AccessibilityLevel GetMethodAccessibility(MethodBase method)
 6392    {
 6393        if (method.IsPublic)
 6394        {
 6395            return AccessibilityLevel.Public;
 396        }
 0397        else if (method.IsFamily)
 0398        {
 0399            return AccessibilityLevel.Protected;
 400        }
 0401        else if (method.IsFamilyOrAssembly)
 0402        {
 0403            return AccessibilityLevel.ProtectedInternal;
 404        }
 0405        else if (method.IsFamilyAndAssembly)
 0406        {
 0407            return AccessibilityLevel.ProtectedPrivate;
 408        }
 0409        else if (method.IsAssembly)
 0410        {
 0411            return AccessibilityLevel.Internal;
 412        }
 413        else
 0414        {
 0415            return AccessibilityLevel.Private;
 416        }
 6417    }
 418
 419    /// <summary>
 420    /// Gets the accessibility level for a property
 421    /// </summary>
 422    /// <param name="property">Property to get accessibility for</param>
 423    /// <returns>Accessibility level</returns>
 424    private AccessibilityLevel GetPropertyAccessibility(PropertyInfo property)
 1425    {
 1426        var getMethod = property.GetMethod;
 1427        var setMethod = property.SetMethod;
 428
 429        // Use the most accessible between get and set
 1430        if (getMethod != null && setMethod != null)
 1431        {
 1432            var getAccess = GetMethodAccessibility(getMethod);
 1433            var setAccess = GetMethodAccessibility(setMethod);
 1434            return GetMostAccessible(getAccess, setAccess);
 435        }
 0436        else if (getMethod != null)
 0437        {
 0438            return GetMethodAccessibility(getMethod);
 439        }
 0440        else if (setMethod != null)
 0441        {
 0442            return GetMethodAccessibility(setMethod);
 443        }
 444
 0445        return AccessibilityLevel.Private;
 1446    }
 447
 448    /// <summary>
 449    /// Gets the accessibility level for a field
 450    /// </summary>
 451    /// <param name="field">Field to get accessibility for</param>
 452    /// <returns>Accessibility level</returns>
 453    private AccessibilityLevel GetFieldAccessibility(FieldInfo field)
 1454    {
 1455        if (field.IsPublic)
 1456        {
 1457            return AccessibilityLevel.Public;
 458        }
 0459        else if (field.IsFamily)
 0460        {
 0461            return AccessibilityLevel.Protected;
 462        }
 0463        else if (field.IsFamilyOrAssembly)
 0464        {
 0465            return AccessibilityLevel.ProtectedInternal;
 466        }
 0467        else if (field.IsFamilyAndAssembly)
 0468        {
 0469            return AccessibilityLevel.ProtectedPrivate;
 470        }
 0471        else if (field.IsAssembly)
 0472        {
 0473            return AccessibilityLevel.Internal;
 474        }
 475        else
 0476        {
 0477            return AccessibilityLevel.Private;
 478        }
 1479    }
 480
 481    /// <summary>
 482    /// Gets the accessibility level for an event
 483    /// </summary>
 484    /// <param name="eventInfo">Event to get accessibility for</param>
 485    /// <returns>Accessibility level</returns>
 486    private AccessibilityLevel GetEventAccessibility(EventInfo eventInfo)
 1487    {
 1488        var addMethod = eventInfo.AddMethod;
 489
 1490        if (addMethod != null)
 1491        {
 1492            return GetMethodAccessibility(addMethod);
 493        }
 494
 0495        return AccessibilityLevel.Private;
 1496    }
 497
 498    /// <summary>
 499    /// Gets the most accessible of two accessibility levels
 500    /// </summary>
 501    /// <param name="access1">First accessibility level</param>
 502    /// <param name="access2">Second accessibility level</param>
 503    /// <returns>Most accessible level</returns>
 504    private AccessibilityLevel GetMostAccessible(AccessibilityLevel access1, AccessibilityLevel access2)
 1505    {
 1506        return CompareAccessibilityLevels(access1, access2);
 1507    }
 508
 509    /// <summary>
 510    /// Compares two accessibility levels and returns the more accessible one
 511    /// </summary>
 512    /// <param name="access1">First accessibility level</param>
 513    /// <param name="access2">Second accessibility level</param>
 514    /// <returns>Most accessible level</returns>
 515    private AccessibilityLevel CompareAccessibilityLevels(AccessibilityLevel access1, AccessibilityLevel access2)
 1516    {
 1517        if (access1 == AccessibilityLevel.Public || access2 == AccessibilityLevel.Public)
 1518        {
 1519            return AccessibilityLevel.Public;
 520        }
 521
 0522        if (access1 == AccessibilityLevel.Protected || access2 == AccessibilityLevel.Protected)
 0523        {
 0524            return AccessibilityLevel.Protected;
 525        }
 526
 0527        if (access1 == AccessibilityLevel.Internal || access2 == AccessibilityLevel.Internal)
 0528        {
 0529            return AccessibilityLevel.Internal;
 530        }
 531
 0532        return AccessibilityLevel.Private;
 1533    }
 534
 535    /// <summary>
 536    /// Gets the attributes applied to a type
 537    /// </summary>
 538    /// <param name="type">Type to get attributes for</param>
 539    /// <returns>List of attribute names</returns>
 540    private List<string> GetTypeAttributes(Type type)
 2541    {
 542        try
 2543        {
 2544            return type.GetCustomAttributesData()
 1545                .Where(a => !IsCompilerGeneratedAttribute(a))
 1546                .Select(a => a.AttributeType.Name)
 2547                .ToList();
 548        }
 0549        catch (Exception ex)
 0550        {
 0551            _logger.LogWarning(ex, "Error getting attributes for type {TypeName}", type.Name);
 0552            return new List<string>();
 553        }
 2554    }
 555
 556    /// <summary>
 557    /// Gets the attributes applied to a member
 558    /// </summary>
 559    /// <param name="member">Member to get attributes for</param>
 560    /// <returns>List of attribute names</returns>
 561    private List<string> GetMemberAttributes(MemberInfo member)
 6562    {
 563        try
 6564        {
 6565            return member.GetCustomAttributesData()
 0566                .Where(a => !IsCompilerGeneratedAttribute(a))
 0567                .Select(a => a.AttributeType.Name)
 6568                .ToList();
 569        }
 0570        catch (Exception ex)
 0571        {
 0572            _logger.LogWarning(ex, "Error getting attributes for member {MemberName}", member.Name);
 0573            return new List<string>();
 574        }
 6575    }
 576
 577    /// <summary>
 578    /// Checks if an attribute is compiler-generated
 579    /// </summary>
 580    /// <param name="attribute">Attribute to check</param>
 581    /// <returns>True if compiler-generated, false otherwise</returns>
 582    private bool IsCompilerGeneratedAttribute(object attribute)
 0583    {
 0584        var typeName = attribute.GetType().Name;
 0585        return typeName == "CompilerGeneratedAttribute" ||
 0586               typeName == "DebuggerHiddenAttribute" ||
 0587               typeName == "DebuggerNonUserCodeAttribute";
 0588    }
 589
 590    /// <summary>
 591    /// Checks if an attribute is compiler-generated
 592    /// </summary>
 593    /// <param name="attributeData">Attribute data to check</param>
 594    /// <returns>True if compiler-generated, false otherwise</returns>
 595    private bool IsCompilerGeneratedAttribute(CustomAttributeData attributeData)
 1596    {
 1597        var typeName = attributeData.AttributeType.Name;
 1598        return typeName == "CompilerGeneratedAttribute" ||
 1599               typeName == "DebuggerHiddenAttribute" ||
 1600               typeName == "DebuggerNonUserCodeAttribute";
 1601    }
 602
 603    /// <summary>
 604    /// Checks if a method is public or an override of a public method
 605    /// </summary>
 606    /// <param name="method">Method to check</param>
 607    /// <returns>True if public or override, false otherwise</returns>
 608    private bool IsPublicOrOverride(MethodInfo method)
 5609    {
 610        // If the method is public, include it
 5611        if (method.IsPublic)
 2612        {
 2613            return true;
 614        }
 615
 616        // If the method is an override, check if it's overriding a public method
 3617        if (method.GetBaseDefinition() != method)
 0618        {
 0619            var baseMethod = method.GetBaseDefinition();
 0620            if (baseMethod != null && baseMethod.IsPublic)
 0621            {
 0622                return true;
 623            }
 0624        }
 625
 3626        return false;
 5627    }
 628
 629    /// <summary>
 630    /// Checks if a property is public or an override of a public property
 631    /// </summary>
 632    /// <param name="property">Property to check</param>
 633    /// <returns>True if public or override, false otherwise</returns>
 634    private bool IsPublicOrOverrideProperty(PropertyInfo property)
 2635    {
 2636        var getMethod = property.GetMethod;
 2637        var setMethod = property.SetMethod;
 638
 639        // If either accessor is public, include the property
 2640        if ((getMethod != null && getMethod.IsPublic) || (setMethod != null && setMethod.IsPublic))
 1641        {
 1642            return true;
 643        }
 644
 645        // Check if either accessor is overriding a public accessor
 1646        if ((getMethod != null && IsPublicOrOverride(getMethod)) ||
 1647            (setMethod != null && IsPublicOrOverride(setMethod)))
 0648        {
 0649            return true;
 650        }
 651
 1652        return false;
 2653    }
 654
 655    /// <summary>
 656    /// Checks if an event is public or an override of a public event
 657    /// </summary>
 658    /// <param name="eventInfo">Event to check</param>
 659    /// <returns>True if public or override, false otherwise</returns>
 660    private bool IsPublicOrOverrideEvent(EventInfo eventInfo)
 1661    {
 1662        var addMethod = eventInfo.AddMethod;
 1663        var removeMethod = eventInfo.RemoveMethod;
 664
 665        // If either accessor is public, include the event
 1666        if ((addMethod != null && addMethod.IsPublic) || (removeMethod != null && removeMethod.IsPublic))
 1667        {
 1668            return true;
 669        }
 670
 671        // Check if either accessor is overriding a public accessor
 0672        if ((addMethod != null && IsPublicOrOverride(addMethod)) ||
 0673            (removeMethod != null && IsPublicOrOverride(removeMethod)))
 0674        {
 0675            return true;
 676        }
 677
 0678        return false;
 1679    }
 680}