' Licensed to the .NET Foundation under one or more agreements. ' The .NET Foundation licenses this file to you under the MIT license. ' See the LICENSE file in the project root for more information. Imports System.Globalization Imports Microsoft.CodeAnalysis Imports Microsoft.CodeAnalysis.CodeGeneration Imports Microsoft.CodeAnalysis.CodeGeneration.CodeGenerationHelpers Imports Microsoft.CodeAnalysis.Editing Imports Microsoft.CodeAnalysis.Simplification Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration Partial Friend Module ExpressionGenerator Private Const s_doubleQuote = """" Public Function GenerateExpression(generator As SyntaxGenerator, typedConstant As TypedConstant) As ExpressionSyntax Select Case typedConstant.Kind Case TypedConstantKind.Primitive, TypedConstantKind.Enum Return GenerateExpression(generator, typedConstant.Type, typedConstant.Value, canUseFieldReference:=True) Case TypedConstantKind.Array If typedConstant.IsNull Then Return GenerateNothingLiteral() Else Return SyntaxFactory.CollectionInitializer( SyntaxFactory.SeparatedList(typedConstant.Values.Select(Function(v) GenerateExpression(generator, v)))) End If Case TypedConstantKind.Type If TypeOf typedConstant.Value IsNot ITypeSymbol Then Return GenerateNothingLiteral() End If Return SyntaxFactory.GetTypeExpression(DirectCast(typedConstant.Value, ITypeSymbol).GenerateTypeSyntax()) Case Else Return GenerateNothingLiteral() End Select End Function Friend Function GenerateExpression(generator As SyntaxGenerator, type As ITypeSymbol, value As Object, canUseFieldReference As Boolean) As ExpressionSyntax If value IsNot Nothing Then If type.IsNullable() Then ' If the type of the argument is T?, then the type of the supplied default value can either be T ' (e.g. Optional x As Integer? = 5) or it can be T? (e.g. Optional x as SomeStruct? = Nothing). The ' below statement handles the case where the type of the supplied default value is T. Return GenerateExpression(generator, DirectCast(type, INamedTypeSymbol).TypeArguments(0), value, canUseFieldReference) ElseIf type?.TypeKind = TypeKind.Enum Then Return DirectCast(VisualBasicFlagsEnumGenerator.Instance.CreateEnumConstantValue( DirectCast(type, INamedTypeSymbol), value), ExpressionSyntax) End If End If Return GenerateNonEnumValueExpression(type, value, canUseFieldReference) End Function Friend Function GenerateNonEnumValueExpression(type As ITypeSymbol, value As Object, canUseFieldReference As Boolean) As ExpressionSyntax If TypeOf value Is Boolean Then Dim boolValue = DirectCast(value, Boolean) If boolValue Then Return SyntaxFactory.TrueLiteralExpression(SyntaxFactory.Token(SyntaxKind.TrueKeyword)) Else Return SyntaxFactory.FalseLiteralExpression(SyntaxFactory.Token(SyntaxKind.FalseKeyword)) End If ElseIf TypeOf value Is String Then Return GenerateStringLiteralExpression(type, DirectCast(value, String)) ElseIf TypeOf value Is Char Then Return GenerateCharLiteralExpression(DirectCast(value, Char)) ElseIf TypeOf value Is SByte Then Return GenerateIntegralLiteralExpression(type, SpecialType.System_SByte, DirectCast(value, SByte), canUseFieldReference, LiteralSpecialValues.SByteSpecialValues, Function(x) x < 0, Function(x) -x, "128") ElseIf TypeOf value Is Short Then Return GenerateIntegralLiteralExpression(type, SpecialType.System_Int16, DirectCast(value, Short), canUseFieldReference, LiteralSpecialValues.Int16SpecialValues, Function(x) x < 0, Function(x) -x, "32768") ElseIf TypeOf value Is Integer Then Return GenerateIntegralLiteralExpression(type, SpecialType.System_Int32, DirectCast(value, Integer), canUseFieldReference, LiteralSpecialValues.Int32SpecialValues, Function(x) x < 0, Function(x) -x, "2147483648") ElseIf TypeOf value Is Long Then Return GenerateIntegralLiteralExpression(type, SpecialType.System_Int64, DirectCast(value, Long), canUseFieldReference, LiteralSpecialValues.Int64SpecialValues, Function(x) x < 0, Function(x) -x, "9223372036854775808") ElseIf TypeOf value Is Byte Then Return GenerateNonNegativeIntegralLiteralExpression(type, SpecialType.System_Byte, DirectCast(value, Byte), canUseFieldReference, LiteralSpecialValues.ByteSpecialValues) ElseIf TypeOf value Is UShort Then Return GenerateNonNegativeIntegralLiteralExpression(type, SpecialType.System_UInt16, DirectCast(value, UShort), canUseFieldReference, LiteralSpecialValues.UInt16SpecialValues) ElseIf TypeOf value Is UInteger Then Return GenerateNonNegativeIntegralLiteralExpression(type, SpecialType.System_UInt32, DirectCast(value, UInteger), canUseFieldReference, LiteralSpecialValues.UInt32SpecialValues) ElseIf TypeOf value Is ULong Then Return GenerateNonNegativeIntegralLiteralExpression(type, SpecialType.System_UInt64, DirectCast(value, ULong), canUseFieldReference, LiteralSpecialValues.UInt64SpecialValues) ElseIf TypeOf value Is Single Then Return GenerateSingleLiteralExpression(type, DirectCast(value, Single), canUseFieldReference) ElseIf TypeOf value Is Double Then Return GenerateDoubleLiteralExpression(type, DirectCast(value, Double), canUseFieldReference) ElseIf TypeOf value Is Decimal Then Return GenerateDecimalLiteralExpression(type, DirectCast(value, Decimal), canUseFieldReference) ElseIf TypeOf value Is DateTime Then Return GenerateDateLiteralExpression(DirectCast(value, DateTime)) Else Return GenerateNothingLiteral() End If End Function Private Function GenerateNothingLiteral() As ExpressionSyntax Return SyntaxFactory.NothingLiteralExpression(SyntaxFactory.Token(SyntaxKind.NothingKeyword)) End Function Private Function GenerateDateLiteralExpression(value As Date) As ExpressionSyntax Dim literal = SymbolDisplay.FormatPrimitive(value, quoteStrings:=False, useHexadecimalNumbers:=False) Return SyntaxFactory.DateLiteralExpression( SyntaxFactory.DateLiteralToken(literal, value)) End Function Private Function GenerateStringLiteralExpression(type As ITypeSymbol, value As String) As ExpressionSyntax Dim pieces = StringPiece.Split(value) If pieces.Count = 0 Then Return SyntaxFactory.StringLiteralExpression(SyntaxFactory.StringLiteralToken(s_doubleQuote & s_doubleQuote, String.Empty)) End If If pieces.Count = 1 AndAlso pieces(0).Kind = StringPieceKind.NonPrintable Then If Not IsSpecialType(type, SpecialType.System_String) Then Return SyntaxFactory.PredefinedCastExpression(SyntaxFactory.Token(SyntaxKind.CStrKeyword), pieces(0).GenerateExpression()) End If End If Dim expression As ExpressionSyntax = Nothing For Each piece In pieces Dim subExpression = piece.GenerateExpression() If expression Is Nothing Then expression = subExpression Else expression = SyntaxFactory.ConcatenateExpression(expression, subExpression) End If Next Return expression End Function Private Function GenerateMemberAccessExpression(ParamArray names As String()) As MemberAccessExpressionSyntax Dim expression As ExpressionSyntax = SyntaxFactory.GlobalName() For Each name In names expression = SyntaxFactory.SimpleMemberAccessExpression( expression, SyntaxFactory.Token(SyntaxKind.DotToken), SyntaxFactory.IdentifierName(name)) Next Return DirectCast(expression, MemberAccessExpressionSyntax).WithAdditionalAnnotations(Simplifier.Annotation) End Function Private Function GenerateChrWExpression(c As Char) As InvocationExpressionSyntax Dim access = GenerateMemberAccessExpression("Microsoft", "VisualBasic", "Strings", "ChrW") Dim value = AscW(c) Dim argument = SyntaxFactory.SimpleArgument( SyntaxFactory.NumericLiteralExpression( SyntaxFactory.IntegerLiteralToken(value.ToString(Nothing, CultureInfo.InvariantCulture), LiteralBase.Decimal, TypeCharacter.None, CULng(value)))) Dim invocation = SyntaxFactory.InvocationExpression( access, SyntaxFactory.ArgumentList(SyntaxFactory.SingletonSeparatedList(Of ArgumentSyntax)(argument))) Return invocation.WithAdditionalAnnotations(Simplifier.Annotation) End Function Private Function GenerateNonNegativeIntegralLiteralExpression(Of TStructure As IEquatable(Of TStructure))( type As ITypeSymbol, specialType As SpecialType, value As TStructure, canUseFieldReference As Boolean, specialValues As IEnumerable(Of KeyValuePair(Of TStructure, String))) As ExpressionSyntax Return GenerateIntegralLiteralExpression( type, specialType, value, canUseFieldReference, specialValues, Function(v) False, Function(v) Throw New InvalidOperationException() End Function, Nothing) End Function Private Function GenerateIntegralLiteralExpression(Of TStructure As IEquatable(Of TStructure))( type As ITypeSymbol, specialType As SpecialType, value As TStructure, canUseFieldReference As Boolean, specialValues As IEnumerable(Of KeyValuePair(Of TStructure, String)), isNegative As Func(Of TStructure, Boolean), negate As Func(Of TStructure, TStructure), integerMinValueString As String) As ExpressionSyntax If canUseFieldReference Then Dim field = GenerateFieldReference(specialType, value, specialValues) If field IsNot Nothing Then Return field End If End If Dim negative = isNegative(value) Dim nonNegativeValue = If(negative, negate(value), value) Dim typeSuffix As TypeCharacter = TypeCharacter.None Dim suffix As String = String.Empty DetermineSuffix(type, nonNegativeValue, typeSuffix, suffix) Dim literal = If(negative AndAlso nonNegativeValue.Equals(value), integerMinValueString, DirectCast(nonNegativeValue, IFormattable).ToString(Nothing, CultureInfo.InvariantCulture) & suffix) Dim expression As ExpressionSyntax = SyntaxFactory.NumericLiteralExpression(SyntaxFactory.IntegerLiteralToken( literal, LiteralBase.Decimal, typeSuffix, IntegerUtilities.ToUInt64(nonNegativeValue))) If negative Then expression = SyntaxFactory.UnaryMinusExpression(expression) End If If TypeOf value Is Byte AndAlso Not IsSpecialType(type, SpecialType.System_Byte) Then Return SyntaxFactory.PredefinedCastExpression(SyntaxFactory.Token(SyntaxKind.CByteKeyword), expression) ElseIf TypeOf value Is SByte AndAlso Not IsSpecialType(type, SpecialType.System_SByte) Then Return SyntaxFactory.PredefinedCastExpression(SyntaxFactory.Token(SyntaxKind.CSByteKeyword), expression) End If Return expression End Function Private Sub DetermineSuffix(type As ITypeSymbol, value As Object, ByRef typeSuffix As TypeCharacter, ByRef suffix As String) If TypeOf value Is Short AndAlso Not IsSpecialType(type, SpecialType.System_Int16) Then typeSuffix = TypeCharacter.ShortLiteral suffix = "S" ElseIf TypeOf value Is Long AndAlso Not IsSpecialType(type, SpecialType.System_Int64) Then typeSuffix = TypeCharacter.LongLiteral suffix = "L" ElseIf TypeOf value Is Decimal Then Dim d = DirectCast(value, Decimal) Dim scale = d.GetScale() Dim typeIsNotDecimal = Not IsSpecialType(type, SpecialType.System_Decimal) Dim scaleIsNotZero = scale <> 0 Dim valueIsOutOfRange = d <= Long.MinValue OrElse d > Long.MaxValue If typeIsNotDecimal OrElse scaleIsNotZero OrElse valueIsOutOfRange Then typeSuffix = TypeCharacter.DecimalLiteral suffix = "D" End If ElseIf TypeOf value Is UShort AndAlso Not IsSpecialType(type, SpecialType.System_UInt16) Then typeSuffix = TypeCharacter.UShortLiteral suffix = "US" ElseIf TypeOf value Is UInteger AndAlso Not IsSpecialType(type, SpecialType.System_UInt32) Then typeSuffix = TypeCharacter.UIntegerLiteral suffix = "UI" ElseIf TypeOf value Is ULong Then Dim d = DirectCast(value, ULong) Dim typeIsNotULong = Not IsSpecialType(type, SpecialType.System_UInt64) Dim valueIsOutOfRange = d > Long.MaxValue If typeIsNotULong OrElse valueIsOutOfRange Then typeSuffix = TypeCharacter.ULongLiteral suffix = "UL" End If ElseIf TypeOf value Is Single AndAlso Not IsSpecialType(type, SpecialType.System_Single) Then typeSuffix = TypeCharacter.SingleLiteral suffix = "F" ElseIf TypeOf value Is Double AndAlso Not IsSpecialType(type, SpecialType.System_Double) Then typeSuffix = TypeCharacter.DoubleLiteral suffix = "R" End If End Sub Private Function GenerateDoubleLiteralExpression(type As ITypeSymbol, value As Double, canUseFieldReference As Boolean) As ExpressionSyntax If Not canUseFieldReference Then If Double.IsNaN(value) Then Return SyntaxFactory.DivideExpression( GenerateFloatLiteral(0.0, "0.0"), GenerateFloatLiteral(0.0, "0.0")) ElseIf Double.IsPositiveInfinity(value) Then Return SyntaxFactory.DivideExpression( GenerateFloatLiteral(1.0, "1.0"), GenerateFloatLiteral(0.0, "0.0")) ElseIf (Double.IsNegativeInfinity(value)) Then Return SyntaxFactory.DivideExpression( SyntaxFactory.UnaryMinusExpression(GenerateFloatLiteral(1.0, "1.0")), GenerateFloatLiteral(0.0, "0.0")) End If End If Return GenerateFloatLiteralExpression( type, SpecialType.System_Double, value, canUseFieldReference, LiteralSpecialValues.DoubleSpecialValues, Function(t) t < 0, Function(t) -t) End Function Private Function GenerateSingleLiteralExpression( type As ITypeSymbol, value As Single, canUseFieldReference As Boolean) As ExpressionSyntax If Not canUseFieldReference Then If Double.IsNaN(value) Then Return SyntaxFactory.DivideExpression( GenerateFloatLiteral(0.0, "0.0F"), GenerateFloatLiteral(0.0, "0.0F")) ElseIf Double.IsPositiveInfinity(value) Then Return SyntaxFactory.DivideExpression( GenerateFloatLiteral(1.0, "1.0F"), GenerateFloatLiteral(0.0, "0.0F")) ElseIf (Double.IsNegativeInfinity(value)) Then Return SyntaxFactory.DivideExpression( SyntaxFactory.UnaryMinusExpression(GenerateFloatLiteral(1.0, "1.0F")), GenerateFloatLiteral(0.0, "0.0F")) End If End If Return GenerateFloatLiteralExpression( type, SpecialType.System_Single, value, canUseFieldReference, LiteralSpecialValues.SingleSpecialValues, Function(t) t < 0, Function(t) -t) End Function Private Function GenerateFloatLiteralExpression(Of TStructure)( type As ITypeSymbol, specialType As SpecialType, value As TStructure, canUseFieldReference As Boolean, specialValues As IEnumerable(Of KeyValuePair(Of TStructure, String)), isNegative As Func(Of TStructure, Boolean), negate As Func(Of TStructure, TStructure)) As ExpressionSyntax If canUseFieldReference Then Dim field = GenerateFieldReference(specialType, value, specialValues) If field IsNot Nothing Then Return field End If End If Dim negative = isNegative(value) If negative Then value = negate(value) End If Dim typeSuffix As TypeCharacter = TypeCharacter.None Dim suffix As String = String.Empty DetermineSuffix(type, value, typeSuffix, suffix) Dim literal = DirectCast(value, IFormattable).ToString("R", CultureInfo.InvariantCulture) & suffix Dim literalSyntax As ExpressionSyntax = GenerateFloatLiteral(Convert.ToDouble(value), literal, typeSuffix) Return If(negative, SyntaxFactory.UnaryMinusExpression(literalSyntax), literalSyntax) End Function Private Function GenerateFloatLiteral(value As Double, literal As String, Optional typeSuffix As TypeCharacter = TypeCharacter.None) As LiteralExpressionSyntax Return SyntaxFactory.NumericLiteralExpression(SyntaxFactory.FloatingLiteralToken( literal, typeSuffix, value)) End Function Private Function GenerateCharLiteralExpression(c As Char) As ExpressionSyntax Dim pieces = StringPiece.Split(c.ToString()) Dim piece = pieces(0) If piece.Kind = StringPieceKind.Normal Then Return SyntaxFactory.CharacterLiteralExpression(SyntaxFactory.CharacterLiteralToken( SymbolDisplay.FormatPrimitive(c, quoteStrings:=True, useHexadecimalNumbers:=False), c)) End If Return GenerateChrWExpression(c) End Function Private Function GenerateDecimalLiteralExpression(type As ITypeSymbol, value As Decimal, canUseFieldReference As Boolean) As ExpressionSyntax If canUseFieldReference Then Dim field = GenerateFieldReference(SpecialType.System_Decimal, value, LiteralSpecialValues.DecimalSpecialValues) If field IsNot Nothing Then Return field End If End If Dim typeSuffix As TypeCharacter = TypeCharacter.None Dim suffix As String = String.Empty DetermineSuffix(type, value, typeSuffix, suffix) Dim literal = value.ToString(Nothing, CultureInfo.InvariantCulture) & suffix Return SyntaxFactory.NumericLiteralExpression(SyntaxFactory.DecimalLiteralToken(literal, typeSuffix, value)) End Function Private Function AddSpecialTypeAnnotation(type As SpecialType, expression As MemberAccessExpressionSyntax) As MemberAccessExpressionSyntax If SpecialType.None <> type Then Return expression.WithAdditionalAnnotations(SpecialTypeAnnotation.Create(type)) End If Return expression End Function Private Function GenerateFieldReference(Of TStructure)(type As SpecialType, value As Object, specialValues As IEnumerable(Of KeyValuePair(Of TStructure, String))) As MemberAccessExpressionSyntax For Each specialValue In specialValues If specialValue.Key.Equals(value) Then Dim memberAccess = AddSpecialTypeAnnotation(type, GenerateMemberAccessExpression("System", GetType(TStructure).Name)) Return SyntaxFactory.SimpleMemberAccessExpression(memberAccess, SyntaxFactory.Token(SyntaxKind.DotToken), SyntaxFactory.IdentifierName(specialValue.Value)) _ .WithAdditionalAnnotations(Simplifier.Annotation) End If Next Return Nothing End Function End Module End Namespace