@@ -641,34 +641,59 @@ public override async Task<SyntaxList<StatementSyntax>> VisitGoToStatement(VBSyn
641641 public override async Task < SyntaxList < StatementSyntax > > VisitSelectBlock ( VBSyntax . SelectBlockSyntax node )
642642 {
643643 var vbExpr = node . SelectStatement . Expression ;
644- var ( csExpr , stmts , csExprWithSourceMapping ) = await GetExpressionWithoutSideEffectsAsync ( vbExpr , "switchExpr" ) ;
644+ var vbEquality = CommonConversions . VisualBasicEqualityComparison ;
645+
646+ var csSwitchExpr = ( ExpressionSyntax ) await vbExpr . AcceptAsync ( _expressionVisitor ) ;
647+ csSwitchExpr = CommonConversions . TypeConversionAnalyzer . AddExplicitConversion ( vbExpr , csSwitchExpr ) ;
648+ var switchExprTypeInfo = _semanticModel . GetTypeInfo ( vbExpr ) ;
649+ var isStringComparison = switchExprTypeInfo . ConvertedType ? . SpecialType == SpecialType . System_String || switchExprTypeInfo . ConvertedType ? . IsArrayOf ( SpecialType . System_Char ) == true ;
650+ var caseInsensitiveStringComparison = vbEquality . OptionCompareTextCaseInsensitive &&
651+ isStringComparison ;
652+ if ( isStringComparison ) {
653+ csSwitchExpr = vbEquality . VbCoerceToNonNullString ( vbExpr , csSwitchExpr , switchExprTypeInfo ) ;
654+ }
655+
645656 var usedConstantValues = new HashSet < object > ( ) ;
646657 var sections = new List < SwitchSectionSyntax > ( ) ;
647658 foreach ( var block in node . CaseBlocks ) {
648659 var labels = new List < SwitchLabelSyntax > ( ) ;
649660 foreach ( var c in block . CaseStatement . Cases ) {
650661 if ( c is VBSyntax . SimpleCaseClauseSyntax s ) {
651662 var originalExpressionSyntax = ( ExpressionSyntax ) await s . Value . AcceptAsync ( _expressionVisitor ) ;
652- // CSharp requires an explicit cast from the base type (e.g. int) in most cases switching on an enum
663+ var caseTypeInfo = _semanticModel . GetTypeInfo ( s . Value ) ;
653664 var typeConversionKind = CommonConversions . TypeConversionAnalyzer . AnalyzeConversion ( s . Value ) ;
654665 var correctTypeExpressionSyntax = CommonConversions . TypeConversionAnalyzer . AddExplicitConversion ( s . Value , originalExpressionSyntax , typeConversionKind , true , true ) ;
655666 var constantValue = _semanticModel . GetConstantValue ( s . Value ) ;
656667 var notAlreadyUsed = ! constantValue . HasValue || usedConstantValues . Add ( constantValue . Value ) ;
657- var caseSwitchLabelSyntax = correctTypeExpressionSyntax . IsConst && notAlreadyUsed
658- ? ( SwitchLabelSyntax ) SyntaxFactory . CaseSwitchLabel ( correctTypeExpressionSyntax . Expr )
659- : WrapInCasePatternSwitchLabelSyntax ( node , correctTypeExpressionSyntax . Expr ) ;
668+
669+ // Pass both halves in case we can optimize away the check based on the switch expr
670+ var wrapForStringComparison = isStringComparison && ( caseInsensitiveStringComparison ||
671+ vbEquality . VbCoerceToNonNullString ( vbExpr , csSwitchExpr , switchExprTypeInfo , true , s . Value , originalExpressionSyntax , caseTypeInfo , false ) . rhs != originalExpressionSyntax ) ;
672+
673+ // CSharp requires an explicit cast from the base type (e.g. int) in most cases switching on an enum
674+ var csExpressionToUse = switchExprTypeInfo . ConvertedType ? . IsEnumType ( ) == true ^ caseTypeInfo . Type ? . IsEnumType ( ) == true ? correctTypeExpressionSyntax . Expr : originalExpressionSyntax ;
675+
676+ var caseSwitchLabelSyntax = ! wrapForStringComparison && correctTypeExpressionSyntax . IsConst && notAlreadyUsed
677+ ? ( SwitchLabelSyntax ) SyntaxFactory . CaseSwitchLabel ( csExpressionToUse )
678+ : WrapInCasePatternSwitchLabelSyntax ( node , s . Value , csExpressionToUse ) ;
660679 labels . Add ( caseSwitchLabelSyntax ) ;
661680 } else if ( c is VBSyntax . ElseCaseClauseSyntax ) {
662681 labels . Add ( SyntaxFactory . DefaultSwitchLabel ( ) ) ;
663682 } else if ( c is VBSyntax . RelationalCaseClauseSyntax relational ) {
683+
684+ var varName = CommonConversions . CsEscapedIdentifier ( GetUniqueVariableNameInScope ( node , "case" ) ) ;
685+ ExpressionSyntax csLeft = SyntaxFactory . IdentifierName ( varName ) ;
664686 var operatorKind = VBasic . VisualBasicExtensions . Kind ( relational ) ;
665- var binaryExp = SyntaxFactory . BinaryExpression ( operatorKind . ConvertToken ( TokenContext . Local ) , csExpr , ( ExpressionSyntax ) await relational . Value . AcceptAsync ( _expressionVisitor ) ) ;
666- labels . Add ( WrapInCasePatternSwitchLabelSyntax ( node , binaryExp , treatAsBoolean : true ) ) ;
687+ var relationalValue = ( ExpressionSyntax ) await relational . Value . AcceptAsync ( _expressionVisitor ) ;
688+ var binaryExp = SyntaxFactory . BinaryExpression ( operatorKind . ConvertToken ( TokenContext . Local ) , csLeft , relationalValue ) ;
689+ labels . Add ( VarWhen ( varName , binaryExp ) ) ;
667690 } else if ( c is VBSyntax . RangeCaseClauseSyntax range ) {
668- var lowerBoundCheck = SyntaxFactory . BinaryExpression ( SyntaxKind . LessThanOrEqualExpression , ( ExpressionSyntax ) await range . LowerBound . AcceptAsync ( _expressionVisitor ) , csExpr ) ;
669- var upperBoundCheck = SyntaxFactory . BinaryExpression ( SyntaxKind . LessThanOrEqualExpression , csExpr , ( ExpressionSyntax ) await range . UpperBound . AcceptAsync ( _expressionVisitor ) ) ;
691+ var varName = CommonConversions . CsEscapedIdentifier ( GetUniqueVariableNameInScope ( node , "case" ) ) ;
692+ ExpressionSyntax csLeft = SyntaxFactory . IdentifierName ( varName ) ;
693+ var lowerBoundCheck = SyntaxFactory . BinaryExpression ( SyntaxKind . LessThanOrEqualExpression , ( ExpressionSyntax ) await range . LowerBound . AcceptAsync ( _expressionVisitor ) , csLeft ) ;
694+ var upperBoundCheck = SyntaxFactory . BinaryExpression ( SyntaxKind . LessThanOrEqualExpression , csLeft , ( ExpressionSyntax ) await range . UpperBound . AcceptAsync ( _expressionVisitor ) ) ;
670695 var withinBounds = SyntaxFactory . BinaryExpression ( SyntaxKind . LogicalAndExpression , lowerBoundCheck , upperBoundCheck ) ;
671- labels . Add ( WrapInCasePatternSwitchLabelSyntax ( node , withinBounds , treatAsBoolean : true ) ) ;
696+ labels . Add ( VarWhen ( varName , withinBounds ) ) ;
672697 } else throw new NotSupportedException ( c . Kind ( ) . ToString ( ) ) ;
673698 }
674699
@@ -681,8 +706,15 @@ public override async Task<SyntaxList<StatementSyntax>> VisitSelectBlock(VBSynta
681706 sections . Add ( SyntaxFactory . SwitchSection ( SyntaxFactory . List ( labels ) , list ) ) ;
682707 }
683708
684- var switchStatementSyntax = ValidSyntaxFactory . SwitchStatement ( csExprWithSourceMapping , sections ) ;
685- return stmts . Add ( switchStatementSyntax ) ;
709+ var switchStatementSyntax = ValidSyntaxFactory . SwitchStatement ( csSwitchExpr , sections ) ;
710+ return SingleStatement ( switchStatementSyntax ) ;
711+ }
712+
713+ private static CasePatternSwitchLabelSyntax VarWhen ( SyntaxToken varName , ExpressionSyntax binaryExp )
714+ {
715+ var patternMatch = ValidSyntaxFactory . VarPattern ( varName ) ;
716+ return SyntaxFactory . CasePatternSwitchLabel ( patternMatch ,
717+ SyntaxFactory . WhenClause ( binaryExp ) , SyntaxFactory . Token ( SyntaxKind . ColonToken ) ) ;
686718 }
687719
688720 private async Task < ( ExpressionSyntax Reusable , SyntaxList < StatementSyntax > Statements , ExpressionSyntax SingleUse ) > GetExpressionWithoutSideEffectsAsync ( VBSyntax . ExpressionSyntax vbExpr , string variableNameBase , bool forceVariable = false )
@@ -720,7 +752,7 @@ private async Task<bool> IsNeverMutatedAsync(VBSyntax.NameSyntax ns)
720752 return symbol . MatchesKind ( SymbolKind . Parameter , SymbolKind . Local ) && await CommonConversions . Document . Project . Solution . IsNeverWrittenAsync ( symbol , allowedLocation ) ;
721753 }
722754
723- private CasePatternSwitchLabelSyntax WrapInCasePatternSwitchLabelSyntax ( VBSyntax . SelectBlockSyntax node , ExpressionSyntax expression , bool treatAsBoolean = false )
755+ private CasePatternSwitchLabelSyntax WrapInCasePatternSwitchLabelSyntax ( VBSyntax . SelectBlockSyntax node , VBSyntax . ExpressionSyntax vbCase , ExpressionSyntax expression , bool treatAsBoolean = false )
724756 {
725757 var typeInfo = _semanticModel . GetTypeInfo ( node . SelectStatement . Expression ) ;
726758
@@ -731,10 +763,18 @@ private CasePatternSwitchLabelSyntax WrapInCasePatternSwitchLabelSyntax(VBSyntax
731763 SyntaxFactory . DiscardDesignation ( ) ) ;
732764 } else {
733765 var varName = CommonConversions . CsEscapedIdentifier ( GetUniqueVariableNameInScope ( node , "case" ) ) ;
734- //CodeAnalysis upgrade to 3.0.0 needed for VarPattern. Correct text comes out, but tree is invalid so the tests this will generate "CS0825: The contextual keyword 'var' may only appear within a local variable declaration or in script code"
735- patternMatch = SyntaxFactory . DeclarationPattern (
736- ValidSyntaxFactory . VarType , SyntaxFactory . SingleVariableDesignation ( varName ) ) ;
737- expression = SyntaxFactory . BinaryExpression ( SyntaxKind . EqualsExpression , SyntaxFactory . IdentifierName ( varName ) , expression ) ;
766+ patternMatch = ValidSyntaxFactory . VarPattern ( varName ) ;
767+ ExpressionSyntax csLeft = SyntaxFactory . IdentifierName ( varName ) , csRight = expression ;
768+ var caseTypeInfo = _semanticModel . GetTypeInfo ( vbCase ) ;
769+ var vbEquality = CommonConversions . VisualBasicEqualityComparison ;
770+ if ( vbEquality . GetObjectEqualityType ( typeInfo , caseTypeInfo ) == VisualBasicEqualityComparison . RequiredType . Object ) {
771+ expression = vbEquality . GetFullExpressionForVbObjectComparison ( csLeft , csRight ) ;
772+ } else {
773+ // We know lhs isn't null, because we always coalesce it in the switch expression
774+ ( csLeft , csRight ) = vbEquality
775+ . AdjustForVbStringComparison ( node . SelectStatement . Expression , csLeft , typeInfo , true , vbCase , csRight , caseTypeInfo , false ) ;
776+ expression = SyntaxFactory . BinaryExpression ( SyntaxKind . EqualsExpression , csLeft , csRight ) ;
777+ }
738778 }
739779
740780 var casePatternSwitchLabelSyntax = SyntaxFactory . CasePatternSwitchLabel ( patternMatch ,
0 commit comments