|
' 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 Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.CodeGeneration
Imports Microsoft.CodeAnalysis.CodeGeneration.CodeGenerationHelpers
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeGeneration
Friend Module EnumMemberGenerator
Friend Function AddEnumMemberTo(
destination As EnumBlockSyntax,
enumMember As IFieldSymbol,
options As CodeGenerationContextInfo) As EnumBlockSyntax
Dim member = GenerateEnumMemberDeclaration(enumMember, destination, options)
If member Is Nothing Then
Return destination
End If
Dim members = New List(Of StatementSyntax)()
members.AddRange(destination.Members)
members.Add(member)
Dim leadingTrivia = destination.EndEnumStatement.GetLeadingTrivia()
Return destination.WithMembers(SyntaxFactory.List(Of StatementSyntax)(members)).
WithEndEnumStatement(If(destination.EndEnumStatement.IsMissing, SyntaxFactory.EndEnumStatement(), destination.EndEnumStatement))
End Function
Public Function GenerateEnumMemberDeclaration(enumMember As IFieldSymbol,
enumDeclarationOpt As EnumBlockSyntax,
options As CodeGenerationContextInfo) As EnumMemberDeclarationSyntax
' We never generate the special enum backing field.
If enumMember.Name = WellKnownMemberNames.EnumBackingFieldName Then
Return Nothing
End If
Dim reusableSyntax = GetReuseableSyntaxNodeForSymbol(Of EnumMemberDeclarationSyntax)(enumMember, options)
If reusableSyntax IsNot Nothing Then
Return reusableSyntax
End If
Dim value = CreateEnumMemberValue(enumDeclarationOpt, enumMember)
Dim member = SyntaxFactory.EnumMemberDeclaration(enumMember.Name.ToIdentifierToken()) _
.WithInitializer(If(value Is Nothing, Nothing, SyntaxFactory.EqualsValue(value:=value)))
Return AddFormatterAndCodeGeneratorAnnotationsTo(ConditionallyAddDocumentationCommentTo(member, enumMember, options))
End Function
Private Function CreateEnumMemberValue(destinationOpt As EnumBlockSyntax, enumMember As IFieldSymbol) As ExpressionSyntax
If Not enumMember.HasConstantValue Then
Return Nothing
End If
If TypeOf enumMember.ConstantValue IsNot Byte AndAlso
TypeOf enumMember.ConstantValue IsNot SByte AndAlso
TypeOf enumMember.ConstantValue IsNot UShort AndAlso
TypeOf enumMember.ConstantValue IsNot Short AndAlso
TypeOf enumMember.ConstantValue IsNot Integer AndAlso
TypeOf enumMember.ConstantValue IsNot UInteger AndAlso
TypeOf enumMember.ConstantValue IsNot Long AndAlso
TypeOf enumMember.ConstantValue IsNot ULong Then
Return Nothing
End If
Dim value = IntegerUtilities.ToInt64(enumMember.ConstantValue)
If destinationOpt IsNot Nothing Then
If destinationOpt.Members.Count = 0 Then
If value = 0 Then
Return Nothing
End If
Else
' if nothing in the enum has any initializers and our value is appropriate for
' the end, then don't generate a value.
If destinationOpt.Members.Count = value AndAlso
destinationOpt.Members.OfType(Of EnumMemberDeclarationSyntax).All(Function(m) m.Initializer Is Nothing) Then
Return Nothing
End If
' Existing members, try to stay consistent with their style.
Dim lastMember = destinationOpt.Members.OfType(Of EnumMemberDeclarationSyntax).LastOrDefault(Function(m) m.Initializer IsNot Nothing)
If lastMember IsNot Nothing Then
Dim lastExpression = lastMember.Initializer.Value
If lastExpression.Kind = SyntaxKind.LeftShiftExpression AndAlso IntegerUtilities.HasOneBitSet(value) Then
Dim binaryExpression = DirectCast(lastExpression, BinaryExpressionSyntax)
If binaryExpression.Left.Kind = SyntaxKind.NumericLiteralExpression Then
Dim numericLiteral = DirectCast(binaryExpression.Left, LiteralExpressionSyntax)
If numericLiteral.Token.ValueText = "1" Then
' The user is left shifting ones, stick with that pattern
Dim shiftValue = IntegerUtilities.LogBase2(value)
' Using the numericLiteral text will ensure the correct type character, ignoring the None that is passed in below
Return SyntaxFactory.LeftShiftExpression(
left:=SyntaxFactory.NumericLiteralExpression(SyntaxFactory.IntegerLiteralToken(numericLiteral.Token.Text, LiteralBase.Decimal, TypeCharacter.None, 1)),
right:=SyntaxFactory.NumericLiteralExpression(SyntaxFactory.IntegerLiteralToken(shiftValue.ToString(), LiteralBase.Decimal, TypeCharacter.None, IntegerUtilities.ToUnsigned(shiftValue))))
End If
End If
ElseIf lastExpression.Kind = SyntaxKind.NumericLiteralExpression Then
Dim numericLiteral = DirectCast(lastExpression, LiteralExpressionSyntax)
Dim numericToken = numericLiteral.Token
Dim numericText = numericToken.ToString()
If numericText.StartsWith("&H", StringComparison.OrdinalIgnoreCase) Then
Dim firstTwoChars = numericText.Substring(0, 2)
If numericText.EndsWith("US", StringComparison.OrdinalIgnoreCase) AndAlso
value >= UShort.MinValue AndAlso value <= UShort.MaxValue Then
Dim ushortValue = CUShort(value)
Dim lastTwoChars = numericText.Substring(numericText.Length - 2, 2)
Return SyntaxFactory.NumericLiteralExpression(
SyntaxFactory.IntegerLiteralToken(firstTwoChars + ushortValue.ToString("X") + lastTwoChars, LiteralBase.Hexadecimal, TypeCharacter.UShortLiteral, IntegerUtilities.ToUnsigned(ushortValue)))
ElseIf numericText.EndsWith("S", StringComparison.OrdinalIgnoreCase) AndAlso
value >= Short.MinValue AndAlso value <= Short.MaxValue Then
Dim shortValue = CShort(value)
Return SyntaxFactory.NumericLiteralExpression(
SyntaxFactory.IntegerLiteralToken(firstTwoChars + shortValue.ToString("X") + numericText.Last(), LiteralBase.Hexadecimal, TypeCharacter.ShortLiteral, IntegerUtilities.ToUnsigned(shortValue)))
Else
Return SyntaxFactory.NumericLiteralExpression(
SyntaxFactory.IntegerLiteralToken(firstTwoChars + value.ToString("X"), LiteralBase.Hexadecimal, TypeCharacter.None, IntegerUtilities.ToUnsigned(value)))
End If
ElseIf numericText.StartsWith("&O", StringComparison.OrdinalIgnoreCase) Then
Return SyntaxFactory.NumericLiteralExpression(SyntaxFactory.IntegerLiteralToken(numericText.Substring(0, 2) + Convert.ToString(value, 8), LiteralBase.Octal, TypeCharacter.None, IntegerUtilities.ToUnsigned(value)))
ElseIf numericText.StartsWith("&B", StringComparison.OrdinalIgnoreCase) Then
Return SyntaxFactory.NumericLiteralExpression(SyntaxFactory.IntegerLiteralToken(numericText.Substring(0, 2) + Convert.ToString(value, 2), LiteralBase.Binary, TypeCharacter.None, IntegerUtilities.ToUnsigned(value)))
End If
End If
End If
End If
End If
Dim namedType = TryCast(enumMember.Type, INamedTypeSymbol)
Dim underlyingType = If(namedType IsNot Nothing, namedType.EnumUnderlyingType, Nothing)
Return ExpressionGenerator.GenerateNonEnumValueExpression(
underlyingType,
enumMember.ConstantValue,
canUseFieldReference:=True)
End Function
End Module
End Namespace
|