File: Extensions\ISymbolExtensions.cs
Web Access
Project: src\aspnetcore\src\Validation\gen\Microsoft.Extensions.Validation.ValidationsGenerator.csproj (Microsoft.Extensions.Validation.ValidationsGenerator)
// 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.Linq;
using Microsoft.AspNetCore.App.Analyzers.Infrastructure;
using Microsoft.CodeAnalysis;
 
namespace Microsoft.Extensions.Validation;
 
internal static class ISymbolExtensions
{
    /// <summary>
    /// Inspects the symbol's <c>[Display]</c> and <c>[DisplayName]</c> attributes and returns
    /// (a) the literal display name to bake into the generated metadata, and
    /// (b) whether <c>[Display]</c> uses <see cref="System.ComponentModel.DataAnnotations.DisplayAttribute.ResourceType"/>
    /// (in which case the generator must emit a runtime accessor for the attribute instead of a literal).
    /// </summary>
    public static (string? LiteralDisplayName, bool HasResourceDisplayAttribute) GetDisplayInfo(
        this ISymbol symbol,
        INamedTypeSymbol displayAttributeSymbol,
        INamedTypeSymbol displayNameAttributeSymbol)
    {
        var displayAttr = symbol.GetAttributes().FirstOrDefault(a =>
            a.AttributeClass is { } ac && SymbolEqualityComparer.Default.Equals(ac, displayAttributeSymbol));
 
        if (displayAttr is not null)
        {
            string? name = null;
            bool hasResourceType = false;
 
            foreach (var arg in displayAttr.NamedArguments)
            {
                if (string.Equals(arg.Key, "Name", StringComparison.Ordinal) && arg.Value.Kind == TypedConstantKind.Primitive)
                {
                    name = arg.Value.Value as string;
                }
                else if (string.Equals(arg.Key, "ResourceType", StringComparison.Ordinal) && arg.Value.Kind == TypedConstantKind.Type)
                {
                    hasResourceType = arg.Value.Value is INamedTypeSymbol;
                }
            }
 
            // ResourceType is only meaningful when Name is also set; DisplayAttribute.GetName()
            // returns null otherwise.
            if (hasResourceType && name is not null)
            {
                // Defer to DisplayAttribute.GetName() at runtime; do not bake the literal Name.
                return (LiteralDisplayName: null, HasResourceDisplayAttribute: true);
            }
 
            // [Display(Name = "X")] without ResourceType → bake literal.
            if (name is not null)
            {
                return (LiteralDisplayName: name, HasResourceDisplayAttribute: false);
            }
        }
 
        var displayNameAttr = symbol.GetAttributes().FirstOrDefault(a =>
            a.AttributeClass is { } ac && SymbolEqualityComparer.Default.Equals(ac, displayNameAttributeSymbol));
 
        if (displayNameAttr is not null
            && !displayNameAttr.ConstructorArguments.IsDefaultOrEmpty
            && displayNameAttr.ConstructorArguments[0].Value is string dn)
        {
            // [DisplayName("X")] → bake literal. (No ResourceType to consider.)
            return (LiteralDisplayName: dn, HasResourceDisplayAttribute: false);
        }
 
        return (LiteralDisplayName: null, HasResourceDisplayAttribute: false);
    }
 
    public static bool IsEqualityContract(this IPropertySymbol prop, WellKnownTypes wellKnownTypes) =>
        prop.Name == "EqualityContract"
        && SymbolEqualityComparer.Default.Equals(prop.Type, wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Type))
        && prop.DeclaredAccessibility == Accessibility.Protected;
}