EnumMemberGenerator.cs 7.5 KB
Newer Older
J
Jonathon Marolf 已提交
1 2 3
// 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.
P
Pilchie 已提交
4

J
Jared Parsons 已提交
5 6
#nullable disable

P
Pilchie 已提交
7 8 9 10 11 12 13 14
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CodeGeneration;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Utilities;

V
VSadov 已提交
15 16 17
using static Microsoft.CodeAnalysis.CodeGeneration.CodeGenerationHelpers;
using static Microsoft.CodeAnalysis.CSharp.CodeGeneration.CSharpCodeGenerationHelpers;

P
Pilchie 已提交
18 19
namespace Microsoft.CodeAnalysis.CSharp.CodeGeneration
{
20
    internal static class EnumMemberGenerator
P
Pilchie 已提交
21 22 23 24 25 26 27 28 29 30 31 32
    {
        internal static EnumDeclarationSyntax AddEnumMemberTo(EnumDeclarationSyntax destination, IFieldSymbol enumMember, CodeGenerationOptions options)
        {
            var members = new List<SyntaxNodeOrToken>();
            members.AddRange(destination.Members.GetWithSeparators());

            var member = GenerateEnumMemberDeclaration(enumMember, destination, options);

            if (members.Count == 0)
            {
                members.Add(member);
            }
33
            else if (members.LastOrDefault().Kind() == SyntaxKind.CommaToken)
P
Pilchie 已提交
34 35 36 37 38 39 40 41 42 43 44 45 46
            {
                members.Add(member);
                members.Add(SyntaxFactory.Token(SyntaxKind.CommaToken));
            }
            else
            {
                var lastMember = members.Last();
                var trailingTrivia = lastMember.GetTrailingTrivia();
                members[members.Count - 1] = lastMember.WithTrailingTrivia();
                members.Add(SyntaxFactory.Token(SyntaxKind.CommaToken).WithTrailingTrivia(trailingTrivia));
                members.Add(member);
            }

47 48
            return destination.EnsureOpenAndCloseBraceTokens()
                .WithMembers(SyntaxFactory.SeparatedList<EnumMemberDeclarationSyntax>(members));
P
Pilchie 已提交
49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
        }

        public static EnumMemberDeclarationSyntax GenerateEnumMemberDeclaration(
            IFieldSymbol enumMember,
            EnumDeclarationSyntax destinationOpt,
            CodeGenerationOptions options)
        {
            var reusableSyntax = GetReuseableSyntaxNodeForSymbol<EnumMemberDeclarationSyntax>(enumMember, options);
            if (reusableSyntax != null)
            {
                return reusableSyntax;
            }

            var value = CreateEnumMemberValue(destinationOpt, enumMember);
            var member = SyntaxFactory.EnumMemberDeclaration(enumMember.Name.ToIdentifierToken())
                .WithEqualsValue(value == null ? null : SyntaxFactory.EqualsValueClause(value: value));

C
CyrusNajmabadi 已提交
66
            return AddFormatterAndCodeGeneratorAnnotationsTo(
P
Pilchie 已提交
67 68 69 70 71
                ConditionallyAddDocumentationCommentTo(member, enumMember, options));
        }

        private static ExpressionSyntax CreateEnumMemberValue(EnumDeclarationSyntax destinationOpt, IFieldSymbol enumMember)
        {
72
            if (!enumMember.HasConstantValue)
P
Pilchie 已提交
73 74 75 76
            {
                return null;
            }

77 78 79 80 81 82 83 84 85 86 87 88 89 90
            if (!(enumMember.ConstantValue is byte) &&
                !(enumMember.ConstantValue is sbyte) &&
                !(enumMember.ConstantValue is ushort) &&
                !(enumMember.ConstantValue is short) &&
                !(enumMember.ConstantValue is int) &&
                !(enumMember.ConstantValue is uint) &&
                !(enumMember.ConstantValue is long) &&
                !(enumMember.ConstantValue is ulong))
            {
                return null;
            }

            var value = IntegerUtilities.ToInt64(enumMember.ConstantValue);

P
Pilchie 已提交
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
            if (destinationOpt != null)
            {
                if (destinationOpt.Members.Count == 0)
                {
                    if (value == 0)
                    {
                        return null;
                    }
                }
                else
                {
                    // Don't generate an initializer if no other members have them, and our value
                    // would be correctly inferred from our position.
                    if (destinationOpt.Members.Count == value &&
                        destinationOpt.Members.All(m => m.EqualsValue == null))
                    {
                        return null;
                    }

                    // Existing members, try to stay consistent with their style.
                    var lastMember = destinationOpt.Members.LastOrDefault(m => m.EqualsValue != null);
                    if (lastMember != null)
                    {
                        var lastExpression = lastMember.EqualsValue.Value;
115
                        if (lastExpression.Kind() == SyntaxKind.LeftShiftExpression &&
P
Pilchie 已提交
116 117 118
                            IntegerUtilities.HasOneBitSet(value))
                        {
                            var binaryExpression = (BinaryExpressionSyntax)lastExpression;
119
                            if (binaryExpression.Left.Kind() == SyntaxKind.NumericLiteralExpression)
P
Pilchie 已提交
120 121 122 123 124 125
                            {
                                var numericLiteral = (LiteralExpressionSyntax)binaryExpression.Left;
                                if (numericLiteral.Token.ValueText == "1")
                                {
                                    // The user is left shifting ones, stick with that pattern
                                    var shiftValue = IntegerUtilities.LogBase2(value);
126 127

                                    // Re-use the numericLiteral text so type suffixes match too
P
Pilchie 已提交
128 129
                                    return SyntaxFactory.BinaryExpression(
                                        SyntaxKind.LeftShiftExpression,
130
                                        SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(numericLiteral.Token.Text, 1)),
P
Pilchie 已提交
131 132 133 134
                                        SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression, SyntaxFactory.Literal(shiftValue.ToString(), shiftValue)));
                                }
                            }
                        }
C
Cyrus Najmabadi 已提交
135
                        else if (lastExpression.IsKind(SyntaxKind.NumericLiteralExpression, out LiteralExpressionSyntax numericLiteral))
P
Pilchie 已提交
136 137 138 139
                        {
                            var numericToken = numericLiteral.Token;
                            var numericText = numericToken.ToString();

140
                            if (numericText.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
P
Pilchie 已提交
141 142 143 144 145
                            {
                                // Hex
                                return SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression,
                                    SyntaxFactory.Literal(numericText.Substring(0, 2) + value.ToString("X"), value));
                            }
146 147 148 149 150
                            else if (numericText.StartsWith("0b", StringComparison.OrdinalIgnoreCase))
                            {
                                return SyntaxFactory.LiteralExpression(SyntaxKind.NumericLiteralExpression,
                                    SyntaxFactory.Literal(numericText.Substring(0, 2) + Convert.ToString(value, 2), value));
                            }
P
Pilchie 已提交
151 152 153 154 155 156
                        }
                    }
                }
            }

            var namedType = enumMember.Type as INamedTypeSymbol;
C
Cyrus Najmabadi 已提交
157
            var underlyingType = namedType?.EnumUnderlyingType;
P
Pilchie 已提交
158 159 160 161 162 163 164

            return ExpressionGenerator.GenerateNonEnumValueExpression(
                underlyingType,
                enumMember.ConstantValue,
                canUseFieldReference: true);
        }
    }
165
}