File: src\Workspaces\SharedUtilitiesAndExtensions\Compiler\Core\Utilities\EnumValueUtilities.cs
Web Access
Project: src\src\CodeStyle\Core\Analyzers\Microsoft.CodeAnalysis.CodeStyle.csproj (Microsoft.CodeAnalysis.CodeStyle)
// 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.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.Shared.Utilities;
 
internal static class EnumValueUtilities
{
    /// <summary>
    /// Determines, using heuristics, what the next likely value is in this enum.
    /// </summary>
    public static object GetNextEnumValue(INamedTypeSymbol enumType)
    {
        var orderedExistingConstants = enumType.GetMembers()
                                        .OfType<IFieldSymbol>()
                                        .Where(f => f.HasConstantValue)
                                        .Select(f => f.ConstantValue)
                                        .OfType<IComparable>()
                                        .OrderByDescending(f => f).ToList();
        var existingConstants = orderedExistingConstants.ToSet();
 
        if (LooksLikeFlagsEnum(orderedExistingConstants))
        {
            if (orderedExistingConstants.Count == 0)
            {
                return CreateOne(enumType.EnumUnderlyingType.SpecialType);
            }
            else
            {
                var largest = orderedExistingConstants[0];
                return Multiply(largest, 2);
            }
        }
        else if (orderedExistingConstants.Count > 0)
        {
            for (uint i = 1; i <= existingConstants.Count; i++)
            {
                var nextValue = Add(orderedExistingConstants[0], i);
                if (!existingConstants.Contains(nextValue))
                {
                    return nextValue;
                }
            }
        }
 
        return null;
    }
 
    private static object CreateOne(SpecialType specialType)
        => specialType switch
        {
            SpecialType.System_SByte => (sbyte)1,
            SpecialType.System_Byte => (byte)1,
            SpecialType.System_Int16 => (short)1,
            SpecialType.System_UInt16 => (ushort)1,
            SpecialType.System_Int32 => 1,
            SpecialType.System_UInt32 => (uint)1,
            SpecialType.System_Int64 => (long)1,
            SpecialType.System_UInt64 => (ulong)1,
            _ => 1,
        };
 
    private static IComparable Multiply(IComparable value, uint number)
        => value switch
        {
            long v => unchecked(v * number),
            ulong v => unchecked(v * number),
            int v => unchecked((int)(v * number)),
            uint v => unchecked(v * number),
            short v => unchecked((short)(v * number)),
            ushort v => unchecked((ushort)(v * number)),
            sbyte v => unchecked((sbyte)(v * number)),
            byte v => unchecked((byte)(v * number)),
            _ => null,
        };
 
    private static IComparable Add(IComparable value, uint number)
        => value switch
        {
            long v => unchecked(v + number),
            ulong v => unchecked(v + number),
            int v => unchecked((int)(v + number)),
            uint v => unchecked(v + number),
            short v => unchecked((short)(v + number)),
            ushort v => unchecked((ushort)(v + number)),
            sbyte v => unchecked((sbyte)(v + number)),
            byte v => unchecked((byte)(v + number)),
            _ => null,
        };
 
    private static bool GreaterThanOrEqualsZero(IComparable value)
        => value switch
        {
            long v => v >= 0,
            ulong v => v >= 0,
            int v => v >= 0,
            uint v => v >= 0,
            short v => v >= 0,
            ushort v => v >= 0,
            sbyte v => v >= 0,
            byte v => v >= 0,
            _ => false,
        };
 
    private static bool LooksLikeFlagsEnum(List<IComparable> existingConstants)
    {
        if (existingConstants.Count >= 1 &&
           IntegerUtilities.HasOneBitSet(existingConstants[0]) &&
           Multiply(existingConstants[0], 2).CompareTo(existingConstants[0]) > 0 &&
           existingConstants.All(GreaterThanOrEqualsZero))
        {
            if (existingConstants.Count == 1)
            {
                return true;
            }
 
            if (existingConstants[0].Equals(Multiply(existingConstants[1], 2)))
            {
                // If you only have two values, and they're 1 and 2, then don't assume this is a
                // flags enum.  The person could have been trying to type, 1, 2, 3 instead.
                if (existingConstants[0].Equals(Convert.ChangeType(2, existingConstants[0].GetType())) &&
                    existingConstants[1].Equals(Convert.ChangeType(1, existingConstants[1].GetType())))
                {
                    return false;
                }
 
                return true;
            }
        }
 
        return false;
    }
}