@@ -81,14 +81,33 @@ private IEnumerable<MethodDeclarationSyntax> DeclareFriendlyOverloads(MethodDefi
8181 }
8282 }
8383
84- bool useSpansForPointers = this . canUseSpan ;
85- foreach ( MethodDeclarationSyntax method in this . DeclareFriendlyOverload ( methodDefinition , externMethodDeclaration , declaringTypeName , overloadOf , helperMethodsAdded , avoidWinmdRootAlias , useSpansForPointers , omitOptionalParams : false ) )
84+ bool improvePointersToSpansAndRefs = this . canUseSpan ;
85+ FriendlyMethodBookkeeping bookkeeping = new ( ) ;
86+ foreach ( MethodDeclarationSyntax method in this . DeclareFriendlyOverload ( methodDefinition , externMethodDeclaration , declaringTypeName , overloadOf , helperMethodsAdded , avoidWinmdRootAlias , improvePointersToSpansAndRefs , omitOptionalParams : false , bookkeeping ) )
8687 {
8788 yield return method ;
8889 }
90+
91+ if ( this . Options . FriendlyOverloads . IncludePointerOverloads && improvePointersToSpansAndRefs && bookkeeping . NumSpanByteParameters > 0 )
92+ {
93+ // If we could use Span and _did_ use span Span and the pointer overloads were requested, then Generate overloads that use pointer types instead of Span<byte>/ReadOnlySpan<byte>.
94+ foreach ( MethodDeclarationSyntax method in this . DeclareFriendlyOverload ( methodDefinition , externMethodDeclaration , declaringTypeName , overloadOf , helperMethodsAdded , avoidWinmdRootAlias , improvePointersToSpansAndRefs : false , omitOptionalParams : false ) )
95+ {
96+ yield return method ;
97+ }
98+ }
8999 }
90100
91- private IEnumerable < MethodDeclarationSyntax > DeclareFriendlyOverload ( MethodDefinition methodDefinition , MethodDeclarationSyntax externMethodDeclaration , NameSyntax declaringTypeName , FriendlyOverloadOf overloadOf , HashSet < string > helperMethodsAdded , bool avoidWinmdRootAlias , bool useSpansForPointers , bool omitOptionalParams )
101+ private IEnumerable < MethodDeclarationSyntax > DeclareFriendlyOverload (
102+ MethodDefinition methodDefinition ,
103+ MethodDeclarationSyntax externMethodDeclaration ,
104+ NameSyntax declaringTypeName ,
105+ FriendlyOverloadOf overloadOf ,
106+ HashSet < string > helperMethodsAdded ,
107+ bool avoidWinmdRootAlias ,
108+ bool improvePointersToSpansAndRefs ,
109+ bool omitOptionalParams ,
110+ FriendlyMethodBookkeeping ? bookkeeping = null )
92111 {
93112#pragma warning disable SA1114 // Parameter list should follow declaration
94113 bool isReleaseMethod = this . MetadataIndex . ReleaseMethods . Contains ( externMethodDeclaration . Identifier . ValueText ) ;
@@ -123,6 +142,7 @@ private IEnumerable<MethodDeclarationSyntax> DeclareFriendlyOverload(MethodDefin
123142 bool minorSignatureChange = false ; // Did the signature change but not enough that overload resolution would be confused?
124143 List < Parameter > ? countOfBytesStructParameters = null ;
125144 int numOptionalParams = 0 ;
145+ int numSpanByteParameters = 0 ;
126146
127147 foreach ( ParameterHandle paramHandle in methodDefinition . GetParameters ( ) )
128148 {
@@ -177,12 +197,18 @@ private IEnumerable<MethodDeclarationSyntax> DeclareFriendlyOverload(MethodDefin
177197 // If there's no MemorySize attribute, we may still need to keep this parameter as a pointer if it's a struct with a flexible array.
178198 mustRemainAsPointer = parameterTypeInfo is PointerTypeHandleInfo { ElementType : HandleTypeHandleInfo pointedElement } && pointedElement . Generator . IsStructWithFlexibleArray ( pointedElement ) ;
179199 }
180- else if ( ! useSpansForPointers )
200+ else if ( ! improvePointersToSpansAndRefs )
181201 {
182202 // If we are generating the overload with pointers for memory sized params then also force them to pointers.
183203 mustRemainAsPointer = true ;
184204 }
185205
206+ // For compat with how out/ref parameters used to be generated, leave out/ref parameters as pointers if we're not tring to improve them.
207+ if ( isOptional && isOut && ! isComOutPtr && ! improvePointersToSpansAndRefs )
208+ {
209+ mustRemainAsPointer = true ;
210+ }
211+
186212 IdentifierNameSyntax origName = IdentifierName ( externParam . Identifier . ValueText ) ;
187213
188214 bool isArray = false ;
@@ -220,7 +246,7 @@ private IEnumerable<MethodDeclarationSyntax> DeclareFriendlyOverload(MethodDefin
220246 }
221247
222248 bool projectAsSpanBytes = false ;
223- if ( useSpansForPointers && IsVoidPtrOrPtrPtr ( externParam . Type ) )
249+ if ( improvePointersToSpansAndRefs && IsVoidPtrOrPtrPtr ( externParam . Type ) )
224250 {
225251 // if it's memory-sized project as Span<byte>
226252 if ( memorySize is not null )
@@ -401,7 +427,7 @@ private IEnumerable<MethodDeclarationSyntax> DeclareFriendlyOverload(MethodDefin
401427 arguments [ param . SequenceNumber - 1 ] = Argument ( typeDefHandleName ) ;
402428 }
403429 else if ( ( externParam . Type is PointerTypeSyntax { ElementType : TypeSyntax ptrElementType }
404- && ( ! IsVoid ( ptrElementType ) || ( useSpansForPointers && isArray ) )
430+ && ( ! IsVoid ( ptrElementType ) || ( improvePointersToSpansAndRefs && isArray ) )
405431 && ! this . IsInterface ( parameterTypeInfo ) ) ||
406432 externParam . Type is ArrayTypeSyntax )
407433 {
@@ -476,6 +502,11 @@ private IEnumerable<MethodDeclarationSyntax> DeclareFriendlyOverload(MethodDefin
476502 fixedBlocks . Add ( VariableDeclaration ( PointerType ( elementType ) ) . AddVariables (
477503 VariableDeclarator ( localName . Identifier ) . WithInitializer ( EqualsValueClause ( origName ) ) ) ) ;
478504 arguments [ param . SequenceNumber - 1 ] = projectAsSpanBytes ? Argument ( CastExpression ( externParam . Type , localName ) ) : Argument ( localName ) ;
505+
506+ if ( projectAsSpanBytes )
507+ {
508+ numSpanByteParameters ++ ;
509+ }
479510 }
480511 else if ( isNullTerminated && isConst && parameters [ param . SequenceNumber - 1 ] . Type is PointerTypeSyntax { ElementType : PredefinedTypeSyntax { Keyword : { RawKind : ( int ) SyntaxKind . CharKeyword } } } )
481512 {
@@ -826,6 +857,7 @@ bool TryHandleCountParam(TypeSyntax elementType, bool nullableSource)
826857 fixedBlocks . Add ( VariableDeclaration ( PointerType ( byteSyntax ) ) . AddVariables (
827858 VariableDeclarator ( localName . Identifier ) . WithInitializer ( EqualsValueClause ( origName ) ) ) ) ;
828859 arguments [ param . SequenceNumber - 1 ] = Argument ( CastExpression ( externParam . Type , localName ) ) ;
860+ numSpanByteParameters ++ ;
829861 }
830862 else
831863 {
@@ -898,7 +930,7 @@ bool TryHandleCountParam(TypeSyntax elementType, bool nullableSource)
898930 : IdentifierName ( externMethodDeclaration . Identifier ) ;
899931 SyntaxTrivia leadingTrivia = Trivia (
900932 DocumentationCommentTrivia ( SyntaxKind . SingleLineDocumentationCommentTrivia ) . AddContent (
901- XmlText ( "/// " ) ,
933+ XmlText ( $ "/// ") ,
902934 XmlEmptyElement ( "inheritdoc" ) . AddAttributes ( XmlCrefAttribute ( NameMemberCref ( docRefExternName , ToCref ( externMethodDeclaration . ParameterList ) ) ) ) ,
903935 XmlText ( ) . AddTextTokens ( XmlTextNewLine ( "\n " , continueXmlDocumentationComment : false ) ) ) ) ;
904936 InvocationExpressionSyntax externInvocation = InvocationExpression (
@@ -1003,12 +1035,17 @@ bool TryHandleCountParam(TypeSyntax elementType, bool nullableSource)
10031035 friendlyDeclaration = friendlyDeclaration
10041036 . WithLeadingTrivia ( leadingTrivia ) ;
10051037
1038+ if ( bookkeeping is not null )
1039+ {
1040+ bookkeeping . NumSpanByteParameters = numSpanByteParameters ;
1041+ }
1042+
10061043 yield return friendlyDeclaration ;
10071044
10081045 // We generated the main overload, but now see if we should generate another helper for things like SHGetFileInfo where
10091046 // there is a parameter that's sized in bytes and for convenience you want to just use the struct and not cast between Span<byte>.
10101047 // To avoid an explosion of overloads, just do this if there's one parameter of this kind.
1011- if ( useSpansForPointers && countOfBytesStructParameters ? . Count == 1 )
1048+ if ( improvePointersToSpansAndRefs && countOfBytesStructParameters ? . Count == 1 )
10121049 {
10131050 MethodDeclarationSyntax ? structOverload = this . DeclareStructCountOfBytesFriendlyOverload ( externMethodDeclaration , countOfBytesStructParameters , friendlyDeclaration ) ;
10141051 if ( structOverload is not null )
@@ -1018,10 +1055,10 @@ bool TryHandleCountParam(TypeSyntax elementType, bool nullableSource)
10181055 }
10191056 }
10201057
1021- if ( numOptionalParams > 0 && ! omitOptionalParams )
1058+ if ( numOptionalParams > 0 && ! omitOptionalParams && improvePointersToSpansAndRefs )
10221059 {
10231060 // Generate overloads for optional parameters.
1024- foreach ( MethodDeclarationSyntax method in this . DeclareFriendlyOverload ( methodDefinition , externMethodDeclaration , declaringTypeName , overloadOf , helperMethodsAdded , avoidWinmdRootAlias , useSpansForPointers , omitOptionalParams : true ) )
1061+ foreach ( MethodDeclarationSyntax method in this . DeclareFriendlyOverload ( methodDefinition , externMethodDeclaration , declaringTypeName , overloadOf , helperMethodsAdded , avoidWinmdRootAlias , improvePointersToSpansAndRefs , omitOptionalParams : true ) )
10251062 {
10261063 yield return method ;
10271064 }
@@ -1163,4 +1200,9 @@ bool TryHandleCountParam(TypeSyntax elementType, bool nullableSource)
11631200
11641201 return helper ;
11651202 }
1203+
1204+ private class FriendlyMethodBookkeeping
1205+ {
1206+ public int NumSpanByteParameters { get ; set ; } = 0 ;
1207+ }
11661208}
0 commit comments