<PackageReference Include="Namotion.Reflection" Version="3.4.2" />

XmlDocsExtensions

public static class XmlDocsExtensions
Provides extension methods for reading XML comments from reflected members.
using Namotion.Reflection.Infrastructure; using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Text; using System.Text.RegularExpressions; using System.Xml.Linq; namespace Namotion.Reflection { [System.Runtime.CompilerServices.NullableContext(1)] [System.Runtime.CompilerServices.Nullable(0)] public static class XmlDocsExtensions { [System.Runtime.CompilerServices.Nullable(new byte[] { 1, 1, 2 })] private static readonly ConcurrentDictionary<string, CachingXDocument> Cache = new ConcurrentDictionary<string, CachingXDocument>(StringComparer.OrdinalIgnoreCase); private static readonly char[] ToXmlDocsContentTrimChars = new char[2] { '!', ':' }; private static readonly char[] RemoveLineBreakWhiteSpacesTrimChars = new char[1] { '\n' }; private static readonly Regex LineBreakRegex = new Regex("(\\n[ \\t]*)", RegexOptions.Compiled); private static readonly Regex runtimeConfigRegex = new Regex("\"((.*?)((\\\\\\\\)|(////))(.*?))\"", RegexOptions.IgnoreCase); internal static void ClearCache() { Cache.Clear(); } public static string GetXmlDocsSummary(this CachedType type, [System.Runtime.CompilerServices.Nullable(2)] XmlDocsOptions options = null) { return type.Type.GetXmlDocsSummary(options); } public static string GetXmlDocsRemarks(this CachedType type, [System.Runtime.CompilerServices.Nullable(2)] XmlDocsOptions options = null) { return type.Type.GetXmlDocsRemarks(options); } public static string GetXmlDocsTag(this CachedType type, string tagName, [System.Runtime.CompilerServices.Nullable(2)] XmlDocsOptions options = null) { return type.Type.GetXmlDocsTag(tagName, options); } public static string GetXmlDocsSummary(this ContextualMemberInfo member, [System.Runtime.CompilerServices.Nullable(2)] XmlDocsOptions options = null) { return member.MemberInfo.GetXmlDocsSummary(options); } public static string GetXmlDocsRemarks(this ContextualMemberInfo member, [System.Runtime.CompilerServices.Nullable(2)] XmlDocsOptions options = null) { return member.MemberInfo.GetXmlDocsRemarks(options); } public static string GetXmlDocsTag(this ContextualMemberInfo member, string tagName, [System.Runtime.CompilerServices.Nullable(2)] XmlDocsOptions options = null) { return member.MemberInfo.GetXmlDocsTag(tagName, options); } public static string GetXmlDocs(this ContextualParameterInfo parameter, [System.Runtime.CompilerServices.Nullable(2)] XmlDocsOptions options = null) { return parameter.ParameterInfo.GetXmlDocs(options); } public static string GetXmlDocsSummary(this Type type, [System.Runtime.CompilerServices.Nullable(2)] XmlDocsOptions options = null) { return ((MemberInfo)type.GetTypeInfo()).GetXmlDocsTag("summary", options); } public static string GetXmlDocsRemarks(this Type type, [System.Runtime.CompilerServices.Nullable(2)] XmlDocsOptions options = null) { return ((MemberInfo)type.GetTypeInfo()).GetXmlDocsTag("remarks", options); } public static string GetXmlDocsTag(this Type type, string tagName, [System.Runtime.CompilerServices.Nullable(2)] XmlDocsOptions options = null) { return ((MemberInfo)type.GetTypeInfo()).GetXmlDocsTag(tagName, options); } public static string GetXmlDocsSummary(this MemberInfo member, [System.Runtime.CompilerServices.Nullable(2)] XmlDocsOptions options = null) { string xmlDocsTag = member.GetXmlDocsTag("summary", options); if (string.IsNullOrEmpty(xmlDocsTag)) { PropertyInfo propertyInfo = member as PropertyInfo; if ((object)propertyInfo != null) return propertyInfo.GetXmlDocsRecordPropertySummary(options); } return xmlDocsTag; } public static string GetXmlDocsRemarks(this MemberInfo member, [System.Runtime.CompilerServices.Nullable(2)] XmlDocsOptions options = null) { return member.GetXmlDocsTag("remarks", options); } [System.Runtime.CompilerServices.NullableContext(2)] public static XElement GetXmlDocsElement([System.Runtime.CompilerServices.Nullable(1)] this MemberInfo member, XmlDocsOptions options = null) { options = (options ?? XmlDocsOptions.Default); if (IsAssemblyIgnored(member.Module.Assembly.GetName(), options.ResolveExternalXmlDocs)) return null; string xmlDocsPath = GetXmlDocsPath(member.Module.Assembly, options); return member.GetXmlDocsElement(xmlDocsPath, options); } [return: System.Runtime.CompilerServices.Nullable(2)] public static XElement GetXmlDocsElement(this MemberInfo member, string pathToXmlFile, [System.Runtime.CompilerServices.Nullable(2)] XmlDocsOptions options = null) { try { options = (options ?? XmlDocsOptions.Default); CachingXDocument cachingXDocument = TryGetXmlDocsDocument(member.Module.Assembly.GetName(), pathToXmlFile, options.ResolveExternalXmlDocs); if (cachingXDocument != null) { XElement xmlDocsElement = member.GetXmlDocsElement(cachingXDocument); member.ReplaceInheritdocElements(xmlDocsElement, options); return xmlDocsElement; } return null; } catch { return null; } } public static string GetXmlDocsTag(this MemberInfo member, string tagName, [System.Runtime.CompilerServices.Nullable(2)] XmlDocsOptions options = null) { options = (options ?? XmlDocsOptions.Default); if ((object)member == null) throw new ArgumentNullException("member"); if (tagName == null) throw new ArgumentNullException("tagName"); if (IsAssemblyIgnored(member.Module.Assembly.GetName(), options.ResolveExternalXmlDocs)) return string.Empty; string xmlDocsPath = GetXmlDocsPath(member.Module.Assembly, options); return (member.GetXmlDocsElement(xmlDocsPath, options)?.Element(tagName)).ToXmlDocsContent(options); } public static string GetXmlDocsRecordPropertySummary(this PropertyInfo member, [System.Runtime.CompilerServices.Nullable(2)] XmlDocsOptions options = null) { options = (options ?? XmlDocsOptions.Default); if ((object)member == null) throw new ArgumentNullException("member"); if (IsAssemblyIgnored(member.Module.Assembly.GetName(), options.ResolveExternalXmlDocs)) return string.Empty; string xmlDocsPath = GetXmlDocsPath(member.Module.Assembly, options); XElement xmlDocsElement = member.DeclaringType.GetTypeInfo().GetXmlDocsElement(xmlDocsPath, options); object obj; if (xmlDocsElement == null) obj = null; else { IEnumerable<XElement> enumerable = xmlDocsElement.Elements("param"); obj = ((enumerable != null) ? enumerable.FirstOrDefault((XElement x) => x.Attribute("name")?.Value == member.Name) : null); } XElement xElement = (XElement)obj; if (xElement == null) return string.Empty; return xElement.ToXmlDocsContent(options); } public static string GetXmlDocs(this ParameterInfo parameter, [System.Runtime.CompilerServices.Nullable(2)] XmlDocsOptions options = null) { options = (options ?? XmlDocsOptions.Default); if (IsAssemblyIgnored(parameter.Member.Module.Assembly.GetName(), options.ResolveExternalXmlDocs)) return string.Empty; string xmlDocsPath = GetXmlDocsPath(parameter.Member.Module.Assembly, options); return parameter.GetXmlDocs(xmlDocsPath, options).ToXmlDocsContent(options); } [return: System.Runtime.CompilerServices.Nullable(2)] public static XElement GetXmlDocsElement(this ParameterInfo parameter, string pathToXmlFile, [System.Runtime.CompilerServices.Nullable(2)] XmlDocsOptions options = null) { try { if (pathToXmlFile != null) return parameter.GetXmlDocs(pathToXmlFile, options ?? XmlDocsOptions.Default); return null; } catch { return null; } } [System.Runtime.CompilerServices.NullableContext(2)] [return: System.Runtime.CompilerServices.Nullable(1)] public static string ToXmlDocsContent(this XElement element, XmlDocsOptions options = null) { if (options == null) options = XmlDocsOptions.Default; if (element != null) { StringBuilder stringBuilder = new StringBuilder(); foreach (XNode item in element.Nodes()) { XElement xElement = item as XElement; if (xElement != null) { if (xElement.Name == (XName)"see") { XAttribute xAttribute = xElement.Attribute("langword"); if (xAttribute != null) stringBuilder.Append(xAttribute.Value); else if (!string.IsNullOrEmpty(xElement.Value)) { stringBuilder.AppendFormattedElement(xElement, options.FormattingMode); } else { xAttribute = xElement.Attribute("cref"); if (xAttribute != null) { string s = xAttribute.Value.Trim(ToXmlDocsContentTrimChars).Trim(); s = s.FirstToken('('); s = s.LastToken('.'); stringBuilder.Append(s); } else { xAttribute = xElement.Attribute("href"); if (xAttribute != null) stringBuilder.Append(xAttribute.Value); } } } else if (xElement.Name == (XName)"paramref") { stringBuilder.Append(xElement.Attribute("name")?.Value ?? xElement.Value); } else { stringBuilder.AppendFormattedElement(xElement, options.FormattingMode); } } else { XText xText = item as XText; if (xText != null) stringBuilder.Append(xText.Value); else stringBuilder.Append(item); } } return RemoveLineBreakWhiteSpaces(stringBuilder.ToString()); } return string.Empty; } [return: System.Runtime.CompilerServices.Nullable(2)] private static XElement GetXmlDocs(this ParameterInfo parameter, [System.Runtime.CompilerServices.Nullable(2)] string pathToXmlFile, XmlDocsOptions options) { try { CachingXDocument cachingXDocument = TryGetXmlDocsDocument(parameter.Member.Module.Assembly.GetName(), pathToXmlFile, options.ResolveExternalXmlDocs); if (cachingXDocument != null) return parameter.GetXmlDocsElement(cachingXDocument, options); return null; } catch { return null; } } [System.Runtime.CompilerServices.NullableContext(2)] private static CachingXDocument TryGetXmlDocsDocument([System.Runtime.CompilerServices.Nullable(1)] AssemblyName assemblyName, string pathToXmlFile, bool resolveExternalXmlDocs) { string cacheKey = GetCacheKey(assemblyName.FullName, resolveExternalXmlDocs); if (Cache.TryGetValue(cacheKey, out CachingXDocument value)) return value; if (pathToXmlFile == null) return null; if (!DynamicApis.FileExists(pathToXmlFile)) { Cache[cacheKey] = null; return null; } value = new CachingXDocument(pathToXmlFile); Cache[cacheKey] = value; return value; } private static bool IsAssemblyIgnored(AssemblyName assemblyName, bool resolveExternalXmlDocs) { if (Cache.TryGetValue(GetCacheKey(assemblyName.FullName, resolveExternalXmlDocs), out CachingXDocument value)) return value == null; return false; } [return: System.Runtime.CompilerServices.Nullable(2)] private static XElement GetXmlDocsElement(this MemberInfo member, CachingXDocument xml) { string memberElementName = GetMemberElementName(member); return xml.GetXmlDocsElement(memberElementName); } [return: System.Runtime.CompilerServices.Nullable(2)] internal static XElement GetXmlDocsElement(this XDocument xml, string name) { return CachingXDocument.GetXmlDocsElement(xml, name); } [return: System.Runtime.CompilerServices.Nullable(2)] private static XElement GetXmlDocsElement(this ParameterInfo parameter, CachingXDocument xml, XmlDocsOptions options) { string memberElementName = GetMemberElementName(parameter.Member); XElement xmlDocsElement = xml.GetXmlDocsElement(memberElementName); if (xmlDocsElement != null) { parameter.Member.ReplaceInheritdocElements(xmlDocsElement, options); IEnumerable source = (!parameter.IsRetval && !string.IsNullOrEmpty(parameter.Name)) ? (from x in xmlDocsElement.Elements("param") where x.Attribute("name")?.Value == parameter.Name select x) : xmlDocsElement.Elements("returns"); return source.OfType<XElement>().FirstOrDefault(); } return null; } private static void ReplaceInheritdocElements(this MemberInfo member, [System.Runtime.CompilerServices.Nullable(2)] XElement element, XmlDocsOptions options) { if (element != null) { foreach (XElement item in element.Nodes().ToList().OfType<XElement>()) { if (item.Name.LocalName.ToLowerInvariant() == "inheritdoc") { bool flag = item.HasAttributes; if (flag) { MemberTypes memberType = member.MemberType; bool flag2 = (memberType == MemberTypes.Property || memberType == MemberTypes.TypeInfo) ? true : false; flag = flag2; } if (flag) member.ProcessInheritDocTypeElements(item, options); else { Type declaringType = member.DeclaringType; Type obj = ((object)declaringType != null) ? declaringType.GetTypeInfo().BaseType : null; MemberInfo memberInfo = ((object)obj != null) ? obj.GetTypeInfo().DeclaredMembers.SingleOrDefault((MemberInfo m) => m.Name == member.Name) : null; if (memberInfo != (MemberInfo)null) { XElement xmlDocsElement = memberInfo.GetXmlDocsElement(options); if (xmlDocsElement != null) { object[] content = xmlDocsElement.Nodes().OfType<object>().ToArray(); item.ReplaceWith(content); } else member.ProcessInheritdocInterfaceElements(item, options); } else member.ProcessInheritdocInterfaceElements(item, options); } } } } } private static void ProcessInheritdocInterfaceElements(this MemberInfo member, XElement child, XmlDocsOptions options) { Type declaringType = member.DeclaringType; if ((((object)declaringType != null) ? declaringType.GetTypeInfo().ImplementedInterfaces : null) != null) { foreach (Type implementedInterface in member.DeclaringType.GetTypeInfo().ImplementedInterfaces) { MemberInfo memberInfo = ((object)implementedInterface != null) ? implementedInterface.GetTypeInfo().DeclaredMembers.SingleOrDefault((MemberInfo m) => m.Name == member.Name) : null; if (memberInfo != (MemberInfo)null) { XElement xmlDocsElement = memberInfo.GetXmlDocsElement(options); if (xmlDocsElement != null) { object[] content = xmlDocsElement.Nodes().OfType<object>().ToArray(); child.ReplaceWith(content); } } } } } private static string RemoveLineBreakWhiteSpaces([System.Runtime.CompilerServices.Nullable(2)] string documentation) { if (string.IsNullOrEmpty(documentation)) return string.Empty; documentation = "\n" + documentation.Replace("\r", string.Empty).Trim(RemoveLineBreakWhiteSpacesTrimChars); string value = LineBreakRegex.Match(documentation).Value; documentation = documentation.Replace(value, "\n"); return documentation.Trim(RemoveLineBreakWhiteSpacesTrimChars); } internal static string GetMemberElementName(dynamic member) { if ((object)member == null) throw new ArgumentNullException("member"); MemberInfo memberInfo = member as MemberInfo; if ((object)memberInfo != null && memberInfo.DeclaringType != (Type)null && memberInfo.DeclaringType.GetTypeInfo().IsGenericType) { PropertyInfo propertyInfo = member as PropertyInfo; member = (((object)propertyInfo == null) ? member.Module.ResolveMember(member.MetadataToken) : propertyInfo.DeclaringType.GetRuntimeProperty(propertyInfo.Name)); } Type type = ((object)member).GetType(); string text; string text2; if (type.FullName.Contains(".Cecil.")) { text = (string)(type.IsAssignableToTypeName("TypeDefinition", TypeNameStyle.Name) ? member.FullName : (member.DeclaringType.FullName + "." + member.Name)); text = text.Replace("/", ".").Replace('+', '.'); text2 = ((!type.IsAssignableToTypeName("MethodDefinition", TypeNameStyle.Name)) ? (type.IsAssignableToTypeName("PropertyDefinition", TypeNameStyle.Name) ? "Property" : (type.IsAssignableToTypeName("FieldDefinition", TypeNameStyle.Name) ? "Field" : "TypeInfo")) : (text.EndsWith("..ctor") ? "Constructor" : "Method")); } else { Type type2 = member as Type; text = (string)(((object)type2 != null && !string.IsNullOrEmpty(type.FullName)) ? type2.FullName.FirstToken('[') : (((string)member.DeclaringType.FullName).FirstToken('[') + "." + member.Name)); text2 = (string)member.MemberType.ToString(); } if (text2 != null) { char c; Func<object, string> func; object enumerable2; IEnumerable<string> source; string text3; switch (text2.Length) { case 5: switch (text2[0]) { case 'E': break; case 'F': goto IL_0781; default: goto IL_0a11; } if (!(text2 == "Event")) break; c = 'E'; goto IL_0a21; case 8: switch (text2[0]) { case 'T': break; case 'P': goto IL_07c0; default: goto IL_0a11; } if (!(text2 == "TypeInfo")) break; goto IL_0a07; case 11: if (!(text2 == "Constructor")) break; text = text.Replace(".ctor", "#ctor"); goto IL_07e6; case 6: if (!(text2 == "Method")) break; goto IL_07e6; case 10: { if (!(text2 == "NestedType")) break; text = text.Replace('+', '.'); goto IL_0a07; } IL_0781: if (!(text2 == "Field")) break; c = 'F'; goto IL_0a21; IL_07e6: c = 'M'; func = delegate(dynamic p) { object obj; if (!(p.ParameterType.ContainsGenericParameter ? true : false)) obj = (string)p.ParameterType.FullName; else { object obj2 = ObjectExtensions.HasProperty(p.ParameterType, "GenericArguments"); obj = (((((dynamic)obj2) ? false : true) ? obj2 : ((dynamic)obj2 & (p.ParameterType.GenericArguments.Count > 0))) ? (((string)p.ParameterType.FullName).FirstToken('`') + "{" + string.Join(",", from object u in (ICollection)p.ParameterType.GenericArguments select "||" + u.Position) + "}") : ("||" + p.ParameterType.Position)); } return (string)obj; }; if (!(member is MethodBase)) { IEnumerable<string> enumerable = (IEnumerable<string>)Enumerable.Select<object, string>(member.Parameters, func); enumerable2 = enumerable; } else enumerable2 = ((MethodBase)member).GetParameters().Select(delegate(ParameterInfo x) { object text4 = x.ParameterType.FullName; if (text4 == null) { if (!((((dynamic)x.ParameterType).GenericTypeArguments.Length > 0) ? true : false)) return "||" + x.ParameterType.GenericParameterPosition.ToString(); text4 = x.ParameterType.Namespace + "." + x.ParameterType.Name.FirstToken('`') + "{" + string.Join(",", ((Type[])((dynamic)x.ParameterType).GenericTypeArguments).Select(delegate(Type a) { if (!a.IsGenericParameter) return a.Namespace + "." + a.Name + "[[||0]]"; return "||" + a.GenericParameterPosition.ToString(); })) + "}"; } return (string)text4; }); source = (IEnumerable<string>)enumerable2; text3 = string.Join(",", (from x in source select Regex.Replace(x, "(`[0-9]+)|(, .*?PublicKeyToken=[0-9a-z]*)", string.Empty).Replace("],[", ",").Replace("||", "`") .Replace("[[", "{") .Replace("[]]]", "[]}") .Replace("]]", "}")).ToArray()); if (!string.IsNullOrEmpty(text3)) text = text + "(" + text3 + ")"; goto IL_0a21; IL_0a21: return string.Format("{0}:{1}", c, text.Replace("+", ".")); IL_07c0: if (!(text2 == "Property")) break; c = 'P'; goto IL_0a21; IL_0a07: c = 'T'; goto IL_0a21; } } goto IL_0a11; IL_0a11: throw new ArgumentException("Unknown member type.", "member"); } [System.Runtime.CompilerServices.NullableContext(2)] public static string GetXmlDocsPath(Assembly assembly, [System.Runtime.CompilerServices.Nullable(1)] XmlDocsOptions options) { try { if (!(assembly == (Assembly)null)) { AssemblyName name = assembly.GetName(); if (!string.IsNullOrEmpty(name.Name)) { string cacheKey = GetCacheKey(name.FullName, options.ResolveExternalXmlDocs); if (!Cache.ContainsKey(cacheKey)) try { string text; if (!string.IsNullOrEmpty(assembly.Location)) { text = DynamicApis.PathCombine(DynamicApis.PathGetDirectoryName(assembly.Location), name.Name + ".xml"); if (DynamicApis.FileExists(text)) return text; } if (assembly.HasProperty("CodeBase")) { string codeBase = assembly.CodeBase; if (!string.IsNullOrEmpty(codeBase)) { text = DynamicApis.PathCombine(DynamicApis.PathGetDirectoryName(codeBase.Replace("file:///", string.Empty)), name.Name + ".xml").Replace("file:\\", string.Empty); if (DynamicApis.FileExists(text)) return text; } } Type type = Type.GetType("System.AppDomain"); object obj = ((object)type == null) ? null : type.GetRuntimeProperty("CurrentDomain")?.GetValue(null); if (obj != null && obj.HasProperty("BaseDirectory")) { string text2 = obj.TryGetPropertyValue("BaseDirectory", ""); if (!string.IsNullOrEmpty(text2)) { text = DynamicApis.PathCombine(text2, name.Name + ".xml"); if (DynamicApis.FileExists(text)) return text; text = DynamicApis.PathCombine(text2, "bin/" + name.Name + ".xml"); if (DynamicApis.FileExists(text)) return text; } } string path = DynamicApis.DirectoryGetCurrentDirectory(); text = DynamicApis.PathCombine(path, assembly.GetName().Name + ".xml"); if (!DynamicApis.FileExists(text)) { text = DynamicApis.PathCombine(path, "bin/" + assembly.GetName().Name + ".xml"); if (!DynamicApis.FileExists(text)) { if (options.ResolveExternalXmlDocs) { text = GetXmlDocsPathFromOSXNuGetCache(assembly, name); if (DynamicApis.FileExists(text)) return text; dynamic val = typeof(Assembly).GetRuntimeMethod("GetExecutingAssembly", new Type[0])?.Invoke(null, new object[0]); if ((!string.IsNullOrEmpty(val?.Location))) { text = GetXmlDocsPathFromNuGetCacheOrDotNetSdk(DynamicApis.PathGetDirectoryName((string)val.Location), name); if (text != null && DynamicApis.FileExists(text)) return text; } } Cache[cacheKey] = null; return null; } return text; } return text; } catch { Cache[cacheKey] = null; return null; } return null; } return null; } return null; } catch { return null; } } private static void ProcessInheritDocTypeElements(this MemberInfo member, XElement child, XmlDocsOptions options) { string text = child.Attribute("cref")?.Value; if (text != null) { MemberInfo referencedType = null; Assembly assembly = null; string text2 = (text[0] != 'P') ? Regex.Match(text, "[A-Z]:(?<FullName>(?<Namespace>[a-zA-Z.]*)\\.(?<TypeName>[a-zA-Z]*))").Groups["FullName"].Value : Regex.Match(text, "(?<FullName>(?<FullTypeName>(?<AssemblyName>[a-zA-Z.]*)\\.(?<TypeName>[a-zA-Z]*))\\.(?<MemberName>[a-zA-Z]*))").Groups["FullTypeName"].Value; if ((object)assembly == null && text2 != null) { assembly = member.Module.Assembly; referencedType = assembly.GetType(text2); if ((object)referencedType == null) { Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly2 in assemblies) { if (text.Contains(assembly2.GetName().Name)) { referencedType = GetTypeByXmlDocTypeName(text2, assembly2); if (referencedType != (MemberInfo)null) { assembly = assembly2; break; } } } } } if ((object)referencedType != null && (object)assembly != null) { XElement xElement = TryGetXmlDocsDocument(assembly.GetName(), GetXmlDocsPath(assembly, options), true)?.GetXmlDocsElement(text); if (xElement == null && referencedType.MemberType == MemberTypes.Property) { string xmlDocsPath = GetXmlDocsPath(member.Module.Assembly, options); if (xmlDocsPath != null) { XElement xmlDocsElement = referencedType.DeclaringType.GetTypeInfo().GetXmlDocsElement(xmlDocsPath, options); object obj; if (xmlDocsElement == null) obj = null; else { IEnumerable<XElement> enumerable = xmlDocsElement.Elements("param"); obj = ((enumerable != null) ? enumerable.FirstOrDefault((XElement x) => x.Attribute("name")?.Value == referencedType.Name) : null); } xElement = (XElement)obj; child.ReplaceWith(xElement); } } else if (xElement != null) { object[] content = xElement.Nodes().OfType<object>().ToArray(); child.ReplaceWith(content); } } } } [return: System.Runtime.CompilerServices.Nullable(2)] private static Type GetTypeByXmlDocTypeName(string xmlDocTypeName, Assembly assembly) { (from type in assembly.GetTypes() select new KeyValuePair<string, Type>(NormalizeTypeName(type.FullName), type)).ToDictionary((KeyValuePair<string, Type> x) => x.Key, (KeyValuePair<string, Type> x) => x.Value).TryGetValue(NormalizeTypeName(xmlDocTypeName), out Type value); return value; } private static string NormalizeTypeName(string typeName) { return typeName.Replace(".", string.Empty).Replace("+", string.Empty); } [System.Runtime.CompilerServices.NullableContext(2)] private static string GetXmlDocsPathFromOSXNuGetCache(dynamic assembly, [System.Runtime.CompilerServices.Nullable(1)] AssemblyName assemblyName) { return null; } [return: System.Runtime.CompilerServices.Nullable(2)] private static string GetXmlDocsPathFromNuGetCacheOrDotNetSdk(string assemblyDirectory, AssemblyName assemblyName) { string[] source = DynamicApis.DirectoryGetFiles(assemblyDirectory, "*.runtimeconfig.dev.json"); if (source.Any()) try { string input = DynamicApis.FileReadAllText(source.First()); MatchCollection matchCollection = runtimeConfigRegex.Matches(input); if (matchCollection.Count > 0) { foreach (Match item in matchCollection) { string text = item.Groups[1].Value.Replace("\\\\", "/").Replace("//", "/").Replace("\\|arch|", "") .Replace("\\|tfm|", "") .Replace("/|arch|", "") .Replace("/|tfm|", ""); if (DynamicApis.DirectoryExists(text)) try { string text2 = DynamicApis.PathCombine(text, assemblyName.Name + "/" + assemblyName.Version.ToString(3)); if (DynamicApis.DirectoryExists(text2)) { string text3 = (from f in DynamicApis.DirectoryGetAllFiles(text2, assemblyName.Name + ".xml") orderby f descending select f).FirstOrDefault(); if (text3 != null) return text3; } } catch { } if (text.Contains("/dotnet/sdk")) { string search; while ((text = DynamicApis.PathGetDirectoryName(text).Replace('\\', '/')) != null) { if (text.EndsWith("/dotnet")) { try { text = DynamicApis.PathCombine(text, "packs"); search = "/" + assemblyName.Version.ToString(2); string text4 = (from f in DynamicApis.DirectoryGetAllFiles(text, assemblyName.Name + ".xml") where f.Replace('\\', '/').Contains(search) orderby f descending select f).FirstOrDefault(); if (text4 != null) return text4; } catch { } break; } } } } } } catch { } string text5 = DynamicApis.PathCombine(assemblyDirectory, "../../obj/project.nuget.cache"); if (DynamicApis.FileExists(text5)) return GetXmlDocsPathFromNuGetCacheFile(text5, assemblyName); text5 = DynamicApis.PathCombine(assemblyDirectory, "../../../obj/project.nuget.cache"); if (DynamicApis.FileExists(text5)) return GetXmlDocsPathFromNuGetCacheFile(text5, assemblyName); text5 = DynamicApis.PathCombine(assemblyDirectory.Replace("/bin/", "/obj/"), "../project.nuget.cache"); if (DynamicApis.FileExists(text5)) return GetXmlDocsPathFromNuGetCacheFile(text5, assemblyName); return null; } [return: System.Runtime.CompilerServices.Nullable(2)] private static string GetXmlDocsPathFromNuGetCacheFile(string nuGetCacheFile, AssemblyName assemblyName) { try { MatchCollection matchCollection = Regex.Matches(DynamicApis.FileReadAllText(nuGetCacheFile), "\"((.*?)" + assemblyName.Name + "((\\\\\\\\)|/).*?)((\\\\\\\\)|/)(.*?)\"", RegexOptions.IgnoreCase); if (matchCollection.Count > 0) { string[] source = DynamicApis.DirectoryGetAllFiles(matchCollection[0].Groups[1].Value.Replace("\\\\", "\\").Replace("//", "/"), assemblyName.Name + ".xml"); if (source.Any()) return source.Last(); } return null; } catch { return null; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] private static string GetCacheKey(string assemblyFullName, bool resolveExternalXmlDocs) { return $"{assemblyFullName}""{resolveExternalXmlDocs}"; } } }