@@ -98,6 +98,11 @@ private ExpressionSyntax AddTypeConversion(VBSyntax.ExpressionSyntax vbNode, Exp
9898 var enumUnderlyingType = ( ( INamedTypeSymbol ) vbType ) . EnumUnderlyingType ;
9999 csNode = AddTypeConversion ( vbNode , csNode , TypeConversionKind . NonDestructiveCast , addParenthesisIfNeeded , vbType , enumUnderlyingType ) ;
100100 return AddTypeConversion ( vbNode , csNode , TypeConversionKind . Conversion , addParenthesisIfNeeded , enumUnderlyingType , vbConvertedType ) ;
101+ case TypeConversionKind . LiteralSuffix :
102+ if ( vbNode is VBSyntax . LiteralExpressionSyntax { Token : { Value : { } val , Text : { } text } } && LiteralConversions . GetLiteralExpression ( val , text , vbConvertedType ) is { } csLiteral ) {
103+ return csLiteral ;
104+ }
105+ return csNode ;
101106 case TypeConversionKind . Unknown :
102107 case TypeConversionKind . Identity :
103108 return addParenthesisIfNeeded ? vbNode . ParenthesizeIfPrecedenceCouldChange ( csNode ) : csNode ;
@@ -178,7 +183,7 @@ public TypeConversionKind AnalyzeConversion(VBSyntax.ExpressionSyntax vbNode, bo
178183 var csConvertedType = GetCSType ( vbConvertedType ) ;
179184
180185 if ( csType != null && csConvertedType != null &&
181- TryAnalyzeCsConversion ( vbNode , csType , csConvertedType , vbConversion , vbConvertedType , vbType , isConst , forceSourceType != null , out TypeConversionKind analyzeConversion ) ) {
186+ TryAnalyzeCsConversion ( vbCompilation , vbNode , csType , csConvertedType , vbConversion , vbConvertedType , vbType , isConst , forceSourceType != null , out TypeConversionKind analyzeConversion ) ) {
182187 return analyzeConversion ;
183188 }
184189
@@ -273,20 +278,28 @@ private ITypeSymbol GetCSType(ITypeSymbol vbType, VBSyntax.ExpressionSyntax vbNo
273278 return csType ;
274279 }
275280
276- private bool TryAnalyzeCsConversion ( VBSyntax . ExpressionSyntax vbNode , ITypeSymbol csType ,
281+ private bool TryAnalyzeCsConversion ( VBasic . VisualBasicCompilation vbCompilation , VBSyntax . ExpressionSyntax vbNode , ITypeSymbol csType ,
277282 ITypeSymbol csConvertedType , Conversion vbConversion , ITypeSymbol vbConvertedType , ITypeSymbol vbType , bool isConst , bool sourceForced ,
278283 out TypeConversionKind typeConversionKind )
279284 {
280285 var csConversion = _csCompilation . ClassifyConversion ( csType , csConvertedType ) ;
281- vbType . IsNullable ( out var underlyingType ) ;
282- vbConvertedType . IsNullable ( out var underlyingConvertedType ) ;
283- var nullableVbType = underlyingType ?? vbType ;
284- var nullableVbConvertedType = underlyingConvertedType ?? vbConvertedType ;
286+
287+ vbType . IsNullable ( out var underlyingVbType ) ;
288+ vbConvertedType . IsNullable ( out var underlyingVbConvertedType ) ;
289+ underlyingVbType ??= vbType ;
290+ underlyingVbConvertedType ??= vbConvertedType ;
291+ var vbUnderlyingConversion = vbCompilation . ClassifyConversion ( underlyingVbType , underlyingVbConvertedType ) ;
292+
293+ csType . IsNullable ( out var underlyingCsType ) ;
294+ csConvertedType . IsNullable ( out var underlyingCsConvertedType ) ;
295+ underlyingCsType ??= csType ;
296+ underlyingCsConvertedType ??= csConvertedType ;
297+ var csUnderlyingConversion = _csCompilation . ClassifyConversion ( underlyingCsType , underlyingCsConvertedType ) ;
285298
286299 bool isConvertToString =
287300 ( vbConversion . IsString || vbConversion . IsReference && vbConversion . IsNarrowing ) && vbConvertedType . SpecialType == SpecialType . System_String ;
288301 bool isConvertFractionalToInt =
289- ! csConversion . IsImplicit && nullableVbType . IsFractionalNumericType ( ) && nullableVbConvertedType . IsIntegralOrEnumType ( ) ;
302+ ! csConversion . IsImplicit && underlyingVbType . IsFractionalNumericType ( ) && underlyingVbConvertedType . IsIntegralOrEnumType ( ) ;
290303
291304 if ( ! csConversion . Exists || csConversion . IsUnboxing ) {
292305 if ( ConvertStringToCharLiteral ( vbNode , vbConvertedType , out _ ) ) {
@@ -300,27 +313,27 @@ private bool TryAnalyzeCsConversion(VBSyntax.ExpressionSyntax vbNode, ITypeSymbo
300313 return true ;
301314 }
302315 if ( isConvertToString || vbConversion . IsNarrowing ) {
303- typeConversionKind = nullableVbConvertedType . IsEnumType ( ) && ! csConversion . Exists
316+ typeConversionKind = underlyingVbConvertedType . IsEnumType ( ) && ! csConversion . Exists
304317 ? TypeConversionKind . EnumConversionThenCast
305318 : TypeConversionKind . Conversion ;
306319 return true ;
307320 }
308321 } else if ( vbConversion . IsNarrowing && vbConversion . IsNullableValueType && isConvertFractionalToInt ) {
309322 typeConversionKind = TypeConversionKind . FractionalNumberRoundThenCast ;
310323 return true ;
311- } else if ( vbConversion . IsNumeric && ( csConversion . IsNumeric || nullableVbConvertedType . IsEnumType ( ) ) && isConvertFractionalToInt ) {
324+ } else if ( vbConversion . IsNumeric && ( csConversion . IsNumeric || underlyingVbConvertedType . IsEnumType ( ) ) && isConvertFractionalToInt ) {
312325 typeConversionKind = TypeConversionKind . FractionalNumberRoundThenCast ;
313326 return true ;
314327 } else if ( csConversion is { IsExplicit : true , IsEnumeration : true } or { IsBoxing : true , IsImplicit : false } ) {
315328 typeConversionKind = TypeConversionKind . NonDestructiveCast ;
316329 return true ;
317- } else if ( vbConversion . IsNumeric && csConversion . IsNumeric ) {
330+ } else if ( vbUnderlyingConversion . IsNumeric && csUnderlyingConversion . IsNumeric ) {
318331 // For widening, implicit, a cast is really only needed to help resolve the overload for the operator/method used.
319332 // e.g. When VB "&" changes to C# "+", there are lots more overloads available that implicit casts could match.
320333 // e.g. sbyte * ulong uses the decimal * operator in VB. In C# it's ambiguous - see ExpressionTests.vb "TestMul".
321334 typeConversionKind =
322- isConst && IsImplicitConstantConversion ( vbNode ) || csConversion . IsIdentity || ! sourceForced && IsExactTypeNumericLiteral ( vbNode , vbConvertedType ) ? TypeConversionKind . Identity :
323- csConversion . IsImplicit || vbType . IsNumericType ( ) ? TypeConversionKind . NonDestructiveCast
335+ isConst && IsImplicitConstantConversion ( vbNode ) || csUnderlyingConversion . IsIdentity || ! sourceForced && IsExactTypeNumericLiteral ( vbNode , underlyingVbConvertedType ) ? TypeConversionKind . LiteralSuffix :
336+ csUnderlyingConversion . IsImplicit || underlyingVbType . IsNumericType ( ) ? TypeConversionKind . NonDestructiveCast
324337 : TypeConversionKind . Conversion ;
325338 return true ;
326339 } else if ( isConvertToString && vbType . SpecialType == SpecialType . System_Object ) {
@@ -494,7 +507,8 @@ public enum TypeConversionKind
494507 NullableBool ,
495508 StringToCharArray ,
496509 DelegateConstructor ,
497- FractionalNumberRoundThenCast
510+ FractionalNumberRoundThenCast ,
511+ LiteralSuffix
498512 }
499513
500514 public static bool ConvertStringToCharLiteral ( VBSyntax . ExpressionSyntax node ,
0 commit comments