From e5a85422d3a78160f6e62ba524e0b1e4d702e735 Mon Sep 17 00:00:00 2001 From: Siegfried Pammer Date: Sun, 13 Apr 2025 21:41:37 +0200 Subject: [PATCH] Fix #3457: NRE when creating Mermaid diagram of assembly with (possibly) missing references. --- .../TypeSystem/InheritanceHelper.cs | 42 ++++++++++++------- .../MermaidDiagrammer/Factory.FlatMembers.cs | 34 ++++++++++++--- 2 files changed, 55 insertions(+), 21 deletions(-) diff --git a/ICSharpCode.Decompiler/TypeSystem/InheritanceHelper.cs b/ICSharpCode.Decompiler/TypeSystem/InheritanceHelper.cs index f9b976fe1..146ea9318 100644 --- a/ICSharpCode.Decompiler/TypeSystem/InheritanceHelper.cs +++ b/ICSharpCode.Decompiler/TypeSystem/InheritanceHelper.cs @@ -20,6 +20,8 @@ using System; using System.Collections.Generic; using System.Linq; +#nullable enable + namespace ICSharpCode.Decompiler.TypeSystem { /// @@ -34,7 +36,7 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// Gets the base member that has the same signature. /// - public static IMember GetBaseMember(IMember member) + public static IMember? GetBaseMember(IMember member) { return GetBaseMembers(member, false).FirstOrDefault(); } @@ -109,7 +111,7 @@ namespace ICSharpCode.Decompiler.TypeSystem /// /// Finds the member declared in 'derivedType' that has the same signature (could override) 'baseMember'. /// - public static IMember GetDerivedMember(IMember baseMember, ITypeDefinition derivedType) + public static IMember? GetDerivedMember(IMember baseMember, ITypeDefinition derivedType) { if (baseMember == null) throw new ArgumentNullException(nameof(baseMember)); @@ -120,9 +122,8 @@ namespace ICSharpCode.Decompiler.TypeSystem throw new ArgumentException("baseMember and derivedType must be from the same compilation"); baseMember = baseMember.MemberDefinition; - bool includeInterfaces = baseMember.DeclaringTypeDefinition.Kind == TypeKind.Interface; - IMethod method = baseMember as IMethod; - if (method != null) + bool includeInterfaces = baseMember.DeclaringTypeDefinition?.Kind == TypeKind.Interface; + if (baseMember is IMethod method) { foreach (IMethod derivedMethod in derivedType.Methods) { @@ -137,8 +138,7 @@ namespace ICSharpCode.Decompiler.TypeSystem } } } - IProperty property = baseMember as IProperty; - if (property != null) + if (baseMember is IProperty property) { foreach (IProperty derivedProperty in derivedType.Properties) { @@ -175,7 +175,7 @@ namespace ICSharpCode.Decompiler.TypeSystem { foreach (var baseType in typeDef.GetNonInterfaceBaseTypes().Reverse()) { - ITypeDefinition baseTypeDef = baseType.GetDefinition(); + ITypeDefinition? baseTypeDef = baseType.GetDefinition(); if (baseTypeDef == null) continue; foreach (var attr in baseTypeDef.GetAttributes()) @@ -185,11 +185,11 @@ namespace ICSharpCode.Decompiler.TypeSystem } } - internal static IAttribute GetAttribute(ITypeDefinition typeDef, KnownAttribute attributeType) + internal static IAttribute? GetAttribute(ITypeDefinition typeDef, KnownAttribute attributeType) { foreach (var baseType in typeDef.GetNonInterfaceBaseTypes().Reverse()) { - ITypeDefinition baseTypeDef = baseType.GetDefinition(); + ITypeDefinition? baseTypeDef = baseType.GetDefinition(); if (baseTypeDef == null) continue; var attr = baseTypeDef.GetAttribute(attributeType); @@ -202,7 +202,7 @@ namespace ICSharpCode.Decompiler.TypeSystem internal static IEnumerable GetAttributes(IMember member) { HashSet visitedMembers = new HashSet(); - do + while (true) { member = member.MemberDefinition; // it's sufficient to look at the definitions if (!visitedMembers.Add(member)) @@ -214,13 +214,19 @@ namespace ICSharpCode.Decompiler.TypeSystem { yield return attr; } - } while (member.IsOverride && (member = InheritanceHelper.GetBaseMember(member)) != null); + if (!member.IsOverride) + break; + var baseMember = GetBaseMember(member); + if (baseMember == null) + break; + member = baseMember; + } } - internal static IAttribute GetAttribute(IMember member, KnownAttribute attributeType) + internal static IAttribute? GetAttribute(IMember member, KnownAttribute attributeType) { HashSet visitedMembers = new HashSet(); - do + while (true) { member = member.MemberDefinition; // it's sufficient to look at the definitions if (!visitedMembers.Add(member)) @@ -231,7 +237,13 @@ namespace ICSharpCode.Decompiler.TypeSystem var attr = member.GetAttribute(attributeType); if (attr != null) return attr; - } while (member.IsOverride && (member = InheritanceHelper.GetBaseMember(member)) != null); + if (!member.IsOverride) + break; + var baseMember = GetBaseMember(member); + if (baseMember == null) + break; + member = baseMember; + } return null; } #endregion diff --git a/ICSharpCode.ILSpyX/MermaidDiagrammer/Factory.FlatMembers.cs b/ICSharpCode.ILSpyX/MermaidDiagrammer/Factory.FlatMembers.cs index 398b6c96c..8ffc38132 100644 --- a/ICSharpCode.ILSpyX/MermaidDiagrammer/Factory.FlatMembers.cs +++ b/ICSharpCode.ILSpyX/MermaidDiagrammer/Factory.FlatMembers.cs @@ -40,12 +40,34 @@ namespace ICSharpCode.ILSpyX.MermaidDiagrammer && !properties.Any(p => f.ReturnType.Equals(p.ReturnType) && Regex.IsMatch(f.Name, "_?" + p.Name, RegexOptions.IgnoreCase | RegexOptions.Singleline | RegexOptions.NonBacktracking))).ToArray(); - private static IEnumerable GetMethods(ITypeDefinition type) => type.GetMethods(m => - !m.IsOperator && !m.IsCompilerGenerated() - && (m.DeclaringType == type // include methods if self-declared - /* but exclude methods declared by object and their overrides, if inherited */ - || (!m.DeclaringType.IsObject() - && (!m.IsOverride || !InheritanceHelper.GetBaseMember(m).DeclaringType.IsObject())))); + private static IEnumerable GetMethods(ITypeDefinition type) + { + return type.GetMethods(IsDeclaredMethod); + + bool IsDeclaredMethod(IMethod m) + { + if (m.IsOperator || m.IsCompilerGenerated()) + return false; + if (m.DeclaringTypeDefinition == type) + { + // include methods if self-declared + return true; + } + else + { + // but exclude methods declared by object and their overrides, if inherited + if (m.DeclaringType.IsObject()) + return false; + IMember? baseMember; + if (m.IsOverride && (baseMember = InheritanceHelper.GetBaseMember(m)) != null) + { + if (baseMember.DeclaringType.IsObject()) + return false; + } + } + return true; + } + } private string FormatMethod(IMethod method) {