ejball / XmlDocMarkdown

Generates Markdown from .NET XML documentation comments.
https://ejball.com/XmlDocMarkdown/
MIT License
102 stars 30 forks source link

InvalidOperationException handling function pointer (unmanaged delegate) type #132

Open jasongin opened 7 months ago

jasongin commented 7 months ago

C# supports function pointers. They may be rarely used, but I happen to be documenting a project that uses them.

The XmlDocMarkdown tool crashes when it encounters a function pointer type, like in this example:

public unsafe record struct ExampleUnmanagedDelegate(nint FunctionPointer)
{
    public ExampleUnmanagedDelegate(delegate* unmanaged[Cdecl]<int, void> functionPointer)
        : this((nint) functionPointer)
    {
    }
}

When built with .NET 8, it throws this exception:

System.InvalidOperationException : Unexpected type: System.Void(System.Int32)
   at XmlDocMarkdown.Core.XmlDocUtility.GetXmlDocTypePart(TypeInfo typeInfo) in /_/src/XmlDocMarkdown.Core/XmlDocUtility.cs:line 101  
   at XmlDocMarkdown.Core.XmlDocUtility.<>c.<GetXmlDocParameters>b__4_0(ParameterInfo x) in /_/src/XmlDocMarkdown.Core/XmlDocUtility.cs:line 117
   at System.Linq.Enumerable.SelectArrayIterator`2.MoveNext()      
   at System.String.Join(String separator, IEnumerable`1 values)   
   at XmlDocMarkdown.Core.XmlDocUtility.GetXmlDocParameters(ParameterInfo[] parameters) in /_/src/XmlDocMarkdown.Core/XmlDocUtility.cs:line 116
   at XmlDocMarkdown.Core.XmlDocUtility.GetXmlDocRef(MemberInfo memberInfo) in /_/src/XmlDocMarkdown.Core/XmlDocUtility.cs:line 21    
   at System.Linq.Lookup`2.Create(IEnumerable`1 source, Func`2 keySelector, IEqualityComparer`1 comparer)
   at System.Linq.GroupedEnumerable`2.GetEnumerator()
   at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
   at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector)    
   at XmlDocMarkdown.Core.MarkdownGenerator.DoGenerateOutput(Assembly assembly, XmlDocAssembly xmlDocAssembly)+MoveNext() in /_/src/XmlDocMarkdown.Core/MarkdownGenerator.cs:line 60
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) 
   at XmlDocMarkdown.Core.MarkdownGenerator.GenerateOutput(Assembly assembly, XmlDocAssembly xmlDocAssembly) in /_/src/XmlDocMarkdown.Core/MarkdownGenerator.cs:line 38
   at XmlDocMarkdown.Core.XmlDocMarkdownGenerator.Generate(XmlDocInput input, String outputPath, XmlDocMarkdownSettings settings) in /_/src/XmlDocMarkdown.Core/XmlDocMarkdownGenerator.cs:line 92       
   at XmlDocMarkdown.Core.XmlDocMarkdownApp.Run(IReadOnlyList`1 args) in /_/src/XmlDocMarkdown.Core/XmlDocMarkdownApp.cs:line 70

When built with .NET 6, it throws this exception:

System.InvalidOperationException: Sequence contains more than one element
   at System.Linq.ThrowHelper.ThrowMoreThanOneElementException()   
   at System.Linq.Enumerable.TryGetSingle[TSource](IEnumerable`1 source, Boolean& found)
   at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source) 
   at XmlDocMarkdown.Core.MarkdownGenerator.<>c.<DoGenerateOutput>b__52_4(IGrouping`2 x) in /_/src/XmlDocMarkdown.Core/MarkdownGenerator.cs:line 66
   at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
   at XmlDocMarkdown.Core.MarkdownGenerator.DoGenerateOutput(Assembly assembly, XmlDocAssembly xmlDocAssembly)+MoveNext() in /_/src/XmlDocMarkdown.Core/MarkdownGenerator.cs:line 60
   at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source) 
   at XmlDocMarkdown.Core.XmlDocMarkdownGenerator.Generate(XmlDocInput input, String outputPath, XmlDocMarkdownSettings settings) in /_/src/XmlDocMarkdown.Core/XmlDocMarkdownGenerator.cs:line 89       
   at XmlDocMarkdown.Core.XmlDocMarkdownApp.Run(IReadOnlyList`1 args) in /_/src/XmlDocMarkdown.Core/XmlDocMarkdownApp.cs:line 70

The "more than one element" is because in .NET 6 the function pointer is represented as System.IntPtr by the reflection APIs, so two constructors with the exact same signature are found.

In the XML file generated by the compiler, the <member> element name is M:ExampleAssembly.ExampleDelegate.#ctor(). That doesn't seem right since it omits the function pointer parameter, and I suppose it could conflict with an actual parameterless constructor. So it might not even be possible to document members that use function pointers.