@@ -407,26 +407,38 @@ public override async Task<CSharpSyntaxNode> VisitSimpleArgument(VBasic.Syntax.S
407407 return await node . Expression . AcceptAsync ( TriviaConvertingExpressionVisitor ) ;
408408 var symbol = GetInvocationSymbol ( invocation ) ;
409409 SyntaxToken token = default ( SyntaxToken ) ;
410- string argName = null ;
411- RefKind refKind = RefKind . None ;
412- AdditionalDeclaration local = null ;
410+ var convertedExpression = ( ExpressionSyntax ) await node . Expression . AcceptAsync ( TriviaConvertingExpressionVisitor ) ;
413411 if ( symbol is IMethodSymbol methodSymbol ) {
414412 var parameters = ( CommonConversions . GetCsOriginalSymbolOrNull ( methodSymbol . OriginalDefinition ) ?? methodSymbol ) . GetParameters ( ) ;
415- var refType = GetRefType ( node , argList , parameters , out argName , out refKind ) ;
416- var expression = CommonConversions . TypeConversionAnalyzer . AddExplicitConversion ( node . Expression , ( ExpressionSyntax ) await node . Expression . AcceptAsync ( TriviaConvertingExpressionVisitor ) , defaultToCast : refKind != RefKind . None ) ;
413+ var refType = GetRefConversionType ( node , argList , parameters , out var argName , out var refKind ) ;
414+ var convertedExpressionWithoutCast = convertedExpression ;
415+ convertedExpression = CommonConversions . TypeConversionAnalyzer . AddExplicitConversion ( node . Expression , convertedExpression , defaultToCast : refKind != RefKind . None ) ;
417416
418- string prefix = $ "arg{ argName } ";
419417 if ( refType != RefConversion . Inline ) {
418+ string prefix = $ "arg{ argName } ";
420419 var expressionTypeInfo = _semanticModel . GetTypeInfo ( node . Expression ) ;
421420 bool useVar = expressionTypeInfo . Type ? . Equals ( expressionTypeInfo . ConvertedType ) == true && ! CommonConversions . ShouldPreferExplicitType ( node . Expression , expressionTypeInfo . ConvertedType , out var _ ) ;
422421 var typeSyntax = CommonConversions . GetTypeSyntax ( expressionTypeInfo . ConvertedType , useVar ) ;
423- local = _additionalLocals . Hoist ( new AdditionalDeclaration ( prefix , expression , typeSyntax ) ) ;
424- }
425- if ( refType == RefConversion . PreAndPostAssignment ) {
426- _additionalLocals . Hoist ( new AdditionalAssignment ( local . IdentifierName , expression ) ) ;
422+ var local = _additionalLocals . Hoist ( new AdditionalDeclaration ( prefix , convertedExpression , typeSyntax ) ) ;
423+
424+ if ( refType == RefConversion . PreAndPostAssignment ) {
425+ var convertedLocalIdentifier = CommonConversions . TypeConversionAnalyzer . AddExplicitConversion ( node . Expression , local . IdentifierName , forceSourceType : expressionTypeInfo . ConvertedType , forceTargetType : expressionTypeInfo . Type ) ;
426+ _additionalLocals . Hoist ( new AdditionalAssignment ( convertedExpressionWithoutCast , convertedLocalIdentifier ) ) ;
427+ }
428+ convertedExpression = local . IdentifierName ;
427429 }
430+ token = GetRefToken ( refKind ) ;
431+ } else {
432+ convertedExpression = CommonConversions . TypeConversionAnalyzer . AddExplicitConversion ( node . Expression , convertedExpression ) ;
428433 }
429434
435+ var nameColon = node . IsNamed ? SyntaxFactory . NameColon ( ( IdentifierNameSyntax ) await node . NameColonEquals . Name . AcceptAsync ( TriviaConvertingExpressionVisitor ) ) : null ;
436+ return SyntaxFactory . Argument ( nameColon , token , convertedExpression ) ;
437+ }
438+
439+ private static SyntaxToken GetRefToken ( RefKind refKind )
440+ {
441+ SyntaxToken token ;
430442 switch ( refKind ) {
431443 case RefKind . None :
432444 token = default ( SyntaxToken ) ;
@@ -440,22 +452,11 @@ public override async Task<CSharpSyntaxNode> VisitSimpleArgument(VBasic.Syntax.S
440452 default :
441453 throw new ArgumentOutOfRangeException ( ) ;
442454 }
443- var simpleExpression = await ConvertArgExpression ( node , refKind ) ;
444455
445- var nameColon = node . IsNamed ? SyntaxFactory . NameColon ( ( IdentifierNameSyntax ) await node . NameColonEquals . Name . AcceptAsync ( TriviaConvertingExpressionVisitor ) ) : null ;
446- if ( local == null ) {
447- return SyntaxFactory . Argument ( nameColon , token , simpleExpression ) ;
448- } else {
449- return SyntaxFactory . Argument ( nameColon , token , local . IdentifierName ) ;
450- }
456+ return token ;
451457 }
452458
453- private async Task < ExpressionSyntax > ConvertArgExpression ( VBSyntax . SimpleArgumentSyntax node , RefKind refKind )
454- {
455- return CommonConversions . TypeConversionAnalyzer . AddExplicitConversion ( node . Expression , ( ExpressionSyntax ) await node . Expression . AcceptAsync ( TriviaConvertingExpressionVisitor ) , defaultToCast : refKind != RefKind . None ) ;
456- }
457-
458- private RefConversion GetRefType ( VBSyntax . ArgumentSyntax node , VBSyntax . ArgumentListSyntax argList , System . Collections . Immutable . ImmutableArray < IParameterSymbol > parameters , out string argName , out RefKind refKind )
459+ private RefConversion GetRefConversionType ( VBSyntax . ArgumentSyntax node , VBSyntax . ArgumentListSyntax argList , System . Collections . Immutable . ImmutableArray < IParameterSymbol > parameters , out string argName , out RefKind refKind )
459460 {
460461 var parameter = node . IsNamed && node is VBSyntax . SimpleArgumentSyntax sas
461462 ? parameters . FirstOrDefault ( p => p . Name . Equals ( sas . NameColonEquals . Name . Identifier . Text , StringComparison . OrdinalIgnoreCase ) )
@@ -918,11 +919,10 @@ async Task<CSharpSyntaxNode> CreateElementAccess()
918919
919920
920921 /// <summary>
921- /// If we need a local function,
922+ /// The VB compiler actually just hoists the conditions within the same method, but that leads to the original logic looking very different.
923+ /// This should be equivalent but keep closer to the look of the original source code.
924+ /// See https://github.com/icsharpcode/CodeConverter/issues/310 and https://github.com/icsharpcode/CodeConverter/issues/324
922925 /// </summary>
923- /// <param name="invocation"></param>
924- /// <param name="invocationSymbol"></param>
925- /// <returns></returns>
926926 private async Task < InvocationExpressionSyntax > HoistAndCallLocalFunction ( VBSyntax . InvocationExpressionSyntax invocation , IMethodSymbol invocationSymbol , ExpressionSyntax csExpression )
927927 {
928928 const string retVariableName = "ret" ;
@@ -945,8 +945,17 @@ private async Task<InvocationExpressionSyntax> HoistAndCallLocalFunction(VBSynta
945945 private bool RequiresLocalFunction ( VBSyntax . InvocationExpressionSyntax invocation , IMethodSymbol invocationSymbol )
946946 {
947947 if ( invocation . ArgumentList == null || IsDefinitelyExecutedInStatement ( invocation ) ) return false ;
948- return invocation . ArgumentList . Arguments
949- . Any ( a => RefConversion . Inline != GetRefType ( a , invocation . ArgumentList , invocationSymbol . Parameters , out var argName , out var refKind ) ) ;
948+ return invocation . ArgumentList . Arguments . Any ( a => RequiresLocalFunction ( invocation , invocationSymbol , a ) ) ;
949+
950+ bool RequiresLocalFunction ( VBSyntax . InvocationExpressionSyntax invocation , IMethodSymbol invocationSymbol , VBSyntax . ArgumentSyntax a )
951+ {
952+ var refConversion = GetRefConversionType ( a , invocation . ArgumentList , invocationSymbol . Parameters , out var argName , out var refKind ) ;
953+ if ( RefConversion . Inline == refConversion ) return false ;
954+ if ( ! ( a is VBSyntax . SimpleArgumentSyntax sas ) ) return false ;
955+ var argExpression = sas . Expression . SkipParens ( ) ;
956+ if ( argExpression is VBSyntax . InstanceExpressionSyntax ) return false ;
957+ return ! _semanticModel . GetConstantValue ( argExpression ) . HasValue ;
958+ }
950959 }
951960
952961 private static bool IsDefinitelyExecutedInStatement ( VBSyntax . InvocationExpressionSyntax invocation )
@@ -961,8 +970,6 @@ private static bool IsDefinitelyExecutedInStatement(VBSyntax.InvocationExpressio
961970 /// <summary>
962971 /// It'd be great to use _semanticModel.AnalyzeControlFlow(invocation).ExitPoints, but that doesn't account for the possibility of exceptions
963972 /// </summary>
964- /// <param name="n"></param>
965- /// <returns></returns>
966973 private static SyntaxNode GetLeftMostWithPossibleExitPoints ( SyntaxNode n ) => n switch
967974 {
968975 VBSyntax . VariableDeclaratorSyntax vds => vds . Initializer ,
0 commit comments