@@ -33,19 +33,19 @@ internal partial class ExpressionNodeVisitor : VBasic.VisualBasicSyntaxVisitor<T
3333 private readonly QueryConverter _queryConverter ;
3434 private readonly Lazy < IReadOnlyDictionary < ITypeSymbol , string > > _convertMethodsLookupByReturnType ;
3535 private readonly LambdaConverter _lambdaConverter ;
36- private readonly VisualBasicNullableExpressionsConverter _visualBasicNullableTypesConverter ;
3736 private readonly Dictionary < string , Stack < ( SyntaxNode Scope , string TempName ) > > _tempNameForAnonymousScope = new ( ) ;
3837 private readonly HashSet < string > _generatedNames = new ( StringComparer . OrdinalIgnoreCase ) ;
3938 private readonly XmlExpressionConverter _xmlExpressionConverter ;
4039 private readonly NameExpressionNodeVisitor _nameExpressionNodeVisitor ;
4140 private readonly ArgumentConverter _argumentConverter ;
41+ private readonly BinaryExpressionConverter _binaryExpressionConverter ;
4242
4343 public ExpressionNodeVisitor ( SemanticModel semanticModel ,
4444 VisualBasicEqualityComparison visualBasicEqualityComparison , ITypeContext typeContext , CommonConversions commonConversions ,
4545 HashSet < string > extraUsingDirectives , XmlImportContext xmlImportContext , VisualBasicNullableExpressionsConverter visualBasicNullableTypesConverter )
4646 {
47- CommonConversions = commonConversions ;
4847 _semanticModel = semanticModel ;
48+ CommonConversions = commonConversions ; ;
4949 _lambdaConverter = new LambdaConverter ( commonConversions , semanticModel ) ;
5050 _visualBasicEqualityComparison = visualBasicEqualityComparison ;
5151 TriviaConvertingExpressionVisitor = new CommentConvertingVisitorWrapper ( this , _semanticModel . SyntaxTree ) ;
@@ -57,6 +57,7 @@ public ExpressionNodeVisitor(SemanticModel semanticModel,
5757 _nameExpressionNodeVisitor = new NameExpressionNodeVisitor ( semanticModel , _generatedNames , typeContext , extraUsingDirectives , _tempNameForAnonymousScope , _withBlockLhs , commonConversions , _argumentConverter , TriviaConvertingExpressionVisitor ) ;
5858 _visualBasicNullableTypesConverter = visualBasicNullableTypesConverter ;
5959 _operatorConverter = VbOperatorConversion . Create ( TriviaConvertingExpressionVisitor , semanticModel , visualBasicEqualityComparison , commonConversions . TypeConversionAnalyzer ) ;
60+ _binaryExpressionConverter = new BinaryExpressionConverter ( semanticModel , _operatorConverter , visualBasicEqualityComparison , visualBasicNullableTypesConverter , commonConversions ) ;
6061 // If this isn't needed, the assembly with Conversions may not be referenced, so this must be done lazily
6162 _convertMethodsLookupByReturnType =
6263 new Lazy < IReadOnlyDictionary < ITypeSymbol , string > > ( ( ) => CreateConvertMethodsLookupByReturnType ( semanticModel ) ) ;
@@ -118,6 +119,7 @@ public override async Task<CSharpSyntaxNode> DefaultVisit(SyntaxNode node)
118119 public override Task < CSharpSyntaxNode > VisitXmlBracketedName ( VBSyntax . XmlBracketedNameSyntax node ) => _xmlExpressionConverter . ConvertXmlBracketedNameAsync ( node ) ;
119120 public override Task < CSharpSyntaxNode > VisitXmlName ( VBSyntax . XmlNameSyntax node ) => _xmlExpressionConverter . ConvertXmlNameAsync ( node ) ;
120121 public override async Task < CSharpSyntaxNode > VisitSimpleArgument ( VBasic . Syntax . SimpleArgumentSyntax node ) => await _argumentConverter . ConvertSimpleArgumentAsync ( node ) ;
122+ public override async Task < CSharpSyntaxNode > VisitBinaryExpression ( VBasic . Syntax . BinaryExpressionSyntax entryNode ) => await _binaryExpressionConverter . ConvertBinaryExpressionAsync ( entryNode ) ;
121123
122124 public override async Task < CSharpSyntaxNode > VisitGetTypeExpression ( VBasic . Syntax . GetTypeExpressionSyntax node )
123125 {
@@ -548,96 +550,6 @@ private CSharpSyntaxNode ConvertAddressOf(VBSyntax.UnaryExpressionSyntax node, E
548550 return expr ;
549551 }
550552
551- public override async Task < CSharpSyntaxNode > VisitBinaryExpression ( VBasic . Syntax . BinaryExpressionSyntax entryNode )
552- {
553- // Walk down the syntax tree for deeply nested binary expressions to avoid stack overflow
554- // e.g. 3 + 4 + 5 + ...
555- // Test "DeeplyNestedBinaryExpressionShouldNotStackOverflowAsync()" skipped because it's too slow
556-
557- ExpressionSyntax csLhs = null ;
558- int levelsToConvert = 0 ;
559- VBSyntax . BinaryExpressionSyntax currentNode = entryNode ;
560-
561- // Walk down the nested levels to count them
562- for ( var nextNode = entryNode ; nextNode != null ; currentNode = nextNode , nextNode = currentNode . Left as VBSyntax . BinaryExpressionSyntax , levelsToConvert ++ ) {
563- // Don't go beyond a rewritten operator because that code has many paths that can call VisitBinaryExpression. Passing csLhs through all of that would harm the code quality more than it's worth to help that edge case.
564- if ( await RewriteBinaryOperatorOrNullAsync ( nextNode ) is { } operatorNode ) {
565- csLhs = operatorNode ;
566- break ;
567- }
568- }
569-
570- // Walk back up the same levels converting as we go.
571- for ( ; levelsToConvert > 0 ; currentNode = currentNode ! . Parent as VBSyntax . BinaryExpressionSyntax , levelsToConvert -- ) {
572- csLhs = ( ExpressionSyntax ) await ConvertBinaryExpressionAsync ( currentNode , csLhs ) ;
573- }
574-
575- return csLhs ;
576- }
577-
578- private async Task < CSharpSyntaxNode > ConvertBinaryExpressionAsync ( VBasic . Syntax . BinaryExpressionSyntax node , ExpressionSyntax lhs = null , ExpressionSyntax rhs = null )
579- {
580- lhs ??= await node . Left . AcceptAsync < ExpressionSyntax > ( TriviaConvertingExpressionVisitor ) ;
581- rhs ??= await node . Right . AcceptAsync < ExpressionSyntax > ( TriviaConvertingExpressionVisitor ) ;
582-
583- var lhsTypeInfo = _semanticModel . GetTypeInfo ( node . Left ) ;
584- var rhsTypeInfo = _semanticModel . GetTypeInfo ( node . Right ) ;
585-
586- ITypeSymbol forceLhsTargetType = null ;
587- bool omitRightConversion = false ;
588- bool omitConversion = false ;
589- if ( lhsTypeInfo . Type != null && rhsTypeInfo . Type != null )
590- {
591- if ( node . IsKind ( VBasic . SyntaxKind . ConcatenateExpression ) &&
592- ! lhsTypeInfo . Type . IsEnumType ( ) && ! rhsTypeInfo . Type . IsEnumType ( ) &&
593- ! lhsTypeInfo . Type . IsDateType ( ) && ! rhsTypeInfo . Type . IsDateType ( ) )
594- {
595- omitRightConversion = true ;
596- omitConversion = lhsTypeInfo . Type . SpecialType == SpecialType . System_String ||
597- rhsTypeInfo . Type . SpecialType == SpecialType . System_String ;
598- if ( lhsTypeInfo . ConvertedType . SpecialType != SpecialType . System_String ) {
599- forceLhsTargetType = CommonConversions . KnownTypes . String ;
600- }
601- }
602- }
603-
604- var objectEqualityType = _visualBasicEqualityComparison . GetObjectEqualityType ( node , lhsTypeInfo , rhsTypeInfo ) ;
605-
606- switch ( objectEqualityType ) {
607- case VisualBasicEqualityComparison . RequiredType . StringOnly :
608- if ( lhsTypeInfo . ConvertedType ? . SpecialType == SpecialType . System_String &&
609- rhsTypeInfo . ConvertedType ? . SpecialType == SpecialType . System_String &&
610- _visualBasicEqualityComparison . TryConvertToNullOrEmptyCheck ( node , lhs , rhs , out CSharpSyntaxNode visitBinaryExpression ) ) {
611- return visitBinaryExpression ;
612- }
613- ( lhs , rhs ) = _visualBasicEqualityComparison . AdjustForVbStringComparison ( node . Left , lhs , lhsTypeInfo , false , node . Right , rhs , rhsTypeInfo , false ) ;
614- omitConversion = true ; // Already handled within for the appropriate types (rhs can become int in comparison)
615- break ;
616- case VisualBasicEqualityComparison . RequiredType . Object :
617- return _visualBasicEqualityComparison . GetFullExpressionForVbObjectComparison ( lhs , rhs , ComparisonKind . Equals , node . IsKind ( VBasic . SyntaxKind . NotEqualsExpression ) ) ;
618- }
619-
620- var lhsTypeIgnoringNullable = lhsTypeInfo . Type . GetNullableUnderlyingType ( ) ?? lhsTypeInfo . Type ;
621- var rhsTypeIgnoringNullable = rhsTypeInfo . Type . GetNullableUnderlyingType ( ) ?? rhsTypeInfo . Type ;
622- omitConversion |= lhsTypeIgnoringNullable != null && rhsTypeIgnoringNullable != null &&
623- lhsTypeIgnoringNullable . IsEnumType ( ) && SymbolEqualityComparer . Default . Equals ( lhsTypeIgnoringNullable , rhsTypeIgnoringNullable )
624- && ! node . IsKind ( VBasic . SyntaxKind . AddExpression , VBasic . SyntaxKind . SubtractExpression , VBasic . SyntaxKind . MultiplyExpression , VBasic . SyntaxKind . DivideExpression , VBasic . SyntaxKind . IntegerDivideExpression , VBasic . SyntaxKind . ModuloExpression )
625- && forceLhsTargetType == null ;
626- lhs = omitConversion ? lhs : CommonConversions . TypeConversionAnalyzer . AddExplicitConversion ( node . Left , lhs , forceTargetType : forceLhsTargetType ) ;
627- rhs = omitConversion || omitRightConversion ? rhs : CommonConversions . TypeConversionAnalyzer . AddExplicitConversion ( node . Right , rhs ) ;
628-
629- var kind = VBasic . VisualBasicExtensions . Kind ( node ) . ConvertToken ( ) ;
630- var op = SyntaxFactory . Token ( CSharpUtil . GetExpressionOperatorTokenKind ( kind ) ) ;
631-
632- var csBinExp = SyntaxFactory . BinaryExpression ( kind , lhs , op , rhs ) ;
633- var exp = _visualBasicNullableTypesConverter . WithBinaryExpressionLogicForNullableTypes ( node , lhsTypeInfo , rhsTypeInfo , csBinExp , lhs , rhs ) ;
634- return node . Parent . IsKind ( VBasic . SyntaxKind . SimpleArgument ) ? exp : exp . AddParens ( ) ;
635- }
636-
637-
638-
639- private async Task < ExpressionSyntax > RewriteBinaryOperatorOrNullAsync ( VBSyntax . BinaryExpressionSyntax node ) =>
640- await _operatorConverter . ConvertRewrittenBinaryOperatorOrNullAsync ( node , TriviaConvertingExpressionVisitor . IsWithinQuery ) ;
641553
642554 public override async Task < CSharpSyntaxNode > VisitSingleLineLambdaExpression ( VBasic . Syntax . SingleLineLambdaExpressionSyntax node )
643555 {
@@ -941,21 +853,6 @@ private static InvocationExpressionSyntax Invoke(ExpressionSyntax toInvoke, Expr
941853 ) ;
942854 }
943855
944- private ExpressionSyntax GetConvertMethodForKeywordOrNull ( SyntaxNode type )
945- {
946- var targetType = _semanticModel . GetTypeInfo ( type ) . Type ;
947- return GetConvertMethodForKeywordOrNull ( targetType ) ;
948- }
949-
950- private ExpressionSyntax GetConvertMethodForKeywordOrNull ( ITypeSymbol targetType )
951- {
952- _extraUsingDirectives . Add ( ConvertType . Namespace ) ;
953- return targetType != null &&
954- _convertMethodsLookupByReturnType . Value . TryGetValue ( targetType , out var convertMethodName )
955- ? SyntaxFactory . ParseExpression ( convertMethodName )
956- : null ;
957- }
958-
959856 private SyntaxToken ConvertIdentifier ( SyntaxToken identifierIdentifier , bool isAttribute = false )
960857 {
961858 return CommonConversions . ConvertIdentifier ( identifierIdentifier , isAttribute ) ;
0 commit comments