@@ -414,19 +414,12 @@ public override async Task<CSharpSyntaxNode> VisitSimpleArgument(VBasic.Syntax.S
414414 var refType = GetRefType ( node , argList , parameters , out argName , out refKind ) ;
415415 var expression = CommonConversions . TypeConversionAnalyzer . AddExplicitConversion ( node . Expression , ( ExpressionSyntax ) await node . Expression . AcceptAsync ( TriviaConvertingExpressionVisitor ) , defaultToCast : refKind != RefKind . None ) ;
416416
417- if ( refType == RefType . Variable ) {
417+ if ( refType != RefConversion . Inline ) {
418418 var expressionTypeInfo = _semanticModel . GetTypeInfo ( node . Expression ) ;
419419 bool useVar = expressionTypeInfo . Type ? . Equals ( expressionTypeInfo . ConvertedType ) == true && ! CommonConversions . ShouldPreferExplicitType ( node . Expression , expressionTypeInfo . ConvertedType , out var _ ) ;
420420 var typeSyntax = CommonConversions . GetTypeSyntax ( expressionTypeInfo . ConvertedType , useVar ) ;
421421 string prefix = $ "arg{ argName } ";
422422 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 ) ) ;
430423 }
431424 }
432425
@@ -458,7 +451,7 @@ private async Task<ExpressionSyntax> ConvertArgExpression(VBSyntax.SimpleArgumen
458451 return CommonConversions . TypeConversionAnalyzer . AddExplicitConversion ( node . Expression , ( ExpressionSyntax ) await node . Expression . AcceptAsync ( TriviaConvertingExpressionVisitor ) , defaultToCast : refKind != RefKind . None ) ;
459452 }
460453
461- private RefType GetRefType ( VBSyntax . SimpleArgumentSyntax node , VBSyntax . ArgumentListSyntax argList , System . Collections . Immutable . ImmutableArray < IParameterSymbol > parameters , out string argName , out RefKind refKind )
454+ private RefConversion GetRefType ( VBSyntax . SimpleArgumentSyntax node , VBSyntax . ArgumentListSyntax argList , System . Collections . Immutable . ImmutableArray < IParameterSymbol > parameters , out string argName , out RefKind refKind )
462455 {
463456 var parameter = ! node . IsNamed ? parameters . ElementAtOrDefault ( argList . Arguments . IndexOf ( node ) ) : parameters . FirstOrDefault ( p => p . Name . Equals ( node . NameColonEquals . Name . Identifier . Text , StringComparison . OrdinalIgnoreCase ) ) ;
464457 if ( parameter != null ) {
@@ -471,38 +464,6 @@ private RefType GetRefType(VBSyntax.SimpleArgumentSyntax node, VBSyntax.Argument
471464 return NeedsVariableForArgument ( node , refKind ) ;
472465 }
473466
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-
506467 private static StatementSyntax AssignStmt ( ExpressionSyntax left , IdentifierNameSyntax right )
507468 {
508469 return SyntaxFactory . ExpressionStatement ( Assign ( left , right ) ) ;
@@ -878,6 +839,9 @@ public override async Task<CSharpSyntaxNode> VisitInvocationExpression(
878839
879840 return SyntaxFactory . InvocationExpression ( ( ExpressionSyntax ) expr , args ) ;
880841 }
842+ //TODO: Decide if the above override should be subject to the rest of this method's adjustments (probably)
843+
844+ CreateLocalByRefFunction ( node , invocationSymbol ) ;
881845
882846 // VB doesn't have a specialized node for element access because the syntax is ambiguous. Instead, it just uses an invocation expression or dictionary access expression, then figures out using the semantic model which one is most likely intended.
883847 // https://github.com/dotnet/roslyn/blob/master/src/Workspaces/VisualBasic/Portable/LanguageServices/VisualBasicSyntaxFactsService.vb#L768
@@ -927,6 +891,48 @@ async Task<CSharpSyntaxNode> CreateElementAccess()
927891 }
928892 }
929893
894+
895+ /// <summary>
896+ /// If we need a local function,
897+ /// </summary>
898+ /// <param name="invocation"></param>
899+ /// <param name="invocationSymbol"></param>
900+ /// <returns></returns>
901+ private async Task < ( string Id , StatementSyntax FunctionDeclaration ) > CreateLocalByRefFunction ( VBSyntax . InvocationExpressionSyntax invocation , IMethodSymbol invocationSymbol )
902+ {
903+ var originalArgsWithRefTypes = invocation . ArgumentList . Arguments
904+ . 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 ) )
905+ . ToArray ( ) ;
906+
907+ var isDefinitelyExecuted = IsDefinitelyExecutedInStatement ( invocation ) ;
908+
909+ var localFuncName = $ "local{ invocationSymbol . Name } ";
910+ const string retVariableName = "ret" ;
911+ var localFuncId = SyntaxFactory . IdentifierName ( localFuncName ) ;
912+ // Need essentially the original invocation with any byref args swapped out for temporaries
913+ var callAndStoreResult = CommonConversions . CreateLocalVariableDeclarationAndAssignment ( retVariableName ,
914+ SyntaxFactory . InvocationExpression ( expression ,
915+ ) ;
916+
917+ var block = SyntaxFactory. Block(
918+ callAndStoreResult ,
919+ AssignStmt ( expression , tempArg ) ,
920+ SyntaxFactory . ReturnStatement ( SyntaxFactory . IdentifierName ( retVariableName ) )
921+ ) ;
922+ var localfunction = SyntaxFactory . LocalFunctionStatement ( CommonConversions . GetTypeSyntax ( invocationSymbol . ReturnType ) ,
923+ localFuncId . Identifier ) . WithBody ( block ) ;
924+ return ( localFuncName , localfunction ) ;
925+ }
926+
927+ private static bool IsDefinitelyExecutedInStatement ( VBSyntax . InvocationExpressionSyntax invocation )
928+ {
929+ VBSyntax . StatementSyntax parentStatement ;
930+ do {
931+ parentStatement = invocation . GetAncestor < VBSyntax . StatementSyntax > ( ) ;
932+ } while ( parentStatement is VBSyntax . ElseIfStatementSyntax ) ;
933+ return parentStatement . FollowProperty < SyntaxNode > ( n => n . ChildNodes ( ) . FirstOrDefault ( ) ) . Contains ( invocation ) ;
934+ }
935+
930936 public override async Task < CSharpSyntaxNode > VisitSingleLineLambdaExpression ( VBasic . Syntax . SingleLineLambdaExpressionSyntax node )
931937 {
932938 IReadOnlyCollection < StatementSyntax > convertedStatements ;
@@ -1358,7 +1364,7 @@ private ArgumentSyntax CreateOptionalRefArg(IParameterSymbol p)
13581364 return ( ArgumentSyntax ) CommonConversions . CsSyntaxGenerator . Argument ( p . Name , p . RefKind , local . IdentifierName ) ;
13591365 }
13601366
1361- private RefType NeedsVariableForArgument ( VBasic . Syntax . SimpleArgumentSyntax node , RefKind refKind )
1367+ private RefConversion NeedsVariableForArgument ( VBasic . Syntax . SimpleArgumentSyntax node , RefKind refKind )
13621368 {
13631369 bool isIdentifier = node . Expression is VBasic . Syntax . IdentifierNameSyntax ;
13641370 bool isMemberAccess = node . Expression is VBasic . Syntax . MemberAccessExpressionSyntax ;
@@ -1370,16 +1376,31 @@ private RefType NeedsVariableForArgument(VBasic.Syntax.SimpleArgumentSyntax node
13701376 var typeInfo = _semanticModel . GetTypeInfo ( node . Expression ) ;
13711377 bool isTypeMismatch = typeInfo . Type == null || ! typeInfo . Type . Equals ( typeInfo . ConvertedType ) ;
13721378
1373- if ( isProperty ) return RefType . Property ;
1374- if ( ( ! isIdentifier && ! isMemberAccess ) || isTypeMismatch || isUsing ) return RefType . Variable ;
1375- return RefType . Inline ;
1379+ if ( isProperty ) return RefConversion . PreAndPostAssignment ;
1380+ if ( ( ! isIdentifier && ! isMemberAccess ) || isTypeMismatch || isUsing ) return RefConversion . PreAssigment ;
1381+ return RefConversion . Inline ;
13761382 }
13771383
1378- private enum RefType
1384+ /// <summary>
1385+ /// https://github.com/icsharpcode/CodeConverter/issues/324
1386+ /// https://github.com/icsharpcode/CodeConverter/issues/310
1387+ /// </summary>
1388+ private enum RefConversion
13791389 {
1390+ /// <summary>
1391+ /// e.g. Normal field, parameter or local
1392+ /// </summary>
13801393 Inline ,
1381- Variable ,
1382- Property
1394+ /// <summary>
1395+ /// Needs assignment before and/or after
1396+ /// e.g. Method/Property result
1397+ /// </summary>
1398+ PreAssigment ,
1399+ /// <summary>
1400+ /// Needs assignment before and/or after
1401+ /// i.e. Property
1402+ /// </summary>
1403+ PreAndPostAssignment
13831404 }
13841405
13851406 private ISymbol GetInvocationSymbol ( SyntaxNode invocation )
0 commit comments