@@ -408,44 +408,111 @@ public override async Task<CSharpSyntaxNode> VisitSimpleArgument(VBasic.Syntax.S
408408 SyntaxToken token = default ( SyntaxToken ) ;
409409 string argName = null ;
410410 RefKind refKind = RefKind . None ;
411+ AdditionalLocal local = null ;
411412 if ( symbol is IMethodSymbol methodSymbol ) {
412413 var parameters = ( CommonConversions . GetCsOriginalSymbolOrNull ( methodSymbol . OriginalDefinition ) ?? methodSymbol ) . GetParameters ( ) ;
413- var parameter = ! node . IsNamed ? parameters . ElementAtOrDefault ( argList . Arguments . IndexOf ( node ) ) : parameters . FirstOrDefault ( p => p . Name . Equals ( node . NameColonEquals . Name . Identifier . Text , StringComparison . OrdinalIgnoreCase ) ) ;
414- if ( parameter != null ) {
415- refKind = parameter . RefKind ;
416- argName = parameter . Name ;
417- }
418- switch ( refKind ) {
419- case RefKind . None :
420- token = default ( SyntaxToken ) ;
421- break ;
422- case RefKind . Ref :
423- token = SyntaxFactory . Token ( SyntaxKind . RefKeyword ) ;
424- break ;
425- case RefKind . Out :
426- token = SyntaxFactory . Token ( SyntaxKind . OutKeyword ) ;
427- break ;
428- default :
429- throw new ArgumentOutOfRangeException ( ) ;
414+ var refType = GetRefType ( node , argList , parameters , out argName , out refKind ) ;
415+ var expression = CommonConversions . TypeConversionAnalyzer . AddExplicitConversion ( node . Expression , ( ExpressionSyntax ) await node . Expression . AcceptAsync ( TriviaConvertingExpressionVisitor ) , defaultToCast : refKind != RefKind . None ) ;
416+
417+ if ( refType == RefType . Variable ) {
418+ var expressionTypeInfo = _semanticModel . GetTypeInfo ( node . Expression ) ;
419+ bool useVar = expressionTypeInfo . Type ? . Equals ( expressionTypeInfo . ConvertedType ) == true && ! CommonConversions . ShouldPreferExplicitType ( node . Expression , expressionTypeInfo . ConvertedType , out var _ ) ;
420+ var typeSyntax = CommonConversions . GetTypeSyntax ( expressionTypeInfo . ConvertedType , useVar ) ;
421+ string prefix = $ "arg{ argName } ";
422+ local = _additionalLocals . AddAdditionalLocal ( new AdditionalLocal ( prefix , expression , typeSyntax ) ) ;
423+ } else if ( refType == RefType . Property && invocation is VBSyntax . InvocationExpressionSyntax ies ) {
424+ //TODO: Don't break for multiple byref args in one method
425+ var expressionTypeInfo = _semanticModel . GetTypeInfo ( node . Expression ) ;
426+ bool useVar = expressionTypeInfo . Type ? . Equals ( expressionTypeInfo . ConvertedType ) == true && ! CommonConversions . ShouldPreferExplicitType ( node . Expression , expressionTypeInfo . ConvertedType , out var _ ) ;
427+ var typeSyntax = CommonConversions . GetTypeSyntax ( expressionTypeInfo . ConvertedType , useVar ) ;
428+ var ( localFuncName , functionDecl ) = CreateLocalByRefFunction ( ies , argName , expression , methodSymbol ) ;
429+ local = _additionalLocals . AddAdditionalLocal ( new AdditionalLocal ( localFuncName , expression , typeSyntax ) ) ;
430430 }
431431 }
432- var expression = CommonConversions . TypeConversionAnalyzer . AddExplicitConversion ( node . Expression , ( ExpressionSyntax ) await node . Expression . AcceptAsync ( TriviaConvertingExpressionVisitor ) , defaultToCast : refKind != RefKind . None ) ;
433- AdditionalLocal local = null ;
434- if ( refKind != RefKind . None && NeedsVariableForArgument ( node ) ) {
435- var expressionTypeInfo = _semanticModel . GetTypeInfo ( node . Expression ) ;
436- bool useVar = expressionTypeInfo . Type ? . Equals ( expressionTypeInfo . ConvertedType ) == true && ! CommonConversions . ShouldPreferExplicitType ( node . Expression , expressionTypeInfo . ConvertedType , out var _ ) ;
437- var typeSyntax = CommonConversions . GetTypeSyntax ( expressionTypeInfo . ConvertedType , useVar ) ;
438- string prefix = $ "arg{ argName } ";
439- local = _additionalLocals . AddAdditionalLocal ( new AdditionalLocal ( prefix , expression , typeSyntax ) ) ;
432+
433+ switch ( refKind ) {
434+ case RefKind . None :
435+ token = default ( SyntaxToken ) ;
436+ break ;
437+ case RefKind . Ref :
438+ token = SyntaxFactory . Token ( SyntaxKind . RefKeyword ) ;
439+ break ;
440+ case RefKind . Out :
441+ token = SyntaxFactory . Token ( SyntaxKind . OutKeyword ) ;
442+ break ;
443+ default :
444+ throw new ArgumentOutOfRangeException ( ) ;
440445 }
441- var nameColon = node . IsNamed ? SyntaxFactory . NameColon ( ( IdentifierNameSyntax ) await node . NameColonEquals . Name . AcceptAsync ( TriviaConvertingExpressionVisitor ) ) : null ;
446+ var simpleExpression = await ConvertArgExpression ( node , refKind ) ;
447+
448+ var nameColon = node . IsNamed ? SyntaxFactory . NameColon ( ( IdentifierNameSyntax ) await node . NameColonEquals . Name . AcceptAsync ( TriviaConvertingExpressionVisitor ) ) : null ;
442449 if ( local == null ) {
443- return SyntaxFactory . Argument ( nameColon , token , expression ) ;
450+ return SyntaxFactory . Argument ( nameColon , token , simpleExpression ) ;
444451 } else {
445452 return SyntaxFactory . Argument ( nameColon , token , local . IdentifierName ) ;
446453 }
447454 }
448455
456+ private async Task < ExpressionSyntax > ConvertArgExpression ( VBSyntax . SimpleArgumentSyntax node , RefKind refKind )
457+ {
458+ return CommonConversions . TypeConversionAnalyzer . AddExplicitConversion ( node . Expression , ( ExpressionSyntax ) await node . Expression . AcceptAsync ( TriviaConvertingExpressionVisitor ) , defaultToCast : refKind != RefKind . None ) ;
459+ }
460+
461+ private RefType GetRefType ( VBSyntax . SimpleArgumentSyntax node , VBSyntax . ArgumentListSyntax argList , System . Collections . Immutable . ImmutableArray < IParameterSymbol > parameters , out string argName , out RefKind refKind )
462+ {
463+ var parameter = ! node . IsNamed ? parameters . ElementAtOrDefault ( argList . Arguments . IndexOf ( node ) ) : parameters . FirstOrDefault ( p => p . Name . Equals ( node . NameColonEquals . Name . Identifier . Text , StringComparison . OrdinalIgnoreCase ) ) ;
464+ if ( parameter != null ) {
465+ refKind = parameter . RefKind ;
466+ argName = parameter . Name ;
467+ } else {
468+ refKind = RefKind . None ;
469+ argName = null ;
470+ }
471+ return NeedsVariableForArgument ( node , refKind ) ;
472+ }
473+
474+ private async Task < ( string Id , StatementSyntax FunctionDeclaration ) > CreateLocalByRefFunction ( VBSyntax . InvocationExpressionSyntax invocation , string argName , ExpressionSyntax expression , IMethodSymbol invocationSymbol )
475+ {
476+ var originalArgsWithRefTypes = invocation . ArgumentList . Arguments
477+ . Select ( a => ( Arg : ( VBSyntax . SimpleArgumentSyntax ) a , RefType : GetRefType ( ( VBSyntax . SimpleArgumentSyntax ) a , invocation . ArgumentList , invocationSymbol . Parameters , out var argName , out var refKind ) , Name : argName , RefKind : refKind ) )
478+ . ToArray ( ) ;
479+
480+ var argsForCallInLocalFunction = ( await originalArgsWithRefTypes . SelectAsync ( async a => a . RefType switch {
481+ RefType . Inline => await ConvertArgExpression ( a . Arg , a . RefKind ) ,
482+ _ => SyntaxFactory . IdentifierName ( a . Name )
483+ } ) ) . Select ( SyntaxFactory . Argument ) . ToArray ( ) ;
484+ var argList = SyntaxFactory . ArgumentList ( GetAdditionalRequiredArgs ( invocationSymbol , argsForCallInLocalFunction ) ) ;
485+ var tmpArgName = $ "arg{ argName } ";
486+ var tempArg = SyntaxFactory . IdentifierName ( tmpArgName ) ;
487+ var localFuncName = $ "local{ invocationSymbol . Name } ";
488+ const string retVariableName = "ret" ;
489+ var localFuncId = SyntaxFactory . IdentifierName ( localFuncName ) ;
490+ var declareTemp = CommonConversions . CreateLocalVariableDeclarationAndAssignment ( tmpArgName , expression ) ;
491+ // Need essentially the original invocation with any byref args swapped out for temporaries
492+ var callAndStoreResult = CommonConversions . CreateLocalVariableDeclarationAndAssignment ( retVariableName ,
493+ SyntaxFactory . InvocationExpression ( expression ,
494+ ) ;
495+ var block = SyntaxFactory. Block(
496+ declareTemp ,
497+ callAndStoreResult ,
498+ AssignStmt ( expression , tempArg ) ,
499+ SyntaxFactory . ReturnStatement ( SyntaxFactory . IdentifierName ( retVariableName ) )
500+ ) ;
501+ var localfunction = SyntaxFactory . LocalFunctionStatement ( CommonConversions . GetTypeSyntax ( invocationSymbol . ReturnType ) ,
502+ localFuncId . Identifier ) . WithBody ( block ) ;
503+ return ( localFuncName , localfunction ) ;
504+ }
505+
506+ private static StatementSyntax AssignStmt ( ExpressionSyntax left , IdentifierNameSyntax right )
507+ {
508+ return SyntaxFactory . ExpressionStatement ( Assign ( left , right ) ) ;
509+ }
510+
511+ private static AssignmentExpressionSyntax Assign ( ExpressionSyntax left , IdentifierNameSyntax right )
512+ {
513+ return SyntaxFactory . AssignmentExpression ( SyntaxKind . SimpleAssignmentExpression , left , right ) ;
514+ }
515+
449516 public override async Task < CSharpSyntaxNode > VisitNameOfExpression ( VBasic . Syntax . NameOfExpressionSyntax node )
450517 {
451518 return SyntaxFactory . InvocationExpression ( NameOf ( ) , SyntaxFactory . ArgumentList ( SyntaxFactory . SingletonSeparatedList < ArgumentSyntax > ( SyntaxFactory . Argument ( ( ExpressionSyntax ) await node . Argument . AcceptAsync ( TriviaConvertingExpressionVisitor ) ) ) ) ) ;
@@ -1291,7 +1358,7 @@ private ArgumentSyntax CreateOptionalRefArg(IParameterSymbol p)
12911358 return ( ArgumentSyntax ) CommonConversions . CsSyntaxGenerator . Argument ( p . Name , p . RefKind , local . IdentifierName ) ;
12921359 }
12931360
1294- private bool NeedsVariableForArgument ( VBasic . Syntax . SimpleArgumentSyntax node )
1361+ private RefType NeedsVariableForArgument ( VBasic . Syntax . SimpleArgumentSyntax node , RefKind refKind )
12951362 {
12961363 bool isIdentifier = node . Expression is VBasic . Syntax . IdentifierNameSyntax ;
12971364 bool isMemberAccess = node . Expression is VBasic . Syntax . MemberAccessExpressionSyntax ;
@@ -1303,7 +1370,16 @@ private bool NeedsVariableForArgument(VBasic.Syntax.SimpleArgumentSyntax node)
13031370 var typeInfo = _semanticModel . GetTypeInfo ( node . Expression ) ;
13041371 bool isTypeMismatch = typeInfo . Type == null || ! typeInfo . Type . Equals ( typeInfo . ConvertedType ) ;
13051372
1306- return ( ! isIdentifier && ! isMemberAccess ) || isProperty || isTypeMismatch || isUsing ;
1373+ if ( isProperty ) return RefType . Property ;
1374+ if ( ( ! isIdentifier && ! isMemberAccess ) || isTypeMismatch || isUsing ) return RefType . Variable ;
1375+ return RefType . Inline ;
1376+ }
1377+
1378+ private enum RefType
1379+ {
1380+ Inline ,
1381+ Variable ,
1382+ Property
13071383 }
13081384
13091385 private ISymbol GetInvocationSymbol ( SyntaxNode invocation )
0 commit comments