File: Microsoft.NetCore.Analyzers\InteropServices\PlatformCompatibilityAnalyzer.cs
Web Access
Project: src\sdk\src\Microsoft.CodeAnalysis.NetAnalyzers\src\Microsoft.CodeAnalysis.NetAnalyzers\Microsoft.CodeAnalysis.NetAnalyzers.csproj (Microsoft.CodeAnalysis.NetAnalyzers)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using Analyzer.Utilities;
using Analyzer.Utilities.Extensions;
using Analyzer.Utilities.PooledObjects;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.GlobalFlowStateAnalysis;
using Microsoft.CodeAnalysis.FlowAnalysis.DataFlow.ValueContentAnalysis;
using Microsoft.CodeAnalysis.Operations;

namespace Microsoft.NetCore.Analyzers.InteropServices
{
    using static MicrosoftNetCoreAnalyzersResources;

    /// <summary>
    /// CA1416: <inheritdoc cref="PlatformCompatibilityTitle"/>
    /// CA1422: <inheritdoc cref="PlatformCompatibilityTitle"/>
    ///
    /// It finds usage of platform-specific or unsupported APIs and diagnoses if the
    /// API is guarded by platform check or if it is annotated with corresponding platform specific attribute.
    /// If using the platform-specific API is not safe it reports diagnostics.
    ///
    /// </summary>
    [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)]
    public sealed partial class PlatformCompatibilityAnalyzer : DiagnosticAnalyzer
    {
        internal const string SupportRuleId = "CA1416";
        internal const string ObsoletedRuleId = "CA1422";
        private static readonly ImmutableArray<string> s_osPlatformAttributes = ImmutableArray.Create(SupportedOSPlatformAttribute, UnsupportedOSPlatformAttribute, ObsoletedOSPlatformAttribute);

        private static readonly LocalizableString s_localizableTitle = CreateLocalizableResourceString(nameof(PlatformCompatibilityTitle));
        private static readonly LocalizableString s_localizableDescription = CreateLocalizableResourceString(nameof(PlatformCompatibilityDescription));

        // We are adding the new attributes into older versions of .Net 5.0, so there could be multiple referenced assemblies each with their own
        // version of internal attribute type which will cause ambiguity, to avoid that we are comparing the attributes by their name
        private const string ObsoletedOSPlatformAttribute = nameof(ObsoletedOSPlatformAttribute);
        private const string SupportedOSPlatformAttribute = nameof(SupportedOSPlatformAttribute);
        private const string UnsupportedOSPlatformAttribute = nameof(UnsupportedOSPlatformAttribute);
        private const string UnsupportedOSPlatformGuardAttribute = nameof(UnsupportedOSPlatformGuardAttribute);
        private const string SupportedOSPlatformGuardAttribute = nameof(SupportedOSPlatformGuardAttribute);

        // Platform guard method name, prefix, suffix
        private const string IsOSPlatform = nameof(IsOSPlatform);
        private const string IsPrefix = "Is";
        private const string OptionalSuffix = "VersionAtLeast";
        private const string NetCoreAppIdentifier = ".NETCoreApp";
        private const string macOS = nameof(macOS);
        private const string OSX = nameof(OSX);
        private const string MacSlashOSX = "macOS/OSX";
        private static readonly Version EmptyVersion = new(0, 0);

        internal static readonly DiagnosticDescriptor OnlySupportedCsReachable = DiagnosticDescriptorHelper.Create(
            SupportRuleId,
            s_localizableTitle,
            CreateLocalizableResourceString(nameof(PlatformCompatibilityOnlySupportedCsReachableMessage)),
            DiagnosticCategory.Interoperability,
            RuleLevel.BuildWarning,
            description: s_localizableDescription,
            isPortedFxCopRule: false,
            isDataflowRule: false);

        internal static readonly DiagnosticDescriptor OnlySupportedCsUnreachable = DiagnosticDescriptorHelper.Create(
            SupportRuleId,
            s_localizableTitle,
            CreateLocalizableResourceString(nameof(PlatformCompatibilityOnlySupportedCsUnreachableMessage)),
            DiagnosticCategory.Interoperability,
            RuleLevel.BuildWarning,
            description: s_localizableDescription,
            isPortedFxCopRule: false,
            isDataflowRule: false);

        internal static readonly DiagnosticDescriptor OnlySupportedCsAllPlatforms = DiagnosticDescriptorHelper.Create(
            SupportRuleId,
            s_localizableTitle,
            CreateLocalizableResourceString(nameof(PlatformCompatibilityOnlySupportedCsAllPlatformMessage)),
            DiagnosticCategory.Interoperability,
            RuleLevel.BuildWarning,
            description: s_localizableDescription,
            isPortedFxCopRule: false,
            isDataflowRule: false);

        internal static readonly DiagnosticDescriptor SupportedCsAllPlatforms = DiagnosticDescriptorHelper.Create(
            SupportRuleId,
            s_localizableTitle,
            CreateLocalizableResourceString(nameof(PlatformCompatibilitySupportedCsAllPlatformMessage)),
            DiagnosticCategory.Interoperability,
            RuleLevel.BuildWarning,
            description: s_localizableDescription,
            isPortedFxCopRule: false,
            isDataflowRule: false);

        internal static readonly DiagnosticDescriptor SupportedCsReachable = DiagnosticDescriptorHelper.Create(
            SupportRuleId,
            s_localizableTitle,
            CreateLocalizableResourceString(nameof(PlatformCompatibilitySupportedCsReachableMessage)),
            DiagnosticCategory.Interoperability,
            RuleLevel.BuildWarning,
            description: s_localizableDescription,
            isPortedFxCopRule: false,
            isDataflowRule: false);

        internal static readonly DiagnosticDescriptor UnsupportedCsAllPlatforms = DiagnosticDescriptorHelper.Create(
            SupportRuleId,
            s_localizableTitle,
            CreateLocalizableResourceString(nameof(PlatformCompatibilityUnsupportedCsAllPlatformMessage)),
            DiagnosticCategory.Interoperability,
            RuleLevel.BuildWarning,
            description: s_localizableDescription,
            isPortedFxCopRule: false,
            isDataflowRule: false);

        internal static readonly DiagnosticDescriptor UnsupportedCsReachable = DiagnosticDescriptorHelper.Create(
            SupportRuleId,
            s_localizableTitle,
            CreateLocalizableResourceString(nameof(PlatformCompatibilityUnsupportedCsReachableMessage)),
            DiagnosticCategory.Interoperability,
            RuleLevel.BuildWarning,
            description: s_localizableDescription,
            isPortedFxCopRule: false,
            isDataflowRule: false);

        internal static readonly DiagnosticDescriptor ObsoletedCsAllPlatforms = DiagnosticDescriptorHelper.Create(
            ObsoletedRuleId,
            s_localizableTitle,
            CreateLocalizableResourceString(nameof(PlatformCompatibilityObsoletedCsAllPlatformMessage)),
            DiagnosticCategory.Interoperability,
            RuleLevel.BuildWarning,
            description: s_localizableDescription,
            isPortedFxCopRule: false,
            isDataflowRule: false);

        internal static readonly DiagnosticDescriptor ObsoletedCsReachable = DiagnosticDescriptorHelper.Create(
            ObsoletedRuleId,
            s_localizableTitle,
            CreateLocalizableResourceString(nameof(PlatformCompatibilityObsoletedCsReachableMessage)),
            DiagnosticCategory.Interoperability,
            RuleLevel.BuildWarning,
            description: s_localizableDescription,
            isPortedFxCopRule: false,
            isDataflowRule: false);

        public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(OnlySupportedCsReachable, OnlySupportedCsUnreachable,
            OnlySupportedCsAllPlatforms, SupportedCsAllPlatforms, SupportedCsReachable, UnsupportedCsAllPlatforms, UnsupportedCsReachable, ObsoletedCsAllPlatforms, ObsoletedCsReachable);

        public override void Initialize(AnalysisContext context)
        {
            context.EnableConcurrentExecution();
            context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);

            context.RegisterCompilationStartAction(context =>
            {
                if (!PlatformAnalysisAllowed(context.Options, context.Compilation))
                {
                    return;
                }

                if (!context.Compilation.TryGetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemOperatingSystem, out var operatingSystemType))
                {
                    return;
                }

                var osPlatformType = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeInteropServicesOSPlatform);
                var runtimeInformationType = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemRuntimeInteropServicesRuntimeInformation);

                var stringType = context.Compilation.GetSpecialType(SpecialType.System_String);
                if (stringType == null)
                {
                    return;
                }

                var msBuildPlatforms = GetSupportedPlatforms(context.Options, context.Compilation);
                var runtimeIsOSPlatformMethod = runtimeInformationType?.GetMembers().OfType<IMethodSymbol>().FirstOrDefault(m =>
                        IsOSPlatform == m.Name &&
                        m.IsStatic &&
                        m.ReturnType.SpecialType == SpecialType.System_Boolean &&
                        m.Parameters.Length == 1 &&
                        m.Parameters[0].Type.Equals(osPlatformType));

                var guardMethods = GetOperatingSystemGuardMethods(runtimeIsOSPlatformMethod, operatingSystemType, out var relatedPlatforms);
                var platformSpecificMembers = new ConcurrentDictionary<ISymbol, PlatformAttributes>();
                var osPlatformCreateMethod = osPlatformType?.GetMembers("Create").OfType<IMethodSymbol>().FirstOrDefault(m =>
                    m.IsStatic &&
                    m.ReturnType.Equals(osPlatformType) &&
                    m.Parameters.Length == 1 &&
                    m.Parameters[0].Type.SpecialType == SpecialType.System_String);
                var notSupportedExceptionType = context.Compilation.GetOrCreateTypeByMetadataName(WellKnownTypeNames.SystemNotSupportedException);
                var crossPlatform = HasCrossPlatformProperty(context.Options, context.Compilation);

                context.RegisterOperationBlockStartAction(
                    context => AnalyzeOperationBlock(context, guardMethods.ToImmutableArray(), runtimeIsOSPlatformMethod, osPlatformCreateMethod, crossPlatform,
                                    osPlatformType, stringType, platformSpecificMembers, msBuildPlatforms, notSupportedExceptionType, relatedPlatforms));
            });

            static List<IMethodSymbol> GetOperatingSystemGuardMethods(IMethodSymbol? runtimeIsOSPlatformMethod,
                INamedTypeSymbol operatingSystemType, out SmallDictionary<string, (string relatedPlatform, bool isSubset)> relatedPlatforms)
            {
                relatedPlatforms = new SmallDictionary<string, (string relatedPlatform, bool isSubset)>(StringComparer.OrdinalIgnoreCase);

                var methods = FilterPlatformCheckMethods(operatingSystemType, relatedPlatforms).ToList();

                if (runtimeIsOSPlatformMethod != null)
                {
                    methods.Add(runtimeIsOSPlatformMethod);
                }

                return methods;
            }

            static IEnumerable<IMethodSymbol> FilterPlatformCheckMethods(INamedTypeSymbol symbol,
                SmallDictionary<string, (string relatedPlatform, bool isSubset)> relatedPlatforms)
            {
                return symbol.GetMembers().OfType<IMethodSymbol>().Where(m =>
                {
                    if (m.IsStatic &&
                        m.ReturnType.SpecialType == SpecialType.System_Boolean &&
                        (IsOSPlatform == m.Name || NameAndParametersValid(m)))
                    {
                        CheckDependentPlatforms(m, relatedPlatforms);
                        return true;
                    }

                    return false;
                });
            }

            static void CheckDependentPlatforms(IMethodSymbol method, SmallDictionary<string, (string relatedPlatform, bool isSubset)> relatedPlatforms)
            {
                if (TryExtractPlatformName(method.Name, out var name))
                {
                    foreach (AttributeData attribute in method.GetAttributes())
                    {
                        if (attribute.AttributeClass?.Name is SupportedOSPlatformGuardAttribute &&
                            TryParsePlatformNameAndVersion(attribute, out var platformName, out var _))
                        {
                            relatedPlatforms[name] = (platformName, true);
                            relatedPlatforms[platformName] = (name, false);
                        }
                    }
                }
            }

            static ImmutableArray<string> GetSupportedPlatforms(AnalyzerOptions options, Compilation compilation) =>
                options.GetMSBuildItemMetadataValues(MSBuildItemOptionNames.SupportedPlatform, compilation);

            static bool NameAndParametersValid(IMethodSymbol method) => method.Name.StartsWith(IsPrefix, StringComparison.Ordinal) &&
                    (method.Parameters.Length == 0 || method.Name.EndsWith(OptionalSuffix, StringComparison.Ordinal));

            static bool HasCrossPlatformProperty(AnalyzerOptions options, Compilation compilation)
            {
                return options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.PlatformNeutralAssembly, compilation) is not null;
            }
        }

        private static bool TryExtractPlatformName(string methodName, [NotNullWhen(true)] out string? platformName)
        {
            if (!methodName.StartsWith(IsPrefix, StringComparison.Ordinal))
            {
                platformName = null;
                return false;
            }

            if (methodName.EndsWith(OptionalSuffix, StringComparison.Ordinal))
            {
                platformName = methodName.Substring(2, methodName.Length - 2 - OptionalSuffix.Length);
                return true;
            }

            platformName = methodName[2..];
            return true;
        }

        private static bool PlatformAnalysisAllowed(AnalyzerOptions options, Compilation compilation)
        {
            string tfmIdentifier = options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.TargetFrameworkIdentifier, compilation) ?? "";
            string tfmVersion = options.GetMSBuildPropertyValue(MSBuildPropertyOptionNames.TargetFrameworkVersion, compilation) ?? "";

            if (tfmIdentifier.Equals(NetCoreAppIdentifier, StringComparison.OrdinalIgnoreCase) &&
                Version.TryParse(tfmVersion.TrimStart(['v', 'V']), out var version) &&
                version.Major >= 5)
            {
                // We want to only support cases we know are well-formed by default
                return true;
            }
            else
            {
                // We want to fallback to allowing force enablement regardless of recognizing the Identifier/Version
                return LowerTargetsEnabled(options, compilation);
            }
        }

        private static bool LowerTargetsEnabled(AnalyzerOptions options, Compilation compilation) =>
            compilation.SyntaxTrees.FirstOrDefault() is { } tree &&
            options.GetBoolOptionValue(EditorConfigOptionNames.EnablePlatformAnalyzerOnPreNet5Target, SupportedCsAllPlatforms, tree, compilation, false);

        private void AnalyzeOperationBlock(
            OperationBlockStartAnalysisContext context,
            ImmutableArray<IMethodSymbol> guardMethods,
            IMethodSymbol? runtimeIsOSPlatformMethod,
            IMethodSymbol? osPlatformCreateMethod,
            bool crossPlatform,
            INamedTypeSymbol? osPlatformType,
            INamedTypeSymbol stringType,
            ConcurrentDictionary<ISymbol, PlatformAttributes> platformSpecificMembers,
            ImmutableArray<string> msBuildPlatforms,
            ITypeSymbol? notSupportedExceptionType,
            SmallDictionary<string, (string relatedPlatform, bool isSubset)> relatedPlatforms)
        {
            if (context.IsMethodNotImplementedOrSupported(checkPlatformNotSupported: true))
            {
                return;
            }

            var osPlatformTypeArray = osPlatformType != null ? ImmutableArray.Create(osPlatformType) : ImmutableArray<INamedTypeSymbol>.Empty;
            var platformSpecificOperations = PooledConcurrentDictionary<KeyValuePair<IOperation, ISymbol>,
                (SmallDictionary<string, Versions> attributes, SmallDictionary<string, Versions>? csAttributes)>.GetInstance();

            context.RegisterOperationAction(context =>
            {
                AnalyzeOperation(context.Operation, context, platformSpecificOperations, platformSpecificMembers,
                    msBuildPlatforms, notSupportedExceptionType, crossPlatform, relatedPlatforms);
            },
            OperationKind.MethodReference,
            OperationKind.EventReference,
            OperationKind.FieldReference,
            OperationKind.Invocation,
            OperationKind.ObjectCreation,
            OperationKind.PropertyReference);

            context.RegisterOperationBlockEndAction(context =>
            {
                try
                {
                    if (platformSpecificOperations.IsEmpty ||
                        guardMethods.IsEmpty ||
                        !(context.OperationBlocks.GetControlFlowGraph() is { } cfg))
                    {
                        return;
                    }

                    var performValueContentAnalysis = ComputeNeedsValueContentAnalysis(cfg.OriginalOperation, guardMethods, runtimeIsOSPlatformMethod, osPlatformType);
                    var wellKnownTypeProvider = WellKnownTypeProvider.GetOrCreate(context.Compilation);
                    var analysisResult = GlobalFlowStateAnalysis.TryGetOrComputeResult(
                        cfg, context.OwningSymbol, CreateOperationVisitor, wellKnownTypeProvider,
                        context.Options, SupportedCsAllPlatforms, performValueContentAnalysis,
                        pessimisticAnalysis: false,
                        valueContentAnalysisResult: out var valueContentAnalysisResult, additionalSupportedValueTypes: osPlatformTypeArray,
                        getValueContentValueForAdditionalSupportedValueTypeOperation: osPlatformTypeArray.IsEmpty ? null : GetValueContentValue);

                    if (analysisResult == null)
                    {
                        return;
                    }

                    foreach (var (platformSpecificOperation, pair) in platformSpecificOperations)
                    {
                        var value = analysisResult[platformSpecificOperation.Key.Kind, platformSpecificOperation.Key.Syntax];
                        var csAttributes = pair.csAttributes != null ? CopyAttributes(pair.csAttributes) : null;
                        var symbol = platformSpecificOperation.Value is IMethodSymbol method && method.IsConstructor() ?
                                        platformSpecificOperation.Value.ContainingType : platformSpecificOperation.Value;
                        var originalAttributes = platformSpecificMembers[symbol].Platforms ?? pair.attributes;

                        if ((value.Kind == GlobalFlowStateAnalysisValueSetKind.Known && IsKnownValueGuarded(pair.attributes, ref csAttributes, value, pair.csAttributes, originalAttributes)) ||
                           (value.Kind == GlobalFlowStateAnalysisValueSetKind.Unknown && HasGuardedLambdaOrLocalFunctionResult(platformSpecificOperation.Key,
                           pair.attributes, ref csAttributes, analysisResult, pair.csAttributes, originalAttributes)))
                        {
                            continue;
                        }

                        ReportDiagnostics(platformSpecificOperation.Key, pair.attributes, csAttributes, context, symbol, originalAttributes);
                    }
                }
                finally
                {
                    platformSpecificOperations.Free(context.CancellationToken);
                }

                return;

                OperationVisitor CreateOperationVisitor(GlobalFlowStateAnalysisContext context) => new(guardMethods, osPlatformType, relatedPlatforms, context);

                ValueContentAbstractValue GetValueContentValue(IOperation operation)
                {
                    Debug.Assert(operation.Type!.Equals(osPlatformType));
                    if (operation is IInvocationOperation invocation &&
                        invocation.TargetMethod.Equals(osPlatformCreateMethod) &&
                        invocation.Arguments.Length == 1 &&
                        invocation.Arguments[0].Value is { } argument &&
                        argument.ConstantValue.HasValue &&
                        argument.ConstantValue.Value is string platformName &&
                        platformName.Length > 0)
                    {
                        return ValueContentAbstractValue.Create(platformName, stringType);
                    }

                    return ValueContentAbstractValue.MayBeContainsNonLiteralState;
                }
            });
        }

        private static bool HasGuardedLambdaOrLocalFunctionResult(IOperation platformSpecificOperation, SmallDictionary<string, Versions> attributes,
            ref SmallDictionary<string, Versions>? csAttributes, DataFlowAnalysisResult<GlobalFlowStateBlockAnalysisResult,
                GlobalFlowStateAnalysisValueSet> analysisResult, SmallDictionary<string, Versions>? originalCsAttributes, SmallDictionary<string, Versions> originalAttributes)
        {
            if (!platformSpecificOperation.IsWithinLambdaOrLocalFunction(out var containingLambdaOrLocalFunctionOperation))
            {
                return false;
            }

            var results = analysisResult.TryGetLambdaOrLocalFunctionResults(containingLambdaOrLocalFunctionOperation);
            Debug.Assert(results.Any(), "Expected at least one analysis result for lambda/local function");

            foreach (var localResult in results)
            {
                Debug.Assert(localResult.ControlFlowGraph.OriginalOperation == containingLambdaOrLocalFunctionOperation);

                var localValue = localResult[platformSpecificOperation.Kind, platformSpecificOperation.Syntax];

                // Value must be known and guarded in all analysis contexts.
                // NOTE: IsKnownValueGuarded mutates the input values, so we pass in cloned values
                // to ensure that evaluation of each result is independent of evaluation of other parts.
                if (localValue.Kind != GlobalFlowStateAnalysisValueSetKind.Known ||
                    !IsKnownValueGuarded(CopyAttributes(attributes), ref csAttributes, localValue, originalCsAttributes, originalAttributes))
                {
                    return false;
                }
            }

            return true;
        }

        private static bool ComputeNeedsValueContentAnalysis(IOperation operationBlock, ImmutableArray<IMethodSymbol> guardMethods, IMethodSymbol? runtimeIsOSPlatformMethod, INamedTypeSymbol? osPlatformType)
        {
            Debug.Assert(runtimeIsOSPlatformMethod == null || guardMethods.Contains(runtimeIsOSPlatformMethod));

            foreach (var operation in operationBlock.Descendants())
            {
                if (operation is IInvocationOperation invocation)
                {
                    if (invocation.TargetMethod.Equals(runtimeIsOSPlatformMethod))
                    {
                        if (invocation.Arguments.Length == 1 &&
                            invocation.Arguments[0].Value is IPropertyReferenceOperation propertyReference &&
                            propertyReference.Property.ContainingType.Equals(osPlatformType))
                        {
                            // "OSPlatform.Platform" property reference does not need value content analysis.
                            continue;
                        }

                        return true;
                    }
                    else if (guardMethods.Contains(invocation.TargetMethod))
                    {
                        // Check if any integral parameter to guard method invocation has non-constant value.
                        foreach (var argument in invocation.Arguments)
                        {
                            if (argument.Parameter?.Type.SpecialType == SpecialType.System_Int32 &&
                                !argument.Value.ConstantValue.HasValue)
                            {
                                return true;
                            }
                        }
                    }
                }
            }

            return false;
        }

        private static bool IsKnownValueGuarded(SmallDictionary<string, Versions> attributes,
                ref SmallDictionary<string, Versions>? csAttributes, GlobalFlowStateAnalysisValueSet value,
                SmallDictionary<string, Versions>? originalCsAttributes, SmallDictionary<string, Versions> originalAttributes)
        {
            using var capturedVersions = PooledDictionary<string, Version>.GetInstance(StringComparer.OrdinalIgnoreCase);
            return IsKnownValueGuarded(attributes, ref csAttributes, value, capturedVersions, originalCsAttributes, originalAttributes);

            static bool IsKnownValueGuarded(
                SmallDictionary<string, Versions> attributes,
                ref SmallDictionary<string, Versions>? csAttributes,
                GlobalFlowStateAnalysisValueSet value,
                PooledDictionary<string, Version> capturedVersions,
                SmallDictionary<string, Versions>? originalCsAttributes,
                SmallDictionary<string, Versions> originalAttributes)
            {
                // 'GlobalFlowStateAnalysisValueSet.AnalysisValues' represent the && of values.
                foreach (var analysisValue in value.AnalysisValues)
                {
                    if (analysisValue is PlatformMethodValue info)
                    {
                        if (attributes.TryGetValue(info.PlatformName, out var attribute))
                        {
                            if (info.Negated)
                            {
                                if (attribute.UnsupportedFirst != null &&
                                    attribute.UnsupportedFirst.IsGreaterThanOrEqualTo(info.Version))
                                {
                                    if (DenyList(attribute))
                                    {
                                        attribute.SupportedFirst = null;
                                        attribute.SupportedSecond = null;
                                        attribute.UnsupportedSecond = null;
                                    }

                                    attribute.UnsupportedFirst = null;
                                    attribute.UnsupportedMessage = null;
                                }
                                else
                                {
                                    if (originalCsAttributes != null &&
                                        AllowList(attribute) &&
                                        IsOnlySupportNeedsGuard(info.PlatformName, attributes, originalCsAttributes))
                                    {
                                        attribute.SupportedFirst = null;
                                        attribute.SupportedSecond = null;
                                        attribute.UnsupportedSecond = null;
                                        attribute.UnsupportedFirst = null;
                                        attribute.UnsupportedMessage = null;
                                    }
                                    else if (value.AnalysisValues.Contains(new PlatformMethodValue(info.PlatformName, EmptyVersion, false)))
                                    {
                                        csAttributes = SetCallSiteUnsupportedAttribute(csAttributes, info);
                                    }
                                }

                                if (attribute.UnsupportedSecond != null &&
                                    attribute.UnsupportedSecond.IsGreaterThanOrEqualTo(info.Version))
                                {
                                    attribute.UnsupportedSecond = null;
                                }

                                if (attribute.Obsoleted != null &&
                                    attribute.Obsoleted.IsGreaterThanOrEqualTo(info.Version))
                                {
                                    attribute.Obsoleted = null;
                                    attribute.ObsoletedMessage = null;
                                    attribute.ObsoletedUrl = null;
                                }

                                if (!IsEmptyVersion(info.Version))
                                {
                                    capturedVersions[info.PlatformName] = info.Version;
                                }
                            }
                            else
                            {
                                if (capturedVersions.Any())
                                {
                                    if (attribute.UnsupportedFirst != null &&
                                        capturedVersions.TryGetValue(info.PlatformName, out var version) &&
                                        attribute.UnsupportedFirst.IsGreaterThanOrEqualTo(version))
                                    {
                                        attribute.UnsupportedFirst = null;
                                        attribute.UnsupportedMessage = null;
                                    }

                                    if (attribute.UnsupportedSecond != null &&
                                        capturedVersions.TryGetValue(info.PlatformName, out version) &&
                                        version.IsGreaterThanOrEqualTo(attribute.UnsupportedSecond))
                                    {
                                        attribute.UnsupportedSecond = null;
                                        attribute.UnsupportedMessage = null;
                                    }
                                }

                                var checkVersion = attribute.SupportedSecond ?? attribute.SupportedFirst;

                                if (checkVersion != null &&
                                    info.Version.IsGreaterThanOrEqualTo(checkVersion))
                                {
                                    attribute.SupportedFirst = null;
                                    attribute.SupportedSecond = null;
                                    RemoveUnsupportedWithLessVersion(info.Version, attribute);
                                    RemoveOtherSupportsOnDifferentPlatforms(attributes, info.PlatformName);
                                }
                                else
                                {
                                    capturedVersions.TryGetValue(info.PlatformName, out var unsupportedVersion);
                                    csAttributes = SetCallSiteSupportedAttribute(csAttributes, info, unsupportedVersion);
                                }

                                RemoveUnsupportsOnDifferentPlatforms(attributes, info.PlatformName);
                            }
                        }
                        else if (!info.Negated)
                        {
                            // it is checking one exact platform, other unsupported should be suppressed
                            RemoveUnsupportsOnDifferentPlatforms(attributes, info.PlatformName);
                            csAttributes = SetCallSiteSupportedAttribute(csAttributes, info, null);
                        }
                    }
                }

                if (value.Parents.IsEmpty)
                {
                    foreach (var attribute in attributes)
                    {
                        // if any of the attributes is not suppressed
                        if (attribute.Value.IsSet())
                        {
                            return false;
                        }
                    }
                }
                else
                {
                    // 'GlobalFlowStateAnalysisValueSet.Parents' represent || of values on different flow paths.
                    // We are guarded only if values are guarded on *all flow paths**.
                    foreach (var parent in value.Parents)
                    {
                        // NOTE: IsKnownValueGuarded mutates the input values, so we pass in cloned values
                        // to ensure that evaluation of each part of || is independent of evaluation of other parts.
                        var parentAttributes = CopyAttributes(attributes);
                        var parentCsAttributes = csAttributes == null ? null : CopyAttributes(csAttributes);
                        using var parentCapturedVersions = PooledDictionary<string, Version>.GetInstance(capturedVersions);

                        if (parent.AnalysisValues.Count > 0)
                        {
                            if (parentCsAttributes != null && parentCsAttributes.Any() &&
                                IsNegationOfCallsiteAttributes(parentCsAttributes, parent.AnalysisValues))
                            {
                                continue;
                            }

                            if (value.AnalysisValues.Count == parent.AnalysisValues.Count &&
                                IsNegationOfParentValues(value, parent.AnalysisValues.GetEnumerator()))
                            {
                                continue;
                            }

                            // Skip the platform check that was originally in the list and suppressed by callsite attributes
                            if (parent.AnalysisValues.Count == 1 &&
                                IsPlatformSupportWasSuppresed((PlatformMethodValue)parent.AnalysisValues.First(), attributes, originalAttributes))
                            {
                                continue;
                            }
                        }

                        if (!IsKnownValueGuarded(parentAttributes, ref parentCsAttributes, parent, parentCapturedVersions, originalCsAttributes, originalAttributes))
                        {
                            csAttributes = parentCsAttributes;
                            return false;
                        }
                    }
                }

                return true;
            }

            static bool IsPlatformSupportWasSuppresed(PlatformMethodValue parentValue, SmallDictionary<string, Versions> attributes, SmallDictionary<string, Versions> originalAttributes)
                => !parentValue.Negated && !attributes.ContainsKey(parentValue.PlatformName) &&
                    originalAttributes.TryGetValue(parentValue.PlatformName, out Versions? version) &&
                    parentValue.Version.IsGreaterThanOrEqualTo(version.SupportedFirst);

            static bool IsOnlySupportNeedsGuard(string platformName, SmallDictionary<string, Versions> attributes, SmallDictionary<string, Versions> csAttributes)
                 => csAttributes.TryGetValue(platformName, out var versions) &&
                    AllowList(versions) &&
                    attributes.Count() == 1 &&
                    csAttributes.Any(cs => !cs.Key.Equals(platformName, StringComparison.OrdinalIgnoreCase));

            static bool IsNegationOfCallsiteAttributes(SmallDictionary<string, Versions> csAttributes, ImmutableHashSet<IAbstractAnalysisValue> parentValues)
            {
                bool allowList = AllowList(csAttributes.First().Value);

                foreach (var value in parentValues)
                {
                    if (value is PlatformMethodValue info)
                    {
                        if (csAttributes.TryGetValue(info.PlatformName, out var version))
                        {
                            if (info.Negated)
                            {
                                if (version.SupportedFirst != info.Version)
                                    return false;
                            }
                            else
                            {
                                if (version.UnsupportedFirst != info.Version)
                                    return false;
                            }

                            continue;
                        }
                        else if (allowList) // If callsite is supported only list then no need to worry about other platform guard
                        {
                            continue;
                        }
                    }

                    return false;
                }

                return true;
            }

            static bool IsNegationOfParentValues(GlobalFlowStateAnalysisValueSet value, ImmutableHashSet<IAbstractAnalysisValue>.Enumerator parentEnumerator)
            {
                foreach (var val in value.AnalysisValues)
                {
                    if (parentEnumerator.MoveNext() && !parentEnumerator.Current.Equals(val.GetNegatedValue()))
                    {
                        return false;
                    }
                }

                return true;
            }

            static SmallDictionary<string, Versions> SetCallSiteSupportedAttribute(SmallDictionary<string, Versions>? csAttributes,
                PlatformMethodValue info, Version? unsupportedVersion)
            {
                csAttributes ??= new SmallDictionary<string, Versions>(StringComparer.OrdinalIgnoreCase);

                if (csAttributes.TryGetValue(info.PlatformName, out var attributes))
                {
                    if (attributes.SupportedFirst == null)
                    {
                        attributes.SupportedFirst = info.Version;
                    }
                    else
                    {
                        attributes.SupportedSecond = info.Version;
                    }

                    attributes.UnsupportedFirst = unsupportedVersion;
                }
                else
                {
                    csAttributes.Add(info.PlatformName, new Versions() { SupportedFirst = info.Version, UnsupportedFirst = unsupportedVersion });
                }

                return csAttributes;
            }

            static SmallDictionary<string, Versions> SetCallSiteUnsupportedAttribute(SmallDictionary<string, Versions>? csAttributes, PlatformMethodValue info)
            {
                csAttributes ??= new SmallDictionary<string, Versions>(StringComparer.OrdinalIgnoreCase);

                if (csAttributes.TryGetValue(info.PlatformName, out var attributes))
                {
                    if (attributes.UnsupportedFirst == null)
                    {
                        attributes.UnsupportedFirst = info.Version;
                    }
                    else
                    {
                        attributes.UnsupportedSecond = info.Version;
                    }
                }
                else
                {
                    csAttributes.Add(info.PlatformName, new Versions() { UnsupportedFirst = info.Version });
                }

                return csAttributes;
            }

            static void RemoveUnsupportsOnDifferentPlatforms(SmallDictionary<string, Versions> attributes, string platformName)
            {
                foreach (var (name, attribute) in attributes)
                {
                    if (!name.Equals(platformName, StringComparison.OrdinalIgnoreCase))
                    {
                        if (DenyList(attribute))
                        {
                            attribute.UnsupportedFirst = null;
                            attribute.UnsupportedSecond = null;
                            attribute.SupportedFirst = null;
                            attribute.SupportedSecond = null;
                            attribute.UnsupportedMessage = null;
                        }

                        attribute.Obsoleted = null;
                        attribute.ObsoletedMessage = null;
                        attribute.ObsoletedUrl = null;
                    }
                }
            }

            static void RemoveUnsupportedWithLessVersion(Version supportedVersion, Versions attribute)
            {
                if (supportedVersion.IsGreaterThanOrEqualTo(attribute.UnsupportedFirst))
                {
                    attribute.UnsupportedFirst = null;
                }
            }

            static void RemoveOtherSupportsOnDifferentPlatforms(SmallDictionary<string, Versions> attributes, string platformName)
            {
                foreach (var (name, attribute) in attributes)
                {
                    if (!name.Equals(platformName, StringComparison.OrdinalIgnoreCase))
                    {
                        attribute.SupportedFirst = null;
                        attribute.SupportedSecond = null;
                    }
                }
            }
        }

        private static bool IsEmptyVersion(Version version) => version.Major == 0 && version.Minor == 0;

        private static void ReportDiagnostics(IOperation operation, SmallDictionary<string, Versions> attributes,
            SmallDictionary<string, Versions>? csAttributes, OperationBlockAnalysisContext context,
            ISymbol symbol, SmallDictionary<string, Versions> originalAttributes)
        {
            var operationName = symbol.ToDisplayString(GetLanguageSpecificFormat(operation));

            foreach (var attribute in originalAttributes.Values)
            {
                if (AllowList(attribute))
                {
                    ReportSupportedDiagnostic(operation, context, operationName, attributes, csAttributes);
                }
                else
                {
                    ReportUnsupportedDiagnostic(operation, context, operationName, attributes, csAttributes);
                }

                break;
            }

            static void ReportSupportedDiagnostic(IOperation operation, OperationBlockAnalysisContext context, string operationName,
                 SmallDictionary<string, Versions> attributes, SmallDictionary<string, Versions>? callsiteAttributes)
            {
                var supportedRule = GetSupportedPlatforms(attributes, callsiteAttributes, out var platformNames, out var obsoletedPlatforms);
                var csPlatformNames = JoinNames(GetCallsitePlatforms(attributes, callsiteAttributes, out var callsite, supported: supportedRule));

                if (callsite == Callsite.Reachable && IsDenyList(callsiteAttributes))
                {
                    csPlatformNames = string.Join(CommaSeparator, csPlatformNames, PlatformCompatibilityAllPlatforms);
                }

                if (!platformNames.IsEmpty)
                {
                    var rule = supportedRule ? SwitchSupportedRule(callsite) : SwitchRule(callsite, true);
                    context.ReportDiagnostic(operation.CreateDiagnostic(rule, operationName, JoinNames(platformNames), csPlatformNames));
                }

                if (!obsoletedPlatforms.IsEmpty)
                {
                    context.ReportDiagnostic(operation.CreateDiagnostic(SwitchObsoletedRule(callsite), operationName, JoinNames(obsoletedPlatforms), csPlatformNames));
                }

                static DiagnosticDescriptor SwitchSupportedRule(Callsite callsite)
                    => callsite switch
                    {
                        Callsite.AllPlatforms => OnlySupportedCsAllPlatforms,
                        Callsite.Reachable => OnlySupportedCsReachable,
                        Callsite.Unreachable => OnlySupportedCsUnreachable,
                        _ => throw new NotImplementedException()
                    };

                static bool IsDenyList(SmallDictionary<string, Versions>? callsiteAttributes) =>
                    callsiteAttributes != null && callsiteAttributes.Any(csa => DenyList(csa.Value));

                static bool GetSupportedPlatforms(SmallDictionary<string, Versions> attributes, SmallDictionary<string, Versions>? csAttributes,
                    out ImmutableArray<string> platformNames, out ImmutableArray<string> obsoletedPlatforms)
                {
                    using var obsoletedBuilder = ArrayBuilder<string>.GetInstance();
                    bool? supportedRule = null;
                    using var platformsBuilder = ArrayBuilder<string>.GetInstance();
                    foreach (var (pName, pAttribute) in attributes)
                    {
                        if (pAttribute.SupportedFirst != null && supportedRule.GetValueOrDefault(true))
                        {
                            supportedRule = true;
                            var supportedVersion = pAttribute.SupportedSecond ?? pAttribute.SupportedFirst;
                            if (pAttribute.UnsupportedFirst != null && !IsEmptyVersion(pAttribute.UnsupportedFirst))
                            {
                                if (IsEmptyVersion(supportedVersion))
                                {
                                    platformsBuilder.Add(AppendMessage(pAttribute,
                                        GetFormattedString(PlatformCompatibilityVersionAndBefore, pName, pAttribute.UnsupportedFirst)));
                                }
                                else
                                {
                                    platformsBuilder.Add(AppendMessage(pAttribute,
                                        GetFormattedString(PlatformCompatibilityFromVersionToVersion, pName, supportedVersion, pAttribute.UnsupportedFirst)));
                                }
                            }
                            else if (IsEmptyVersion(supportedVersion))
                            {
                                if (csAttributes != null && HasSameVersionedPlatformSupport(csAttributes, pName, checkSupport: false))
                                {
                                    platformsBuilder.Add(GetFormattedString(PlatformCompatibilityAllVersions, pName));
                                    continue;
                                }

                                platformsBuilder.Add(EncloseWithQuotes(pName));
                            }
                            else
                            {
                                platformsBuilder.Add(GetFormattedString(PlatformCompatibilityVersionAndLater, pName, supportedVersion));
                            }
                        }
                        else if (pAttribute.UnsupportedFirst != null)
                        {
                            if (supportedRule.GetValueOrDefault())
                            {
                                platformsBuilder.Clear();
                            }

                            supportedRule = false;
                            if (IsEmptyVersion(pAttribute.UnsupportedFirst))
                            {
                                if (csAttributes != null && HasSameVersionedPlatformSupport(csAttributes, pName, checkSupport: true))
                                {
                                    platformsBuilder.Add(AppendMessage(pAttribute, GetFormattedString(PlatformCompatibilityAllVersions, pName)));
                                    continue;
                                }

                                platformsBuilder.Add(AppendMessage(pAttribute, EncloseWithQuotes(pName)));
                            }
                            else
                            {
                                platformsBuilder.Add(AppendMessage(pAttribute,
                                    GetFormattedString(PlatformCompatibilityVersionAndLater, pName, pAttribute.UnsupportedFirst)));
                            }
                        }

                        AddObsoleted(csAttributes, obsoletedBuilder, pName, pAttribute);
                    }

                    obsoletedPlatforms = obsoletedBuilder.ToImmutable();
                    platformNames = platformsBuilder.ToImmutable();
                    return supportedRule.GetValueOrDefault(true);
                }
            }

            static DiagnosticDescriptor SwitchObsoletedRule(Callsite callsite)
            {
                return callsite switch
                {
                    Callsite.AllPlatforms => ObsoletedCsAllPlatforms,
                    Callsite.Reachable => ObsoletedCsReachable,
                    _ => throw new NotImplementedException()
                };
            }

            static DiagnosticDescriptor SwitchRule(Callsite callsite, bool unsupported)
            {
                if (unsupported)
                {
                    return callsite switch
                    {
                        Callsite.AllPlatforms => UnsupportedCsAllPlatforms,
                        Callsite.Reachable => UnsupportedCsReachable,
                        _ => throw new NotImplementedException()
                    };
                }
                else
                {
                    return callsite switch
                    {
                        Callsite.AllPlatforms => SupportedCsAllPlatforms,
                        Callsite.Reachable => SupportedCsReachable,
                        _ => throw new NotImplementedException()
                    };
                }
            }

            static string AppendMessage(Versions attribute, string message)
            {
                if (attribute.UnsupportedMessage is not null)
                {
                    message += string.Format(CultureInfo.InvariantCulture, ParenthesisWithPlaceHolder, attribute.UnsupportedMessage);
                }

                return message;
            }

            static string AppendMessageAndUrl(Versions attribute, string message)
            {
                if (attribute.ObsoletedMessage is not null)
                {
                    string customMessge = attribute.ObsoletedMessage;
                    if (attribute.ObsoletedUrl is not null)
                    {
                        customMessge = $"{customMessge} {attribute.ObsoletedUrl}";
                    }

                    message += string.Format(CultureInfo.InvariantCulture, ParenthesisWithPlaceHolder, customMessge);
                }
                else if (attribute.ObsoletedUrl is not null)
                {
                    message += string.Format(CultureInfo.InvariantCulture, ParenthesisWithPlaceHolder, attribute.ObsoletedUrl);
                }

                return message;
            }

            static void ReportUnsupportedDiagnostic(IOperation operation, OperationBlockAnalysisContext context, string operationName,
                SmallDictionary<string, Versions> attributes, SmallDictionary<string, Versions>? callsiteAttributes)
            {
                var unsupportedRule = GetPlatformNames(attributes, callsiteAttributes, out var platformNames, out var obsoletedPlatforms);
                var csPlatformNames = JoinNames(GetCallsitePlatforms(attributes, callsiteAttributes, out var callsite, supported: !unsupportedRule));

                if (!platformNames.IsEmpty)
                {
                    context.ReportDiagnostic(operation.CreateDiagnostic(SwitchRule(callsite, unsupportedRule), operationName, JoinNames(platformNames), csPlatformNames));
                }

                if (!obsoletedPlatforms.IsEmpty)
                {
                    context.ReportDiagnostic(operation.CreateDiagnostic(SwitchObsoletedRule(callsite), operationName, JoinNames(obsoletedPlatforms), csPlatformNames));
                }

                static bool GetPlatformNames(SmallDictionary<string, Versions> attributes, SmallDictionary<string, Versions>? csAttributes,
                    out ImmutableArray<string> platformNames, out ImmutableArray<string> obsoletedPlatforms)
                {
                    using var obsoletedBuilder = ArrayBuilder<string>.GetInstance();
                    using var platformsBuilder = ArrayBuilder<string>.GetInstance();
                    var unsupportedRule = true;
                    foreach (var (pName, pAttribute) in attributes)
                    {
                        var unsupportedVersion = pAttribute.UnsupportedSecond ?? pAttribute.UnsupportedFirst;
                        var supportedVersion = pAttribute.SupportedSecond ?? pAttribute.SupportedFirst;

                        if (unsupportedVersion != null)
                        {
                            if (supportedVersion != null)
                            {
                                unsupportedRule = false;

                                if (supportedVersion > unsupportedVersion)
                                {
                                    if (csAttributes == null || (csAttributes.TryGetValue(pName, out var csAttribute) &&
                                        csAttribute.UnsupportedFirst != null && csAttribute.UnsupportedFirst > supportedVersion))
                                    {
                                        unsupportedRule = true;
                                        if (IsEmptyVersion(pAttribute.UnsupportedFirst!))
                                        {
                                            platformsBuilder.Add(AppendMessage(pAttribute, GetFormattedString(PlatformCompatibilityVersionAndBefore, pName, supportedVersion)));
                                        }
                                        else
                                        {
                                            platformsBuilder.Add(AppendMessage(pAttribute,
                                                GetFormattedString(PlatformCompatibilityFromVersionToVersion, pName, unsupportedVersion, supportedVersion)));
                                        }
                                    }
                                    else
                                    {
                                        platformsBuilder.Add(AppendMessage(pAttribute, GetFormattedString(PlatformCompatibilityVersionAndLater, pName, supportedVersion)));
                                    }
                                }
                                else
                                {
                                    platformsBuilder.Add(AppendMessage(pAttribute,
                                        GetFormattedString(PlatformCompatibilityFromVersionToVersion, pName, supportedVersion, unsupportedVersion)));
                                }
                            }
                            else
                            {
                                if (IsEmptyVersion(unsupportedVersion))
                                {
                                    if (csAttributes != null && HasSameVersionedPlatformSupport(csAttributes, pName, checkSupport: true))
                                    {
                                        platformsBuilder.Add(AppendMessage(pAttribute, GetFormattedString(PlatformCompatibilityAllVersions, pName)));
                                    }
                                    else
                                    {
                                        platformsBuilder.Add(AppendMessage(pAttribute, EncloseWithQuotes(pName)));
                                    }
                                }
                                else
                                {
                                    platformsBuilder.Add(AppendMessage(pAttribute, GetFormattedString(PlatformCompatibilityVersionAndLater, pName, unsupportedVersion)));
                                }
                            }
                        }
                        else if (supportedVersion != null)
                        {
                            unsupportedRule = false;
                            if (IsEmptyVersion(supportedVersion))
                            {
                                platformsBuilder.Add(EncloseWithQuotes(pName));
                            }
                            else
                            {
                                platformsBuilder.Add(GetFormattedString(PlatformCompatibilityVersionAndLater, pName, supportedVersion));
                            }
                        }

                        AddObsoleted(csAttributes, obsoletedBuilder, pName, pAttribute);
                    }

                    obsoletedPlatforms = obsoletedBuilder.ToImmutable();
                    platformNames = platformsBuilder.ToImmutable();
                    return unsupportedRule;
                }
            }

            static void AddObsoleted(SmallDictionary<string, Versions>? csAttributes, ArrayBuilder<string> obsoletedBuilder, string pName, Versions pAttribute)
            {
                if (pAttribute.Obsoleted != null)
                {
                    if (IsEmptyVersion(pAttribute.Obsoleted)) // Do not need to add the version part if it is 0.0
                    {
                        if (csAttributes != null && HasVersionedCallsite(csAttributes, pName))
                        {
                            obsoletedBuilder.Add(AppendMessage(pAttribute, GetFormattedString(PlatformCompatibilityAllVersions, pName)));
                        }
                        else
                        {
                            obsoletedBuilder.Add(AppendMessage(pAttribute, EncloseWithQuotes(pName)));
                        }
                    }
                    else
                    {
                        obsoletedBuilder.Add(AppendMessageAndUrl(pAttribute, GetFormattedString(PlatformCompatibilityVersionAndLater, pName, pAttribute.Obsoleted)));
                    }
                }
            }

            static ImmutableArray<string> GetCallsitePlatforms(SmallDictionary<string, Versions> attributes,
                SmallDictionary<string, Versions>? callsiteAttributes, out Callsite callsite, bool supported)
            {
                callsite = Callsite.AllPlatforms;
                using var platformNames = ArrayBuilder<string>.GetInstance();
                if (callsiteAttributes != null)
                {
                    foreach (var (pName, csAttribute) in callsiteAttributes)
                    {
                        var supportedVersion = csAttribute.SupportedSecond ?? csAttribute.SupportedFirst;
                        if (supportedVersion != null)
                        {
                            callsite = Callsite.Reachable;
                            if (csAttribute.UnsupportedFirst != null && !IsEmptyVersion(csAttribute.UnsupportedFirst))
                            {
                                if (IsEmptyVersion(supportedVersion))
                                {
                                    platformNames.Add(GetFormattedString(PlatformCompatibilityVersionAndBefore, pName, csAttribute.UnsupportedFirst));
                                }
                                else if (supportedVersion > csAttribute.UnsupportedFirst)
                                {
                                    platformNames.Add(GetFormattedString(PlatformCompatibilityVersionAndLater, pName, supportedVersion));
                                }
                                else
                                {
                                    platformNames.Add(GetFormattedString(PlatformCompatibilityFromVersionToVersion,
                                        pName, supportedVersion, csAttribute.UnsupportedFirst));
                                }
                            }
                            else if (IsEmptyVersion(supportedVersion))
                            {
                                if (HasSameVersionedPlatformSupport(attributes, pName, supported))
                                {
                                    platformNames.Add(GetFormattedString(PlatformCompatibilityAllVersions, pName));
                                    continue;
                                }

                                platformNames.Add(EncloseWithQuotes(pName));
                            }
                            else
                            {
                                var unsupportedVersion = csAttribute.UnsupportedSecond ?? csAttribute.UnsupportedFirst;
                                if (unsupportedVersion != null && unsupportedVersion > supportedVersion)
                                {
                                    platformNames.Add(GetFormattedString(PlatformCompatibilityFromVersionToVersion,
                                        pName, supportedVersion, unsupportedVersion));
                                }
                                else
                                {
                                    platformNames.Add(GetFormattedString(PlatformCompatibilityVersionAndLater, pName, supportedVersion));
                                }
                            }
                        }
                        else
                        {
                            var unsupportedVersion = csAttribute.UnsupportedSecond ?? csAttribute.UnsupportedFirst;
                            if (unsupportedVersion != null && attributes.TryGetValue(pName, out var attribute))
                            {
                                var calledUnsupported = attribute.UnsupportedSecond ?? attribute.UnsupportedFirst;
                                callsite = Callsite.Unreachable;
                                if (IsEmptyVersion(unsupportedVersion))
                                {
                                    if (HasSameVersionedPlatformSupport(attributes, pName, supported))
                                    {
                                        platformNames.Add(GetFormattedString(PlatformCompatibilityAllVersions, pName));
                                        continue;
                                    }

                                    platformNames.Add(EncloseWithQuotes(pName));
                                }
                                else
                                {
                                    if ((attribute.SupportedFirst == null || !IsEmptyVersion(attribute.SupportedFirst)) &&
                                        (calledUnsupported == null || calledUnsupported < unsupportedVersion))
                                    {
                                        callsite = Callsite.Reachable;
                                        platformNames.Add(GetFormattedString(PlatformCompatibilityVersionAndBefore,
                                            pName, unsupportedVersion));
                                    }
                                    else
                                    {
                                        platformNames.Add(GetFormattedString(PlatformCompatibilityVersionAndLater,
                                            pName, unsupportedVersion));
                                    }
                                }
                            }
                        }
                    }
                }

                return platformNames.ToImmutable();
            }

            static string GetFormattedString(string resource, string platformName, object? arg1 = null, object? arg2 = null) =>
                string.Format(CultureInfo.InvariantCulture, resource, AddOsxIfMacOS(platformName), arg1, arg2);

            static string AddOsxIfMacOS(string platformName) =>
                platformName.Equals(macOS, StringComparison.OrdinalIgnoreCase) ? MacSlashOSX : platformName;

            static string EncloseWithQuotes(string pName) => $"'{AddOsxIfMacOS(pName)}'";

            static string JoinNames(ImmutableArray<string> platformNames)
            {
                platformNames = platformNames.Sort(StringComparer.OrdinalIgnoreCase);
                return string.Join(CommaSeparator, platformNames);
            }

            static SymbolDisplayFormat GetLanguageSpecificFormat(IOperation operation) =>
                operation.Language == LanguageNames.CSharp ? SymbolDisplayFormat.CSharpShortErrorMessageFormat : SymbolDisplayFormat.VisualBasicShortErrorMessageFormat;

            static bool HasSameVersionedPlatformSupport(SmallDictionary<string, Versions> attributes, string pName, bool checkSupport)
            {
                if (attributes.TryGetValue(pName, out var attribute))
                {
                    var version = attribute.UnsupportedSecond ?? attribute.UnsupportedFirst;
                    if (checkSupport)
                    {
                        var supportedVersion = attribute.SupportedSecond ?? attribute.SupportedFirst;
                        if (supportedVersion != null)
                        {
                            version = supportedVersion.IsGreaterThanOrEqualTo(version) ? supportedVersion : version;
                        }
                    }

                    if (version != null && !IsEmptyVersion(version))
                    {
                        return true;
                    }
                }

                return false;
            }

            static bool HasVersionedCallsite(SmallDictionary<string, Versions> csAttributes, string pName)
            {
                if (csAttributes.TryGetValue(pName, out var attribute))
                {
                    var version = attribute.Obsoleted;
                    var supportedVersion = attribute.SupportedSecond ?? attribute.SupportedFirst;
                    if (supportedVersion != null)
                    {
                        version = supportedVersion.IsGreaterThanOrEqualTo(version) ? supportedVersion : version;
                    }

                    if (version != null && !IsEmptyVersion(version))
                    {
                        return true;
                    }
                }

                return false;
            }
        }

        private enum Callsite
        {
            AllPlatforms,
            Reachable,
            Unreachable,
            Empty
        }

        private static ISymbol? GetOperationSymbol(IOperation operation)
            => operation switch
            {
                IInvocationOperation iOperation => iOperation.TargetMethod,
                IObjectCreationOperation cOperation => cOperation.Constructor,
                IFieldReferenceOperation fOperation => IsWithinConditionalOperation(fOperation) ? null : fOperation.Field,
                IMemberReferenceOperation mOperation => mOperation.Member,
                _ => null,
            };

        private static IEnumerable<ISymbol> GetPropertyAccessors(IPropertySymbol property, IOperation operation)
        {
            var usageInfo = operation.GetValueUsageInfo(property.ContainingSymbol);

            // not checking/using ValueUsageInfo.Reference related values as property cannot be used as ref or out parameter
            // not using ValueUsageInfo.Name too, it only use name of the property
            if (usageInfo == ValueUsageInfo.ReadWrite)
            {
                yield return property.GetMethod!;
                yield return property.SetMethod!;
            }
            else if (usageInfo.IsWrittenTo())
            {
                yield return property.SetMethod!;
            }
            else if (usageInfo.IsReadFrom())
            {
                yield return property.GetMethod!;
            }
            else
            {
                yield return property;
            }
        }

        private static ISymbol GetEventAccessor(IEventSymbol iEvent, IOperation operation)
        {
            if (operation.Parent is IEventAssignmentOperation eventAssignment)
            {
                if (eventAssignment.Adds)
                    return iEvent.AddMethod!;
                else
                    return iEvent.RemoveMethod!;
            }

            return iEvent;
        }

        private static void AnalyzeOperation(IOperation operation, OperationAnalysisContext context, PooledConcurrentDictionary<KeyValuePair<IOperation, ISymbol>,
            (SmallDictionary<string, Versions> attributes, SmallDictionary<string, Versions>? csAttributes)> platformSpecificOperations,
             ConcurrentDictionary<ISymbol, PlatformAttributes> platformSpecificMembers, ImmutableArray<string> msBuildPlatforms,
             ITypeSymbol? notSupportedExceptionType, bool crossPlatform, SmallDictionary<string, (string relatedPlatform, bool isSubset)> relatedPlatforms)
        {
            if (operation.Parent is IArgumentOperation argumentOperation && UsedInCreatingNotSupportedException(argumentOperation, notSupportedExceptionType))
            {
                return;
            }

            var symbol = GetOperationSymbol(operation);

            if (symbol == null || symbol is ITypeSymbol type && type.SpecialType != SpecialType.None)
            {
                return;
            }

            CheckOperationAttributes(symbol, checkParents: true);

            if (symbol is IPropertySymbol property)
            {
                foreach (var accessor in GetPropertyAccessors(property, operation))
                {
                    if (accessor != null)
                    {
                        CheckOperationAttributes(accessor, checkParents: false);
                    }
                }
            }
            else if (symbol is IEventSymbol iEvent)
            {
                var accessor = GetEventAccessor(iEvent, operation);

                if (accessor != null)
                {
                    CheckOperationAttributes(accessor, checkParents: false);
                }
            }
            else if (symbol is IMethodSymbol method && method.IsGenericMethod)
            {
                CheckTypeArguments(method.TypeArguments);
            }

            if (symbol.ContainingSymbol is INamedTypeSymbol namedType && namedType.IsGenericType)
            {
                CheckTypeArguments(namedType.TypeArguments);
            }

            void CheckTypeArguments(ImmutableArray<ITypeSymbol> typeArguments)
            {
                using var workingSet = PooledHashSet<ITypeSymbol>.GetInstance();
                CheckTypeArgumentsCore(typeArguments, workingSet);
            }

            void CheckTypeArgumentsCore(ImmutableArray<ITypeSymbol> typeArguments, PooledHashSet<ITypeSymbol> workingSet)
            {
                foreach (var typeArgument in typeArguments)
                {
                    if (workingSet.Add(typeArgument))
                    {
                        if (typeArgument.SpecialType == SpecialType.None)
                        {
                            CheckOperationAttributes(typeArgument, checkParents: true);

                            if (typeArgument is INamedTypeSymbol nType && nType.IsGenericType)
                            {
                                CheckTypeArgumentsCore(nType.TypeArguments, workingSet);
                            }
                        }
                    }
                }
            }

            void CheckOperationAttributes(ISymbol symbol, bool checkParents)
            {
                if (TryGetOrCreatePlatformAttributes(symbol, checkParents, crossPlatform, platformSpecificMembers, relatedPlatforms, out var operationAttributes))
                {
                    var containingSymbol = context.ContainingSymbol;
                    if (containingSymbol is IMethodSymbol method && method.IsAccessorMethod())
                    {
                        containingSymbol = method.AssociatedSymbol!;
                    }

                    if (TryGetOrCreatePlatformAttributes(containingSymbol, true, crossPlatform, platformSpecificMembers, relatedPlatforms, out var callSiteAttributes))
                    {
                        if (callSiteAttributes.Callsite != Callsite.Empty &&
                            IsNotSuppressedByCallSite(operationAttributes.Platforms!, callSiteAttributes.Platforms!, msBuildPlatforms,
                                out var notSuppressedAttributes, crossPlatform & operationAttributes.IsAssemblyAttribute))
                        {
                            platformSpecificOperations.TryAdd(new KeyValuePair<IOperation, ISymbol>(operation, symbol), (notSuppressedAttributes, callSiteAttributes.Platforms));
                        }
                    }
                    else if (!OperationHasOnlyAssemblyAttributesAndCalledFromSameAssembly(operationAttributes, symbol, containingSymbol) &&
                             TryCopyAttributesNotSuppressedByMsBuild(operationAttributes.Platforms!, msBuildPlatforms, out var copiedAttributes))
                    {
                        platformSpecificOperations.TryAdd(new KeyValuePair<IOperation, ISymbol>(operation, symbol), (copiedAttributes, null));
                    }
                }
            }
        }

        private static bool OperationHasOnlyAssemblyAttributesAndCalledFromSameAssembly(PlatformAttributes operationAttributes, ISymbol symbol, ISymbol containingSymbol) =>
            operationAttributes.IsAssemblyAttribute && containingSymbol.ContainingAssembly == symbol.ContainingAssembly;

        private static bool UsedInCreatingNotSupportedException(IArgumentOperation operation, ITypeSymbol? notSupportedExceptionType)
        {
            if (operation.Parent is IObjectCreationOperation creation &&
                operation.Parameter?.Type.SpecialType == SpecialType.System_String &&
                creation.Type.DerivesFrom(notSupportedExceptionType, baseTypesOnly: true, checkTypeParameterConstraints: false))
            {
                return true;
            }

            return false;
        }

        private static bool TryCopyAttributesNotSuppressedByMsBuild(SmallDictionary<string, Versions> operationAttributes,
            ImmutableArray<string> msBuildPlatforms, out SmallDictionary<string, Versions> copiedAttributes)
        {
            copiedAttributes = new SmallDictionary<string, Versions>(StringComparer.OrdinalIgnoreCase);
            foreach (var (platformName, attributes) in operationAttributes)
            {
                if (AllowList(attributes) || msBuildPlatforms.Contains(platformName, StringComparer.OrdinalIgnoreCase))
                {
                    copiedAttributes.Add(platformName, CopyAllAttributes(new Versions(), attributes));
                }
            }

            return !copiedAttributes.IsEmpty;
        }

        private static PlatformAttributes CopyAttributes(PlatformAttributes copyAttributes)
        {
            var copy = new PlatformAttributes(copyAttributes.Callsite, new SmallDictionary<string, Versions>(StringComparer.OrdinalIgnoreCase));

            foreach (var (platformName, attributes) in copyAttributes.Platforms!)
            {
                copy.Platforms!.Add(platformName, CopyAllAttributes(new Versions(), attributes));
            }

            copy.IsAssemblyAttribute = copyAttributes.IsAssemblyAttribute;
            return copy;
        }

        private static SmallDictionary<string, Versions> CopyAttributes(SmallDictionary<string, Versions> copyAttributes)
        {
            var copy = new SmallDictionary<string, Versions>(StringComparer.OrdinalIgnoreCase);

            foreach (var (platformName, attributes) in copyAttributes!)
            {
                copy.Add(platformName, CopyAllAttributes(new Versions(), attributes));
            }

            return copy;
        }

        /// <summary>
        /// Checks if API attributes suppressed by call site attribute. For examble if windows only API is called within call site attributes as windows only then that call will be suppressed
        /// The semantics of the platform specific attributes are :
        ///    - An API that doesn't have any of these attributes is considered supported by all platforms.
        ///    - If either [SupportedOSPlatform] or [UnsupportedOSPlatform] attributes are present, we group all attributes by OS platform identifier:
        ///        - Allow list.If the lowest version for each OS platform is a [SupportedOSPlatform] attribute, the API
        ///          is considered to only be supported by the listed platforms and unsupported by all other platforms.
        ///        - Deny list. If the lowest version for each OS platform is a [UnsupportedOSPlatform] attribute, then the
        ///          API is considered to only be unsupported by the listed platforms and supported by all other platforms.
        ///        - Inconsistent list. If for some platforms the lowest version attribute is [SupportedOSPlatform] while for others it is [UnsupportedOSPlatform],
        ///          we will add another analyzer to produce a warning on the API definition in such scenario because the API is attributed inconsistently.
        ///    - Both attributes can be instantiated without version numbers. This means the version number is assumed to be 0.0. This simplifies guard clauses, see examples below for more details.
        /// </summary>
        /// <param name="operationAttributes">Platform specific attributes applied to the invoked member</param>
        /// <param name="callSiteAttributes">Platform specific attributes applied to the call site where the member invoked</param>
        /// <param name="msBuildPlatforms">Supported platform names provided by MSBuild, used for deciding if we need to flag for Deny list (Unssuported) attributes</param>
        /// <param name="notSuppressedAttributes"> Out parameter will include all attributes not suppressed by call site</param>
        /// <returns>true if all attributes applied to the operation is suppressed, false otherwise</returns>

        private static bool IsNotSuppressedByCallSite(SmallDictionary<string, Versions> operationAttributes,
            SmallDictionary<string, Versions> callSiteAttributes, ImmutableArray<string> msBuildPlatforms,
            out SmallDictionary<string, Versions> notSuppressedAttributes, bool crossPlatform)
        {
            notSuppressedAttributes = new SmallDictionary<string, Versions>(StringComparer.OrdinalIgnoreCase);
            bool? mandatorySupportFound = null;
            using var supportedOnlyPlatforms = PooledHashSet<string>.GetInstance(StringComparer.OrdinalIgnoreCase);
            foreach (var (platformName, attribute) in operationAttributes)
            {
                var diagnosticAttribute = new Versions();

                if (attribute.SupportedFirst != null)
                {
                    if (attribute.UnsupportedFirst == null || attribute.UnsupportedFirst > attribute.SupportedFirst)
                    {
                        // If only supported for current platform
                        supportedOnlyPlatforms.Add(platformName);
                        mandatorySupportFound ??= false;

                        if (callSiteAttributes.TryGetValue(platformName, out var callSiteAttribute))
                        {
                            var attributeToCheck = attribute.SupportedSecond ?? attribute.SupportedFirst;
                            if ((MandatoryOsVersionsSuppressed(callSiteAttribute, attributeToCheck) || crossPlatform) && AllowList(callSiteAttribute))
                            {
                                mandatorySupportFound = true;
                            }
                            else
                            {
                                diagnosticAttribute.SupportedFirst = (Version)attributeToCheck.Clone();
                            }

                            if (attribute.UnsupportedFirst != null &&
                                (!mandatorySupportFound.Value ||
                                  !(SuppressedByCallSiteSupported(attribute, callSiteAttribute.SupportedFirst) ||
                                  SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedFirst))))
                            {
                                diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone();
                                diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage;
                            }
                        }
                    }
                    else if (attribute.UnsupportedFirst != null) // also means Unsupported < Supported, deny list
                    {
                        if (callSiteAttributes.TryGetValue(platformName, out var callSiteAttribute))
                        {
                            if (callSiteAttribute.SupportedFirst != null)
                            {
                                if (!OptionalOsSupportSuppressed(callSiteAttribute, attribute))
                                {
                                    diagnosticAttribute.SupportedFirst = (Version)attribute.SupportedFirst.Clone();
                                }

                                if (!UnsupportedFirstSuppressed(attribute, callSiteAttribute))
                                {
                                    diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone();
                                    diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage;
                                }

                                if (attribute.UnsupportedSecond != null &&
                                    !UnsupportedSecondSuppressed(attribute, callSiteAttribute))
                                {
                                    diagnosticAttribute.UnsupportedSecond = (Version)attribute.UnsupportedSecond.Clone();
                                    diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage;
                                }
                            }
                            else if (msBuildPlatforms.Contains(platformName, StringComparer.OrdinalIgnoreCase))
                            {
                                if (!SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedFirst))
                                {
                                    diagnosticAttribute.SupportedFirst = (Version)attribute.SupportedFirst.Clone();
                                    diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone();
                                    diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage;
                                }

                                if (attribute.UnsupportedSecond != null && !SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedSecond))
                                {
                                    diagnosticAttribute.SupportedFirst = (Version)attribute.SupportedFirst.Clone();
                                    diagnosticAttribute.UnsupportedSecond = (Version)attribute.UnsupportedSecond.Clone();
                                    diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage;
                                }
                            }
                        }
                        // Call site has no attributes for this platform, check if MsBuild list has it,
                        // then if call site has deny list, it should support its later support
                        else if (msBuildPlatforms.Contains(platformName, StringComparer.OrdinalIgnoreCase) &&
                                callSiteAttributes.Any(ca => DenyList(ca.Value)))
                        {
                            diagnosticAttribute.SupportedFirst = (Version)attribute.SupportedFirst.Clone();
                        }
                    }
                }
                else if (attribute.UnsupportedFirst != null) // Unsupported for this but supported all other
                {
                    if (callSiteAttributes.TryGetValue(platformName, out var callSiteAttribute))
                    {
                        if (callSiteAttribute.SupportedFirst != null)
                        {
                            if (callSiteAttribute.UnsupportedFirst != null)
                            {
                                if (!SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedFirst))
                                {
                                    diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone();
                                    diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage;
                                }
                                else if (DenyList(callSiteAttribute))
                                {
                                    diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone();
                                    diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage;
                                }
                            }
                            else
                            {
                                diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone();
                                diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage;
                            }
                        }
                        else if (msBuildPlatforms.Contains(platformName, StringComparer.OrdinalIgnoreCase) &&
                                !SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedFirst))
                        {
                            diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone();
                            diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage;
                        }
                    }
                    else if (msBuildPlatforms.Contains(platformName, StringComparer.OrdinalIgnoreCase) &&
                             !callSiteAttributes.Values.Any(v => v.SupportedFirst != null))
                    {
                        // if MsBuild list contain the platform and call site has no any other supported attribute it means global, so need to warn
                        diagnosticAttribute.UnsupportedFirst = (Version)attribute.UnsupportedFirst.Clone();
                        diagnosticAttribute.UnsupportedMessage = attribute.UnsupportedMessage;
                    }
                }

                // Check if obsoleted attribute guarded by callsite attributes
                if (attribute.Obsoleted != null)
                {
                    if (callSiteAttributes.TryGetValue(platformName, out var callSiteAttribute))
                    {
                        if (callSiteAttribute.SupportedFirst != null)
                        {
                            if ((callSiteAttribute.Obsoleted == null || callSiteAttribute.Obsoleted > attribute.Obsoleted) &&
                                (callSiteAttribute.UnsupportedFirst == null || callSiteAttribute.UnsupportedFirst > attribute.Obsoleted))
                            {
                                diagnosticAttribute.Obsoleted = (Version)attribute.Obsoleted.Clone();
                                diagnosticAttribute.ObsoletedMessage = attribute.ObsoletedMessage;
                                diagnosticAttribute.ObsoletedUrl = attribute.ObsoletedUrl;
                            }
                        }
                        else if (msBuildPlatforms.Contains(platformName, StringComparer.OrdinalIgnoreCase) &&
                                 (callSiteAttribute.UnsupportedFirst != null && callSiteAttribute.UnsupportedFirst > attribute.Obsoleted ||
                                  callSiteAttribute.Obsoleted != null && callSiteAttribute.Obsoleted > attribute.Obsoleted))
                        {
                            diagnosticAttribute.Obsoleted = (Version)attribute.Obsoleted.Clone();
                            diagnosticAttribute.ObsoletedMessage = attribute.ObsoletedMessage;
                            diagnosticAttribute.ObsoletedUrl = attribute.ObsoletedUrl;
                        }
                    }
                    else if (msBuildPlatforms.Contains(platformName, StringComparer.OrdinalIgnoreCase) &&
                             !callSiteAttributes.Values.Any(AllowList))
                    {
                        diagnosticAttribute.Obsoleted = (Version)attribute.Obsoleted.Clone();
                        diagnosticAttribute.ObsoletedMessage = attribute.ObsoletedMessage;
                        diagnosticAttribute.ObsoletedUrl = attribute.ObsoletedUrl;
                    }
                }

                if (diagnosticAttribute.IsSet())
                {
                    notSuppressedAttributes[platformName] = diagnosticAttribute;
                }
            }

            if (mandatorySupportFound.HasValue)
            {
                if (!mandatorySupportFound.Value)
                {
                    foreach (var (name, attributes) in operationAttributes)
                    {
                        if (attributes.SupportedFirst != null &&
                            !notSuppressedAttributes.TryGetValue(name, out var diagnosticAttribute))
                        {
                            diagnosticAttribute = new Versions();
                            CopyAllAttributes(diagnosticAttribute, attributes);
                            notSuppressedAttributes[name] = diagnosticAttribute;
                        }
                    }
                }
                else if (!crossPlatform)
                {
                    // if supportedOnlyList then call site should not have any platform not listed in the support list
                    foreach (var (platform, csAttributes) in callSiteAttributes)
                    {
                        if (csAttributes.SupportedFirst != null &&
                            !supportedOnlyPlatforms.Contains(platform) &&
                            !notSuppressedAttributes.ContainsKey(platform))
                        {
                            foreach (var (name, version) in operationAttributes)
                            {
                                AddOrUpdatedDiagnostic(version, notSuppressedAttributes, name);
                            }
                        }
                    }
                }
            }

            return !notSuppressedAttributes.IsEmpty;

            static void AddOrUpdatedDiagnostic(Versions operationAttributes,
                SmallDictionary<string, Versions> notSuppressedAttributes, string name)
            {
                if (operationAttributes.SupportedFirst != null)
                {
                    if (!notSuppressedAttributes.TryGetValue(name, out var diagnosticAttribute))
                    {
                        diagnosticAttribute = new Versions();
                    }

                    diagnosticAttribute.SupportedFirst = (Version)operationAttributes.SupportedFirst.Clone();
                    notSuppressedAttributes[name] = diagnosticAttribute;
                }
            }

            static bool UnsupportedSecondSuppressed(Versions attribute, Versions callSiteAttribute) =>
                SuppressedByCallSiteSupported(attribute, callSiteAttribute.SupportedFirst) ||
                SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedSecond!);

            static bool SuppressedByCallSiteUnsupported(Versions callSiteAttribute, Version unsupporteAttribute) =>
                DenyList(callSiteAttribute) && callSiteAttribute.SupportedFirst != null ?
                callSiteAttribute.UnsupportedSecond != null && unsupporteAttribute.IsGreaterThanOrEqualTo(callSiteAttribute.UnsupportedSecond) :
                callSiteAttribute.UnsupportedFirst != null && unsupporteAttribute.IsGreaterThanOrEqualTo(callSiteAttribute.UnsupportedFirst);

            static bool SuppressedByCallSiteSupported(Versions attribute, Version? callSiteSupportedFirst) =>
                callSiteSupportedFirst != null && callSiteSupportedFirst.IsGreaterThanOrEqualTo(attribute.SupportedFirst) &&
                attribute.SupportedSecond != null && callSiteSupportedFirst.IsGreaterThanOrEqualTo(attribute.SupportedSecond);

            static bool UnsupportedFirstSuppressed(Versions attribute, Versions callSiteAttribute) =>
                callSiteAttribute.SupportedFirst != null && callSiteAttribute.SupportedFirst.IsGreaterThanOrEqualTo(attribute.SupportedFirst) ||
                SuppressedByCallSiteUnsupported(callSiteAttribute, attribute.UnsupportedFirst!);

            // As optianal if call site supports that platform, their versions should match
            static bool OptionalOsSupportSuppressed(Versions callSiteAttribute, Versions attribute) =>
                (callSiteAttribute.SupportedFirst == null || callSiteAttribute.SupportedFirst.IsGreaterThanOrEqualTo(attribute.SupportedFirst)) &&
                (callSiteAttribute.SupportedSecond == null || callSiteAttribute.SupportedSecond.IsGreaterThanOrEqualTo(attribute.SupportedFirst));

            static bool MandatoryOsVersionsSuppressed(Versions callSitePlatforms, Version checkingVersion) =>
                callSitePlatforms.SupportedFirst != null && callSitePlatforms.SupportedFirst.IsGreaterThanOrEqualTo(checkingVersion) ||
                callSitePlatforms.SupportedSecond != null && callSitePlatforms.SupportedSecond.IsGreaterThanOrEqualTo(checkingVersion);
        }

        private static Versions CopyAllAttributes(Versions copyTo, Versions copyFrom)
        {
            copyTo.SupportedFirst = (Version?)copyFrom.SupportedFirst?.Clone();
            copyTo.SupportedSecond = (Version?)copyFrom.SupportedSecond?.Clone();
            copyTo.UnsupportedFirst = (Version?)copyFrom.UnsupportedFirst?.Clone();
            copyTo.UnsupportedSecond = (Version?)copyFrom.UnsupportedSecond?.Clone();
            copyTo.UnsupportedMessage = copyFrom.UnsupportedMessage;
            copyTo.Obsoleted = (Version?)copyFrom.Obsoleted?.Clone();
            copyTo.ObsoletedMessage = copyFrom.ObsoletedMessage;
            copyTo.ObsoletedUrl = copyFrom.ObsoletedUrl;
            return copyTo;
        }

        // Do not warn if platform specific enum/field value is used in conditional check, like: 'if (value == FooEnum.WindowsOnlyValue)'
        private static bool IsWithinConditionalOperation(IFieldReferenceOperation pOperation) =>
            pOperation.ConstantValue.HasValue &&
            pOperation.Parent is IBinaryOperation bo &&
            (bo.OperatorKind == BinaryOperatorKind.Equals ||
            bo.OperatorKind == BinaryOperatorKind.NotEquals ||
            bo.OperatorKind == BinaryOperatorKind.GreaterThan ||
            bo.OperatorKind == BinaryOperatorKind.LessThan ||
            bo.OperatorKind == BinaryOperatorKind.GreaterThanOrEqual ||
            bo.OperatorKind == BinaryOperatorKind.LessThanOrEqual);

        private static bool TryGetOrCreatePlatformAttributes(ISymbol symbol, bool checkParents,
            bool crossPlatform, ConcurrentDictionary<ISymbol, PlatformAttributes> platformSpecificMembers,
            SmallDictionary<string, (string relatedPlatform, bool isSubset)> relatedPlatforms, out PlatformAttributes attributes)
        {
            if (!platformSpecificMembers.TryGetValue(symbol, out attributes))
            {
                if (checkParents)
                {
                    var container = symbol.ContainingSymbol;

                    // Namespaces do not have attributes
                    while (container is INamespaceSymbol)
                    {
                        container = container.ContainingSymbol;
                    }

                    if (container != null &&
                        TryGetOrCreatePlatformAttributes(container, checkParents, crossPlatform, platformSpecificMembers, relatedPlatforms, out var containerAttributes))
                    {
                        attributes = CopyAttributes(containerAttributes);
                    }
                }

                attributes ??= new PlatformAttributes() { IsAssemblyAttribute = symbol is IAssemblySymbol };
                MergePlatformAttributes(symbol.GetAttributes(), ref attributes, crossPlatform, relatedPlatforms);
                attributes = platformSpecificMembers.GetOrAdd(symbol, attributes);
            }

            return attributes.Platforms != null;

            static void MergePlatformAttributes(ImmutableArray<AttributeData> immediateAttributes, ref PlatformAttributes parentAttributes,
                bool crossPlatform, SmallDictionary<string, (string relatedPlatform, bool isSubset)> relatedPlatforms)
            {
                SmallDictionary<string, Versions>? childAttributes = null;
                foreach (AttributeData attribute in immediateAttributes)
                {
                    if (attribute.AttributeClass == null)
                        continue;

                    if (attribute.AttributeClass.Name is SupportedOSPlatformGuardAttribute or UnsupportedOSPlatformGuardAttribute)
                    {
                        parentAttributes = new PlatformAttributes(); // The API is for guard, clear parent attributes
                        return;
                    }

                    if (s_osPlatformAttributes.Contains(attribute.AttributeClass.Name))
                    {
                        TryAddValidAttribute(ref childAttributes, attribute, relatedPlatforms);
                    }
                }

                if (childAttributes == null)
                {
                    return;
                }

                CheckAttributesConsistency(childAttributes);
                var pAttributes = parentAttributes.Platforms;
                if (pAttributes != null && !pAttributes.IsEmpty)
                {
                    var notFoundPlatforms = PooledHashSet<string>.GetInstance();
                    bool supportFound = false;
                    foreach (var (platform, attributes) in pAttributes)
                    {
                        if (DenyList(attributes) &&
                            !pAttributes.Any(ca => AllowList(ca.Value)))
                        {
                            // if all are deny list then we can add the child attributes
                            foreach (var (name, childAttribute) in childAttributes)
                            {
                                if (pAttributes.TryGetValue(name, out var existing))
                                {
                                    if (childAttribute.UnsupportedFirst != null)
                                    {
                                        // but don't override existing unless narrowing the support
                                        if (childAttribute.UnsupportedFirst < existing.UnsupportedFirst)
                                        {
                                            existing.UnsupportedFirst = childAttribute.UnsupportedFirst;
                                            if (childAttribute.SupportedFirst != null && (existing.SupportedFirst == null ||
                                                childAttribute.SupportedFirst > existing.SupportedFirst))
                                            {
                                                existing.SupportedFirst = childAttribute.SupportedFirst;
                                            }
                                        }

                                        if (childAttribute.UnsupportedSecond != null && (existing.UnsupportedSecond == null ||
                                             childAttribute.UnsupportedSecond < existing.UnsupportedSecond))
                                        {
                                            existing.UnsupportedSecond = childAttribute.UnsupportedSecond;
                                        }

                                        if (existing.SupportedFirst != null &&
                                            childAttribute.SupportedFirst != null &&
                                            childAttribute.SupportedFirst > existing.SupportedFirst)
                                        {
                                            existing.SupportedFirst = childAttribute.SupportedFirst;
                                        }
                                    }

                                    if (childAttribute.Obsoleted != null &&
                                        (childAttribute.Obsoleted < existing.UnsupportedFirst ||
                                            existing.SupportedFirst != null &&
                                            (existing.UnsupportedSecond == null || existing.UnsupportedSecond > childAttribute.Obsoleted)) &&
                                        (existing.Obsoleted == null || childAttribute.Obsoleted < existing.Obsoleted))
                                    {
                                        existing.Obsoleted = childAttribute.Obsoleted;
                                        existing.ObsoletedMessage = childAttribute.ObsoletedMessage;
                                        existing.ObsoletedUrl = childAttribute.ObsoletedUrl;
                                    }
                                }
                                else
                                {
                                    pAttributes[name] = childAttribute;
                                }
                            }
                            // merged all attributes, no need to continue looping
                            return;
                        }
                        else if (AllowList(attributes))
                        {
                            // only attributes with same platform matter, could narrow the list
                            if (childAttributes.TryGetValue(platform, out var childAttribute))
                            {
                                // only later versions could narrow, other versions ignored
                                if (childAttribute.SupportedFirst.IsGreaterThanOrEqualTo(attributes.SupportedFirst))
                                {
                                    supportFound = true;
                                    if (attributes.SupportedSecond == null || attributes.SupportedSecond < childAttribute.SupportedFirst)
                                    {
                                        attributes.SupportedSecond = childAttribute.SupportedFirst;
                                    }
                                }

                                if (childAttribute.UnsupportedFirst != null)
                                {
                                    if (attributes.SupportedFirst.IsGreaterThanOrEqualTo(childAttribute.UnsupportedFirst))
                                    {
                                        parentAttributes.Callsite = Callsite.Empty;
                                        attributes.SupportedFirst = childAttribute.SupportedFirst > attributes.SupportedFirst ? childAttribute.SupportedFirst : null;
                                        attributes.UnsupportedFirst = childAttribute.UnsupportedFirst;
                                        attributes.UnsupportedMessage = childAttribute.UnsupportedMessage;
                                    }
                                    else if (attributes.UnsupportedFirst == null || attributes.UnsupportedFirst > childAttribute.UnsupportedFirst)
                                    {
                                        attributes.UnsupportedFirst = childAttribute.UnsupportedFirst;
                                        attributes.UnsupportedMessage = childAttribute.UnsupportedMessage;
                                    }

                                    if (attributes.SupportedSecond.IsGreaterThanOrEqualTo(childAttribute.UnsupportedFirst))
                                    {
                                        attributes.SupportedSecond = null;
                                    }

                                    if (childAttribute.UnsupportedSecond != null && childAttribute.UnsupportedSecond > attributes.UnsupportedFirst)
                                    {
                                        attributes.UnsupportedFirst = childAttribute.UnsupportedSecond;
                                        attributes.UnsupportedMessage = childAttribute.UnsupportedMessage;
                                    }
                                }
                            }
                            else
                            {
                                // not existing parent platforms might need to be removed
                                notFoundPlatforms.Add(platform);
                            }
                        }

                        // Check for Obsoleted attributes, only lower version could overwrite
                        if (childAttributes.TryGetValue(platform, out var childAttr) &&
                            childAttr.Obsoleted != null &&
                            (attributes.Obsoleted == null || childAttr.Obsoleted < attributes.Obsoleted))
                        {
                            attributes.Obsoleted = childAttr.Obsoleted;
                            attributes.ObsoletedMessage = childAttr.ObsoletedMessage;
                            attributes.ObsoletedUrl = childAttr.ObsoletedUrl;
                        }
                    }

                    CheckAttributesConsistency(pAttributes);
                    if (crossPlatform && parentAttributes.IsAssemblyAttribute)
                    {
                        foreach (var childAttribute in childAttributes)
                        {
                            if (AllowList(childAttribute.Value))
                            {
                                parentAttributes.Platforms = childAttributes;
                                break;
                            }
                        }
                    }

                    if (notFoundPlatforms.Count > 0)
                    {
                        // For allow list if child narrowing supported platforms by having less platforms support than parent,
                        // not existing parent platforms should be removed
                        if (supportFound)
                        {
                            foreach (var platform in notFoundPlatforms)
                            {
                                parentAttributes.Platforms!.Remove(platform);
                            }
                        }
                        else
                        {
                            parentAttributes.Callsite = Callsite.Empty;
                        }
                    }

                    parentAttributes.IsAssemblyAttribute = false;
                }
                else
                {
                    pAttributes ??= new SmallDictionary<string, Versions>(StringComparer.OrdinalIgnoreCase);
                    foreach (var (platform, attributes) in childAttributes)
                    {
                        pAttributes[platform] = attributes;
                    }

                    parentAttributes.Platforms = pAttributes;
                }

                return;

                static void CheckAttributesConsistency(SmallDictionary<string, Versions> childAttributes)
                {
                    bool allowList = false;
                    using var unsupportedList = PooledHashSet<string>.GetInstance();

                    foreach (var (platform, attributes) in childAttributes)
                    {
                        NormalizeAttribute(attributes);
                        if (AllowList(attributes))
                        {
                            allowList = true;
                        }
                        else if (DenyList(attributes) || !attributes.IsSet())
                        {
                            unsupportedList.Add(platform);
                        }
                    }

                    if (allowList && unsupportedList.Count > 0)
                    {
                        foreach (var name in unsupportedList)
                        {
                            childAttributes.Remove(name);
                        }
                    }
                }

                static Versions NormalizeAttribute(Versions attributes)
                {
                    if (AllowList(attributes))
                    {
                        // For Allow list UnsupportedSecond should not be used.
                        attributes.UnsupportedSecond = null;
                        if (attributes.UnsupportedFirst != null && attributes.UnsupportedFirst == attributes.SupportedFirst)
                        {
                            attributes.SupportedFirst = null;
                        }
                    }
                    // For deny list UnsupportedSecond should only set if there is SupportedFirst version between UnsupportedSecond and UnsupportedFirst 
                    else if (attributes.SupportedFirst == null ||
                            (attributes.UnsupportedSecond != null &&
                             attributes.SupportedFirst > attributes.UnsupportedSecond))
                    {
                        attributes.UnsupportedSecond = null;
                    }

                    return attributes;
                }
            }
        }

        private static bool TryAddValidAttribute([NotNullWhen(true)] ref SmallDictionary<string, Versions>? attributes,
            AttributeData attribute, SmallDictionary<string, (string relatedPlatform, bool isSubset)> relatedPlatforms)
        {
            if (TryParsePlatformNameAndVersion(attribute, out var platformName, out var version))
            {
                attributes ??= new SmallDictionary<string, Versions>(StringComparer.OrdinalIgnoreCase);

                if (!attributes.TryGetValue(platformName, out var _))
                {
                    attributes[platformName] = new Versions();
                }

                if (!AddAttribute(attribute, version, attributes[platformName]))
                {
                    attributes.Remove(platformName);
                }
                else if (relatedPlatforms.TryGetValue(platformName, out var relation) && relation.isSubset)
                {
                    if (!attributes.TryGetValue(relation.relatedPlatform, out var _))
                    {
                        attributes[relation.relatedPlatform] = new Versions();
                    }

                    AddAttribute(attribute, version, attributes[relation.relatedPlatform]);
                }

                return true;
            }

            return false;
        }

        private static bool TryParsePlatformNameAndVersion(AttributeData attribute, out string platformName, [NotNullWhen(true)] out Version? version)
        {
            if (HasNonEmptyStringArgument(attribute, out var argument))
            {
                return TryParsePlatformNameAndVersion(argument, out platformName, out version);
            }

            version = null;
            platformName = string.Empty;
            return false;
        }

        private static bool HasNonEmptyStringArgument(AttributeData attribute, [NotNullWhen(true)] out string? stringArgument)
        {
            if (!attribute.ConstructorArguments.IsEmpty &&
                attribute.ConstructorArguments[0] is { } argument &&
                argument.Type?.SpecialType == SpecialType.System_String &&
                !argument.IsNull &&
                argument.Value is string value &&
                !string.IsNullOrEmpty(value))
            {
                stringArgument = argument.Value.ToString();
                return true;
            }

            stringArgument = null;
            return false;
        }

        private static bool TryParsePlatformNameAndVersion(string osString, out string osPlatformName, [NotNullWhen(true)] out Version? version)
        {
            version = null;
            osPlatformName = string.Empty;
            for (int i = 0; i < osString.Length; i++)
            {
                if (char.IsDigit(osString[i]))
                {
                    if (i > 0 && Version.TryParse(osString[i..], out Version? parsedVersion))
                    {
                        osPlatformName = GetNameAsMacOsWhenOSX(osString[..i]);
                        version = parsedVersion;
                        return true;
                    }

                    return false;
                }
            }

            osPlatformName = GetNameAsMacOsWhenOSX(osString);
            version = EmptyVersion;
            return true;
        }

        private static string GetNameAsMacOsWhenOSX(string platformName) =>
            platformName.Equals(OSX, StringComparison.OrdinalIgnoreCase) ? macOS : platformName;

        private static bool AddAttribute(AttributeData attribute, Version version, Versions attributes)
        {
            var name = attribute.AttributeClass?.Name;
            if (name == SupportedOSPlatformAttribute)
            {
                if (attributes.UnsupportedFirst != null && attributes.UnsupportedFirst == version)
                {
                    attributes.UnsupportedFirst = null;
                    return false;
                }
                else
                {
                    AddOrUpdateSupportedAttribute(attributes, version);
                }
            }
            else if (name == UnsupportedOSPlatformAttribute)
            {
                if (attributes.SupportedFirst != null && attributes.SupportedFirst == version)
                {
                    attributes.SupportedFirst = null;
                }

                AddOrUpdateUnsupportedAttribute(attribute, attributes, version);
            }
            else
            {
                Debug.Assert(name == ObsoletedOSPlatformAttribute);
                AddOrUpdateObsoletedAttribute(attribute, attributes, version);
            }

            return true;

            static void AddOrUpdateObsoletedAttribute(AttributeData attribute, Versions attributes, Version version)
            {
                var message = PopulateMessage(attribute);
                var url = PopulateUrl(attribute);

                if (attributes.Obsoleted != null)
                {
                    // only keep lowest version, ignore other versions
                    if (attributes.Obsoleted > version)
                    {
                        attributes.Obsoleted = version;
                        attributes.ObsoletedMessage = message;
                        attributes.ObsoletedUrl = url;
                    }
                }
                else
                {
                    attributes.Obsoleted = version;
                    attributes.ObsoletedMessage = message;
                    attributes.ObsoletedUrl = url;
                }
            }

            static void AddOrUpdateUnsupportedAttribute(AttributeData attribute, Versions attributes, Version version)
            {
                var message = PopulateMessage(attribute);
                if (attributes.UnsupportedFirst != null)
                {
                    if (attributes.UnsupportedFirst > version)
                    {
                        attributes.UnsupportedSecond = attributes.UnsupportedFirst;
                        attributes.UnsupportedFirst = version;
                        attributes.UnsupportedMessage = message;
                    }
                    else if (attributes.UnsupportedSecond == null ||
                            attributes.UnsupportedSecond > version)
                    {
                        attributes.UnsupportedSecond = version;
                        attributes.UnsupportedMessage = message;
                    }
                }
                else
                {
                    attributes.UnsupportedFirst = version;
                    attributes.UnsupportedMessage = message;
                }
            }

            static void AddOrUpdateSupportedAttribute(Versions attributes, Version version)
            {
                if (attributes.SupportedFirst != null)
                {
                    // only keep lowest version, ignore other versions
                    if (attributes.SupportedFirst > version)
                    {
                        attributes.SupportedFirst = version;
                    }
                }
                else
                {
                    attributes.SupportedFirst = version;
                }
            }
        }

        private static string? PopulateMessage(AttributeData attribute)
        {
            if (attribute.ConstructorArguments.Length == 2)
            {
                return attribute.ConstructorArguments[1].Value?.ToString();
            }

            return null;
        }

#pragma warning disable CA1055 // URI-like return values should not be strings - https://github.com/dotnet/roslyn-analyzers/issues/6379
        private static string? PopulateUrl(AttributeData attribute)
#pragma warning restore CA1055 // URI-like return values should not be strings
        {
            if (attribute.NamedArguments.Length == 1 && attribute.NamedArguments[0].Key is "Url")
            {
                return attribute.NamedArguments[0].Value.Value?.ToString();
            }

            return null;
        }

        /// <summary>
        /// Determines if the attributes supported only for the platform (allow list)
        /// </summary>
        /// <param name="attributes">PlatformAttributes being checked</param>
        /// <returns>true if it is allow list</returns>
        private static bool AllowList(Versions attributes) =>
            attributes.SupportedFirst != null &&
            (attributes.UnsupportedFirst == null || attributes.UnsupportedFirst.IsGreaterThanOrEqualTo(attributes.SupportedFirst));

        /// <summary>
        /// Determines if the attributes unsupported only for the platform (deny list)
        /// </summary>
        /// <param name="attributes">PlatformAttributes being checked</param>
        /// <returns>true if it is deny list</returns>
        private static bool DenyList(Versions attributes) =>
            attributes.UnsupportedFirst != null &&
            (attributes.SupportedFirst == null || attributes.UnsupportedFirst < attributes.SupportedFirst);
    }
}