< Summary

Information
Class: NexusLabs.Needlr.Generators.ConstructorGuardAnalysisHelper
Assembly: NexusLabs.Needlr.Generators
File(s): /_work/nick-alienware-needlr-1-1784953859-3e8997/needlr/needlr/src/NexusLabs.Needlr.Generators/ConstructorGuardAnalysisHelper.cs
Line coverage
86%
Covered lines: 572
Uncovered lines: 91
Coverable lines: 663
Total lines: 1207
Line coverage: 86.2%
Branch coverage
77%
Covered branches: 253
Total branches: 326
Branch coverage: 77.6%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

File(s)

/_work/nick-alienware-needlr-1-1784953859-3e8997/needlr/needlr/src/NexusLabs.Needlr.Generators/ConstructorGuardAnalysisHelper.cs

#LineLine coverage
 1using System.Collections.Generic;
 2using System.Collections.Immutable;
 3using System.Linq;
 4
 5using Microsoft.CodeAnalysis;
 6using Microsoft.CodeAnalysis.CSharp;
 7using Microsoft.CodeAnalysis.CSharp.Syntax;
 8using Microsoft.CodeAnalysis.Diagnostics;
 9
 10using NexusLabs.Needlr.Generators.Models;
 11using NexusLabs.Needlr.Roslyn.Shared;
 12
 13namespace NexusLabs.Needlr.Generators;
 14
 15/// <summary>
 16/// Shared analyzer logic for constructor guards applied to generated-constructor
 17/// fields or generated record-overload properties.
 18/// </summary>
 19internal static class ConstructorGuardAnalysisHelper
 20{
 21    private const string ConstructorGuardDefinitionAttributeName = "ConstructorGuardDefinitionAttribute";
 22    private const string DefaultGuardMethodName = "Validate";
 23    private const int AttributeTargetsField = 0x0100;
 24    private const int AttributeTargetsProperty = 0x0080;
 25
 26    /// <summary>
 27    /// Builds a normalized occurrence for a direct
 28    /// <c>ConstructorGuardAttribute</c>.
 29    /// </summary>
 30    internal static ConstructorGuardOccurrence BuildDirectGuardOccurrence(
 31        ISymbol member,
 32        ITypeSymbol memberType,
 33        string memberKind,
 34        AttributeData attribute,
 35        string? ineligibilityReason)
 36    {
 6037        if (attribute.ConstructorArguments.Length == 0)
 38        {
 039            return CreateOccurrence(
 040                member,
 041                memberType,
 042                memberKind,
 043                attribute,
 044                ConstructorGuardOccurrenceKind.BuiltInNone,
 045                ineligibilityReason,
 046                null,
 047                null,
 048                false,
 049                false);
 50        }
 51
 6052        var first = attribute.ConstructorArguments[0];
 6053        if (first.Kind == TypedConstantKind.Enum && first.Value is int enumValue)
 54        {
 3155            var kind = enumValue == 0
 3156                ? ConstructorGuardOccurrenceKind.BuiltInNone
 3157                : ConstructorGuardOccurrenceKind.BuiltInPositive;
 3158            return CreateOccurrence(
 3159                member,
 3160                memberType,
 3161                memberKind,
 3162                attribute,
 3163                kind,
 3164                ineligibilityReason,
 3165                null,
 3166                null,
 3167                false,
 3168                false);
 69        }
 70
 2971        if (first.Value is ITypeSymbol guardType)
 72        {
 2973            var methodNameExplicit = attribute.ConstructorArguments.Length > 1 &&
 2974                attribute.ConstructorArguments[1].Value is string;
 2975            var methodName = methodNameExplicit
 2976                ? (string)attribute.ConstructorArguments[1].Value!
 2977                : DefaultGuardMethodName;
 2978            return CreateOccurrence(
 2979                member,
 2980                memberType,
 2981                memberKind,
 2982                attribute,
 2983                ConstructorGuardOccurrenceKind.CustomType,
 2984                ineligibilityReason,
 2985                guardType,
 2986                methodName,
 2987                methodNameExplicit,
 2988                false);
 89        }
 90
 091        return CreateOccurrence(
 092            member,
 093            memberType,
 094            memberKind,
 095            attribute,
 096            ConstructorGuardOccurrenceKind.BuiltInNone,
 097            ineligibilityReason,
 098            null,
 099            null,
 0100            false,
 0101            false);
 102    }
 103
 104    /// <summary>
 105    /// Builds a normalized occurrence for a custom guard alias usage.
 106    /// </summary>
 107    internal static ConstructorGuardOccurrence BuildAliasOccurrence(
 108        ISymbol member,
 109        ITypeSymbol memberType,
 110        string memberKind,
 111        AttributeData attribute,
 112        string? ineligibilityReason,
 113        ITypeSymbol? guardType,
 114        string? methodName,
 115        bool methodNameExplicit,
 116        bool guardTypeUsageIsInSourceAlias)
 117    {
 32118        return CreateOccurrence(
 32119            member,
 32120            memberType,
 32121            memberKind,
 32122            attribute,
 32123            ConstructorGuardOccurrenceKind.Alias,
 32124            ineligibilityReason,
 32125            guardType,
 32126            methodName,
 32127            methodNameExplicit,
 32128            guardTypeUsageIsInSourceAlias);
 129    }
 130
 131    /// <summary>
 132    /// Resolves a <c>ConstructorGuardDefinitionAttribute</c> from an application-defined
 133    /// alias attribute type.
 134    /// </summary>
 135    internal static bool TryGetGuardDefinition(
 136        INamedTypeSymbol attributeClass,
 137        out ITypeSymbol? guardType,
 138        out string? methodName,
 139        out bool methodNameExplicit)
 140    {
 236141        foreach (var metaAttribute in attributeClass.GetAttributes())
 142        {
 67143            if (metaAttribute.AttributeClass is not { } metaClass ||
 67144                !GeneratedConstructorEligibility.IsNeedlrGeneratorsAttribute(
 67145                    metaClass,
 67146                    ConstructorGuardDefinitionAttributeName))
 147            {
 148                continue;
 149            }
 150
 32151            if (metaAttribute.ConstructorArguments.Length == 0 ||
 32152                metaAttribute.ConstructorArguments[0].Value is not ITypeSymbol resolvedGuardType)
 153            {
 154                continue;
 155            }
 156
 32157            guardType = resolvedGuardType;
 32158            methodNameExplicit = metaAttribute.ConstructorArguments.Length > 1 &&
 32159                metaAttribute.ConstructorArguments[1].Value is string;
 32160            methodName = methodNameExplicit
 32161                ? (string)metaAttribute.ConstructorArguments[1].Value!
 32162                : DefaultGuardMethodName;
 32163            return true;
 164        }
 165
 35166        guardType = null;
 35167        methodName = null;
 35168        methodNameExplicit = false;
 35169        return false;
 170    }
 171
 172    /// <summary>
 173    /// Reports the built-in or custom guard diagnostics for one positive occurrence.
 174    /// </summary>
 175    internal static void AnalyzePositiveGuardOccurrence(
 176        SyntaxNodeAnalysisContext context,
 177        INamedTypeSymbol containingType,
 178        ConstructorGuardOccurrence occurrence,
 179        Location location)
 180    {
 72181        switch (occurrence.Kind)
 182        {
 183            case ConstructorGuardOccurrenceKind.BuiltInPositive:
 14184                AnalyzeBuiltInGuard(context, occurrence, location);
 14185                break;
 186            case ConstructorGuardOccurrenceKind.CustomType:
 27187                AnalyzeCustomGuard(
 27188                    context,
 27189                    containingType,
 27190                    occurrence,
 27191                    location,
 27192                    occurrence.GuardType,
 27193                    occurrence.MethodName,
 27194                    occurrence.MethodNameExplicit,
 27195                    ImmutableArray<ITypeSymbol>.Empty);
 27196                break;
 197            case ConstructorGuardOccurrenceKind.Alias:
 30198                AnalyzeAliasGuardAtUsage(
 30199                    context,
 30200                    containingType,
 30201                    occurrence,
 30202                    location);
 203                break;
 204        }
 30205    }
 206
 207    /// <summary>
 208    /// Returns whether a positive guard occurrence can be emitted as a valid direct
 209    /// call. Used by generators to fail closed when analyzer diagnostics identify an
 210    /// invalid guard declaration.
 211    /// </summary>
 212    internal static bool IsPositiveGuardOccurrenceValidForGeneration(
 213        Compilation compilation,
 214        INamedTypeSymbol containingType,
 215        ConstructorGuardOccurrence occurrence)
 216    {
 7217        switch (occurrence.Kind)
 218        {
 219            case ConstructorGuardOccurrenceKind.BuiltInPositive:
 3220                if (occurrence.Attribute.ConstructorArguments.Length == 0)
 0221                    return false;
 222
 3223                var constant = occurrence.Attribute.ConstructorArguments[0];
 3224                if (!IsDefinedEnumValue(constant) ||
 3225                    constant.Value is not int rawValue)
 226                {
 0227                    return false;
 228                }
 229
 3230                var kind = (BuiltInConstructorGuardKindMirror)rawValue;
 3231                return kind switch
 3232                {
 3233                    BuiltInConstructorGuardKindMirror.NotNull =>
 2234                        CanBeRuntimeNull(occurrence.MemberType),
 3235                    BuiltInConstructorGuardKindMirror.NotNullOrEmpty or
 3236                    BuiltInConstructorGuardKindMirror.NotNullOrWhiteSpace =>
 1237                        occurrence.MemberType.SpecialType ==
 1238                            SpecialType.System_String,
 0239                    _ => false,
 3240                };
 241            case ConstructorGuardOccurrenceKind.CustomType:
 2242                return IsCustomGuardValidForGeneration(
 2243                    compilation,
 2244                    containingType,
 2245                    occurrence,
 2246                    ImmutableArray<ITypeSymbol>.Empty);
 247            case ConstructorGuardOccurrenceKind.Alias:
 2248                if (!TryGetForwardedArgumentTypes(
 2249                    occurrence.Attribute,
 2250                    out var forwardedArgumentTypes,
 2251                    out _))
 252                {
 0253                    return false;
 254                }
 255
 2256                return IsCustomGuardValidForGeneration(
 2257                    compilation,
 2258                    containingType,
 2259                    occurrence,
 2260                    forwardedArgumentTypes);
 261            default:
 0262                return true;
 263        }
 264    }
 265
 266    /// <summary>
 267    /// Reports NDLRGEN047 when an enum-valued guard argument is undefined.
 268    /// </summary>
 269    internal static bool TryReportUndefinedEnum(
 270        SyntaxNodeAnalysisContext context,
 271        Location location,
 272        TypedConstant constant)
 273    {
 16274        if (constant.Kind != TypedConstantKind.Enum ||
 16275            constant.Value is not int rawValue ||
 16276            constant.Type is not { } enumType)
 277        {
 0278            return false;
 279        }
 280
 16281        if (IsDefinedEnumValue(constant))
 12282            return false;
 283
 4284        context.ReportDiagnostic(Diagnostic.Create(
 4285            DiagnosticDescriptors.InvalidConstructorGuardEnumValue,
 4286            location,
 4287            rawValue,
 4288            enumType.Name));
 4289        return true;
 290    }
 291
 292    /// <summary>
 293    /// Resolves the accessible static guard method compatible with a guarded member and
 294    /// any positional arguments forwarded from an alias usage.
 295    /// </summary>
 296    internal static GuardMethodResolution TryResolveGuardMethod(
 297        Compilation compilation,
 298        INamedTypeSymbol withinType,
 299        ITypeSymbol guardType,
 300        string methodName,
 301        ITypeSymbol? memberType,
 302        string memberKind,
 303        ImmutableArray<ITypeSymbol> forwardedArgumentTypes,
 304        out IMethodSymbol? method,
 305        out string? reason,
 306        out GuardResolutionFailureKind failureKind)
 307    {
 83308        method = null;
 83309        reason = null;
 83310        failureKind = GuardResolutionFailureKind.None;
 311
 83312        var candidates = guardType.GetMembers(methodName)
 83313            .OfType<IMethodSymbol>()
 85314            .Where(candidate => candidate.MethodKind == MethodKind.Ordinary)
 83315            .ToList();
 83316        if (candidates.Count == 0)
 317        {
 4318            reason = $"no method named '{methodName}' was found on '{guardType.ToDisplayString()}'";
 4319            failureKind = GuardResolutionFailureKind.General;
 4320            return GuardMethodResolution.NotFound;
 321        }
 322
 79323        var expectedArity = memberType is null
 79324            ? (int?)null
 79325            : forwardedArgumentTypes.Length + 2;
 79326        var matches = new List<IMethodSymbol>();
 327
 328328        foreach (var candidate in candidates)
 329        {
 85330            if (!compilation.IsSymbolAccessibleWithin(candidate, withinType))
 331            {
 0332                reason = "it is not accessible";
 0333                failureKind = GuardResolutionFailureKind.General;
 0334                continue;
 335            }
 336
 85337            if (!candidate.IsStatic)
 338            {
 2339                reason = "it is not static";
 2340                failureKind = GuardResolutionFailureKind.General;
 2341                continue;
 342            }
 343
 83344            if (!candidate.ReturnsVoid)
 345            {
 2346                reason = "it does not return void";
 2347                failureKind = GuardResolutionFailureKind.General;
 2348                continue;
 349            }
 350
 81351            if (candidate.Parameters.Length < 2)
 352            {
 0353                reason = "it does not have at least a value parameter and a trailing string parameter name";
 0354                failureKind = GuardResolutionFailureKind.General;
 0355                continue;
 356            }
 357
 81358            if (expectedArity.HasValue &&
 81359                candidate.Parameters.Length != expectedArity.Value)
 360            {
 2361                reason = $"it has {candidate.Parameters.Length - 2} parameter(s) between the value and the parameter nam
 2362                failureKind = GuardResolutionFailureKind.ForwardedArgument;
 2363                continue;
 364            }
 365
 79366            if (candidate.Parameters[candidate.Parameters.Length - 1].Type.SpecialType !=
 79367                SpecialType.System_String)
 368            {
 0369                reason = "its last parameter is not a string parameter name";
 0370                failureKind = GuardResolutionFailureKind.General;
 0371                continue;
 372            }
 373
 79374            var refKindParameter = candidate.Parameters.FirstOrDefault(
 285375                parameter => parameter.RefKind != RefKind.None);
 79376            if (refKindParameter is not null)
 377            {
 6378                reason = $"its '{refKindParameter.Name}' parameter is passed by '{refKindParameter.RefKind.ToString().To
 6379                failureKind = GuardResolutionFailureKind.General;
 6380                continue;
 381            }
 382
 73383            if (memberType is null)
 384            {
 32385                matches.Add(candidate);
 32386                continue;
 387            }
 388
 41389            var valueParameterType = candidate.Parameters[0].Type;
 41390            var middleParameters = candidate.Parameters
 41391                .Skip(1)
 41392                .Take(candidate.Parameters.Length - 2)
 41393                .ToList();
 394
 395            bool isCompatible;
 396            string? candidateReason;
 397            GuardResolutionFailureKind candidateFailureKind;
 398
 41399            if (candidate.IsGenericMethod)
 400            {
 15401                isCompatible = TryInferGenericParameterCompatibility(
 15402                    candidate,
 15403                    valueParameterType,
 15404                    memberType,
 15405                    memberKind,
 15406                    middleParameters,
 15407                    forwardedArgumentTypes,
 15408                    compilation,
 15409                    out candidateReason,
 15410                    out candidateFailureKind);
 411            }
 412            else
 413            {
 26414                isCompatible = TryCheckNonGenericCompatibility(
 26415                    memberType,
 26416                    memberKind,
 26417                    valueParameterType,
 26418                    middleParameters,
 26419                    forwardedArgumentTypes,
 26420                    compilation,
 26421                    out candidateReason,
 26422                    out candidateFailureKind);
 423            }
 424
 41425            if (!isCompatible)
 426            {
 16427                reason = candidateReason;
 16428                failureKind = candidateFailureKind;
 16429                continue;
 430            }
 431
 25432            matches.Add(candidate);
 433        }
 434
 79435        if (matches.Count == 1)
 436        {
 45437            method = matches[0];
 45438            failureKind = GuardResolutionFailureKind.None;
 45439            return GuardMethodResolution.Found;
 440        }
 441
 34442        if (matches.Count > 1)
 443        {
 6444            failureKind = GuardResolutionFailureKind.None;
 6445            return GuardMethodResolution.Ambiguous;
 446        }
 447
 28448        if (reason is null)
 449        {
 0450            reason = "no accessible static method compatible with (value, ...forwarded arguments, string parameterName) 
 0451            failureKind = GuardResolutionFailureKind.General;
 452        }
 453
 28454        return GuardMethodResolution.NotFound;
 455    }
 456
 457    /// <summary>
 458    /// Returns why an alias definition cannot target constructor-guard members.
 459    /// </summary>
 460    internal static string? GetGuardDefinitionTargetInvalidReason(
 461        INamedTypeSymbol attributeClass)
 462    {
 38463        if (!InheritsFromSystemAttribute(attributeClass))
 2464            return "not derived from System.Attribute";
 465
 36466        var usageAttribute = attributeClass.GetAttributes()
 36467            .FirstOrDefault(attribute =>
 108468                attribute.AttributeClass?.ToDisplayString() ==
 108469                "System.AttributeUsageAttribute");
 36470        if (usageAttribute is not null &&
 36471            usageAttribute.ConstructorArguments.Length > 0 &&
 36472            usageAttribute.ConstructorArguments[0].Value is int validOn &&
 36473            (validOn & (AttributeTargetsField | AttributeTargetsProperty)) == 0)
 474        {
 2475            return "not usable on fields or properties ([AttributeUsage] includes neither AttributeTargets.Field nor Att
 476        }
 477
 34478        return null;
 479    }
 480
 481    /// <summary>
 482    /// Gets the source location of an attribute occurrence.
 483    /// </summary>
 484    internal static Location GetAttributeLocation(
 485        SyntaxNodeAnalysisContext context,
 486        AttributeData attribute)
 487    {
 142488        return attribute.ApplicationSyntaxReference?
 142489            .GetSyntax(context.CancellationToken)
 142490            .GetLocation() ?? Location.None;
 491    }
 492
 493    private static ConstructorGuardOccurrence CreateOccurrence(
 494        ISymbol member,
 495        ITypeSymbol memberType,
 496        string memberKind,
 497        AttributeData attribute,
 498        ConstructorGuardOccurrenceKind kind,
 499        string? ineligibilityReason,
 500        ITypeSymbol? guardType,
 501        string? methodName,
 502        bool methodNameExplicit,
 503        bool guardTypeUsageIsInSourceAlias)
 504    {
 92505        return new ConstructorGuardOccurrence(
 92506            member,
 92507            memberType,
 92508            memberKind,
 92509            attribute,
 92510            kind,
 92511            ineligibilityReason,
 92512            guardType,
 92513            methodName,
 92514            methodNameExplicit,
 92515            guardTypeUsageIsInSourceAlias);
 516    }
 517
 518    private static bool IsCustomGuardValidForGeneration(
 519        Compilation compilation,
 520        INamedTypeSymbol containingType,
 521        ConstructorGuardOccurrence occurrence,
 522        ImmutableArray<ITypeSymbol> forwardedArgumentTypes)
 523    {
 4524        if (occurrence.GuardType is null ||
 4525            occurrence.GuardType.TypeKind == TypeKind.Error ||
 4526            !compilation.IsSymbolAccessibleWithin(
 4527                occurrence.GuardType,
 4528                containingType) ||
 4529            occurrence.MethodNameExplicit &&
 4530                string.IsNullOrWhiteSpace(occurrence.MethodName))
 531        {
 0532            return false;
 533        }
 534
 4535        var methodName = string.IsNullOrEmpty(occurrence.MethodName)
 4536            ? DefaultGuardMethodName
 4537            : occurrence.MethodName!;
 4538        return TryResolveGuardMethod(
 4539            compilation,
 4540            containingType,
 4541            occurrence.GuardType,
 4542            methodName,
 4543            occurrence.MemberType,
 4544            occurrence.MemberKind,
 4545            forwardedArgumentTypes,
 4546            out _,
 4547            out _,
 4548            out _) == GuardMethodResolution.Found;
 549    }
 550
 551    private static bool IsDefinedEnumValue(TypedConstant constant)
 552    {
 19553        if (constant.Kind != TypedConstantKind.Enum ||
 19554            constant.Value is not int rawValue ||
 19555            constant.Type is not { } enumType)
 556        {
 0557            return false;
 558        }
 559
 19560        return enumType.GetMembers()
 19561            .OfType<IFieldSymbol>()
 19562            .Any(field =>
 73563                field.HasConstantValue &&
 73564                field.ConstantValue is int memberValue &&
 73565                memberValue == rawValue);
 566    }
 567
 568    private static void AnalyzeAliasGuardAtUsage(
 569        SyntaxNodeAnalysisContext context,
 570        INamedTypeSymbol containingType,
 571        ConstructorGuardOccurrence occurrence,
 572        Location location)
 573    {
 30574        if (!TryGetForwardedArgumentTypes(
 30575            occurrence.Attribute,
 30576            out var forwardedArgumentTypes,
 30577            out var unsupportedReason))
 578        {
 6579            context.ReportDiagnostic(Diagnostic.Create(
 6580                DiagnosticDescriptors.ConstructorGuardAliasUsageArgumentUnsupported,
 6581                location,
 6582                occurrence.Member.Name,
 6583                unsupportedReason));
 6584            return;
 585        }
 586
 24587        if (!occurrence.GuardTypeUsageIsInSourceAlias)
 588        {
 0589            AnalyzeCustomGuard(
 0590                context,
 0591                containingType,
 0592                occurrence,
 0593                location,
 0594                occurrence.GuardType,
 0595                occurrence.MethodName,
 0596                occurrence.MethodNameExplicit,
 0597                forwardedArgumentTypes);
 0598            return;
 599        }
 600
 24601        if (occurrence.GuardType is null ||
 24602            occurrence.GuardType.TypeKind == TypeKind.Error)
 603        {
 0604            return;
 605        }
 606
 24607        var methodName = string.IsNullOrEmpty(occurrence.MethodName)
 24608            ? DefaultGuardMethodName
 24609            : occurrence.MethodName!;
 24610        var resolution = TryResolveGuardMethod(
 24611            context.Compilation,
 24612            containingType,
 24613            occurrence.GuardType,
 24614            methodName,
 24615            occurrence.MemberType,
 24616            occurrence.MemberKind,
 24617            forwardedArgumentTypes,
 24618            out _,
 24619            out var reason,
 24620            out var failureKind);
 621
 24622        if (resolution == GuardMethodResolution.NotFound &&
 24623            failureKind == GuardResolutionFailureKind.ForwardedArgument)
 624        {
 10625            context.ReportDiagnostic(Diagnostic.Create(
 10626                DiagnosticDescriptors.ConstructorGuardForwardedArgumentIncompatible,
 10627                location,
 10628                methodName,
 10629                occurrence.GuardType.ToDisplayString(),
 10630                occurrence.Member.Name,
 10631                occurrence.MemberType.ToDisplayString(),
 10632                reason));
 633        }
 14634        else if (resolution == GuardMethodResolution.NotFound &&
 14635            reason == GetMemberTypeIncompatibleReason(occurrence.MemberKind))
 636        {
 2637            context.ReportDiagnostic(Diagnostic.Create(
 2638                DiagnosticDescriptors.ConstructorGuardMethodInvalid,
 2639                location,
 2640                methodName,
 2641                occurrence.GuardType.ToDisplayString(),
 2642                occurrence.Member.Name,
 2643                occurrence.MemberType.ToDisplayString(),
 2644                reason));
 645        }
 12646        else if (resolution == GuardMethodResolution.Ambiguous)
 647        {
 2648            context.ReportDiagnostic(Diagnostic.Create(
 2649                DiagnosticDescriptors.ConstructorGuardMethodAmbiguous,
 2650                location,
 2651                methodName,
 2652                occurrence.GuardType.ToDisplayString(),
 2653                occurrence.Member.Name,
 2654                occurrence.MemberType.ToDisplayString()));
 655        }
 12656    }
 657
 658    private static void AnalyzeBuiltInGuard(
 659        SyntaxNodeAnalysisContext context,
 660        ConstructorGuardOccurrence occurrence,
 661        Location location)
 662    {
 14663        var first = occurrence.Attribute.ConstructorArguments[0];
 14664        if (TryReportUndefinedEnum(context, location, first))
 2665            return;
 666
 12667        if (first.Value is not int rawValue)
 0668            return;
 669
 12670        var kind = (BuiltInConstructorGuardKindMirror)rawValue;
 671        switch (kind)
 672        {
 673            case BuiltInConstructorGuardKindMirror.NotNull:
 7674                if (!CanBeRuntimeNull(occurrence.MemberType))
 675                {
 4676                    context.ReportDiagnostic(Diagnostic.Create(
 4677                        DiagnosticDescriptors.ConstructorGuardIncompatibleWithFieldType,
 4678                        location,
 4679                        "NotNull",
 4680                        occurrence.Member.Name,
 4681                        occurrence.MemberType.ToDisplayString(),
 4682                        $"the {occurrence.MemberKind}'s type is a non-nullable value type, so a runtime null value is ne
 683                }
 684
 4685                break;
 686            case BuiltInConstructorGuardKindMirror.NotNullOrEmpty:
 687            case BuiltInConstructorGuardKindMirror.NotNullOrWhiteSpace:
 5688                if (occurrence.MemberType.SpecialType != SpecialType.System_String)
 689                {
 4690                    context.ReportDiagnostic(Diagnostic.Create(
 4691                        DiagnosticDescriptors.ConstructorGuardIncompatibleWithFieldType,
 4692                        location,
 4693                        kind.ToString(),
 4694                        occurrence.Member.Name,
 4695                        occurrence.MemberType.ToDisplayString(),
 4696                        $"this guard only applies to string-compatible {GetMemberKindPlural(occurrence.MemberKind)}"));
 697                }
 698
 699                break;
 700        }
 8701    }
 702
 703    private static void AnalyzeCustomGuard(
 704        SyntaxNodeAnalysisContext context,
 705        INamedTypeSymbol containingType,
 706        ConstructorGuardOccurrence occurrence,
 707        Location location,
 708        ITypeSymbol? guardType,
 709        string? methodName,
 710        bool methodNameExplicit,
 711        ImmutableArray<ITypeSymbol> forwardedArgumentTypes)
 712    {
 27713        if (guardType is null || guardType.TypeKind == TypeKind.Error)
 714        {
 2715            context.ReportDiagnostic(Diagnostic.Create(
 2716                DiagnosticDescriptors.ConstructorGuardTypeInvalid,
 2717                location,
 2718                occurrence.Member.Name,
 2719                "the guard type could not be resolved"));
 2720            return;
 721        }
 722
 25723        if (!context.Compilation.IsSymbolAccessibleWithin(guardType, containingType))
 724        {
 0725            context.ReportDiagnostic(Diagnostic.Create(
 0726                DiagnosticDescriptors.ConstructorGuardTypeInvalid,
 0727                location,
 0728                occurrence.Member.Name,
 0729                $"'{guardType.ToDisplayString()}' is not accessible from '{containingType.ToDisplayString()}'"));
 0730            return;
 731        }
 732
 25733        if (methodNameExplicit && string.IsNullOrWhiteSpace(methodName))
 734        {
 2735            context.ReportDiagnostic(Diagnostic.Create(
 2736                DiagnosticDescriptors.ConstructorGuardMethodNameInvalid,
 2737                location,
 2738                occurrence.Member.Name));
 2739            return;
 740        }
 741
 23742        var effectiveMethodName = string.IsNullOrEmpty(methodName)
 23743            ? DefaultGuardMethodName
 23744            : methodName!;
 23745        var resolution = TryResolveGuardMethod(
 23746            context.Compilation,
 23747            containingType,
 23748            guardType,
 23749            effectiveMethodName,
 23750            occurrence.MemberType,
 23751            occurrence.MemberKind,
 23752            forwardedArgumentTypes,
 23753            out _,
 23754            out var reason,
 23755            out var failureKind);
 756
 23757        switch (resolution)
 758        {
 759            case GuardMethodResolution.NotFound
 18760                when failureKind == GuardResolutionFailureKind.ForwardedArgument:
 0761                context.ReportDiagnostic(Diagnostic.Create(
 0762                    DiagnosticDescriptors.ConstructorGuardForwardedArgumentIncompatible,
 0763                    location,
 0764                    effectiveMethodName,
 0765                    guardType.ToDisplayString(),
 0766                    occurrence.Member.Name,
 0767                    occurrence.MemberType.ToDisplayString(),
 0768                    reason));
 0769                break;
 770            case GuardMethodResolution.NotFound:
 18771                context.ReportDiagnostic(Diagnostic.Create(
 18772                    DiagnosticDescriptors.ConstructorGuardMethodInvalid,
 18773                    location,
 18774                    effectiveMethodName,
 18775                    guardType.ToDisplayString(),
 18776                    occurrence.Member.Name,
 18777                    occurrence.MemberType.ToDisplayString(),
 18778                    reason ?? "no compatible method was found"));
 18779                break;
 780            case GuardMethodResolution.Ambiguous:
 2781                context.ReportDiagnostic(Diagnostic.Create(
 2782                    DiagnosticDescriptors.ConstructorGuardMethodAmbiguous,
 2783                    location,
 2784                    effectiveMethodName,
 2785                    guardType.ToDisplayString(),
 2786                    occurrence.Member.Name,
 2787                    occurrence.MemberType.ToDisplayString()));
 788                break;
 789        }
 2790    }
 791
 792    private static bool TryGetForwardedArgumentTypes(
 793        AttributeData attribute,
 794        out ImmutableArray<ITypeSymbol> forwardedArgumentTypes,
 795        out string? unsupportedReason)
 796    {
 32797        if (attribute.NamedArguments.Length > 0)
 798        {
 2799            forwardedArgumentTypes = ImmutableArray<ITypeSymbol>.Empty;
 2800            unsupportedReason = $"named argument '{attribute.NamedArguments[0].Key}' is not forwarded to the guard metho
 2801            return false;
 802        }
 803
 30804        if (attribute.ConstructorArguments.Length == 0)
 805        {
 3806            forwardedArgumentTypes = ImmutableArray<ITypeSymbol>.Empty;
 3807            unsupportedReason = null;
 3808            return true;
 809        }
 810
 27811        var builder = ImmutableArray.CreateBuilder<ITypeSymbol>(
 27812            attribute.ConstructorArguments.Length);
 106813        for (var i = 0; i < attribute.ConstructorArguments.Length; i++)
 814        {
 30815            var constant = attribute.ConstructorArguments[i];
 30816            if (!TypedConstantRenderer.TryRender(constant, out _))
 817            {
 4818                forwardedArgumentTypes = ImmutableArray<ITypeSymbol>.Empty;
 4819                unsupportedReason = $"positional argument {i + 1} is {DescribeUnsupportedConstant(constant)}, which is n
 4820                return false;
 821            }
 822
 26823            builder.Add(constant.Type!);
 824        }
 825
 23826        forwardedArgumentTypes = builder.MoveToImmutable();
 23827        unsupportedReason = null;
 23828        return true;
 829    }
 830
 831    private static string DescribeUnsupportedConstant(TypedConstant constant)
 832    {
 4833        if (constant.Kind == TypedConstantKind.Array)
 2834            return "an array";
 835
 2836        if (constant.Value is float or double)
 2837            return "a floating-point value";
 838
 0839        return "an unsupported value";
 840    }
 841
 842    private static bool CanBeRuntimeNull(ITypeSymbol type)
 843    {
 9844        if (type.IsReferenceType)
 3845            return true;
 846
 6847        if (type is ITypeParameterSymbol typeParameter)
 848        {
 3849            return !typeParameter.HasValueTypeConstraint &&
 3850                !typeParameter.HasUnmanagedTypeConstraint;
 851        }
 852
 3853        return type is INamedTypeSymbol
 3854        {
 3855            OriginalDefinition.SpecialType: SpecialType.System_Nullable_T,
 3856        };
 857    }
 858
 859    private static bool IsAssignableTo(
 860        ITypeSymbol sourceType,
 861        ITypeSymbol parameterType,
 862        Compilation compilation)
 863    {
 43864        if (SymbolEqualityComparer.Default.Equals(sourceType, parameterType))
 35865            return true;
 866
 8867        var conversion = compilation.ClassifyConversion(sourceType, parameterType);
 8868        return conversion.Exists && (conversion.IsIdentity || conversion.IsImplicit);
 869    }
 870
 871    private static bool TryCheckNonGenericCompatibility(
 872        ITypeSymbol memberType,
 873        string memberKind,
 874        ITypeSymbol valueParameterType,
 875        List<IParameterSymbol> middleParameters,
 876        ImmutableArray<ITypeSymbol> forwardedArgumentTypes,
 877        Compilation compilation,
 878        out string? reason,
 879        out GuardResolutionFailureKind failureKind)
 880    {
 26881        if (!IsAssignableTo(memberType, valueParameterType, compilation))
 882        {
 4883            reason = GetMemberTypeIncompatibleReason(memberKind);
 4884            failureKind = GuardResolutionFailureKind.General;
 4885            return false;
 886        }
 887
 70888        for (var i = 0; i < middleParameters.Count; i++)
 889        {
 17890            if (IsAssignableTo(
 17891                forwardedArgumentTypes[i],
 17892                middleParameters[i].Type,
 17893                compilation))
 894            {
 895                continue;
 896            }
 897
 4898            reason = $"its parameter '{middleParameters[i].Name}' of type '{middleParameters[i].Type.ToDisplayString()}'
 4899            failureKind = GuardResolutionFailureKind.ForwardedArgument;
 4900            return false;
 901        }
 902
 18903        reason = null;
 18904        failureKind = GuardResolutionFailureKind.None;
 18905        return true;
 906    }
 907
 908    private static bool TryInferGenericParameterCompatibility(
 909        IMethodSymbol genericMethod,
 910        ITypeSymbol parameterType,
 911        ITypeSymbol memberType,
 912        string memberKind,
 913        List<IParameterSymbol> middleParameters,
 914        ImmutableArray<ITypeSymbol> forwardedArgumentTypes,
 915        Compilation compilation,
 916        out string? reason,
 917        out GuardResolutionFailureKind failureKind)
 918    {
 15919        var methodTypeParameters = new HashSet<ITypeSymbol>(
 15920            genericMethod.TypeParameters,
 15921            SymbolEqualityComparer.Default);
 15922        string? lastReason = null;
 15923        var lastFailureKind = GuardResolutionFailureKind.General;
 924
 173925        foreach (var candidateType in GetTypeAndSupertypes(memberType))
 926        {
 75927            var substitution = new Dictionary<ITypeSymbol, ITypeSymbol>(
 75928                SymbolEqualityComparer.Default);
 75929            if (!TryUnify(
 75930                parameterType,
 75931                candidateType,
 75932                methodTypeParameters,
 75933                substitution))
 934            {
 935                continue;
 936            }
 937
 75938            var forwardedMismatch = false;
 188939            for (var i = 0; i < middleParameters.Count; i++)
 940            {
 35941                if (TryUnify(
 35942                    middleParameters[i].Type,
 35943                    forwardedArgumentTypes[i],
 35944                    methodTypeParameters,
 35945                    substitution))
 946                {
 947                    continue;
 948                }
 949
 16950                lastReason ??= $"its parameter '{middleParameters[i].Name}' cannot accept the forwarded argument of type
 16951                lastFailureKind = GuardResolutionFailureKind.ForwardedArgument;
 16952                forwardedMismatch = true;
 16953                break;
 954            }
 955
 75956            if (forwardedMismatch)
 957                continue;
 958
 59959            var unboundParameter = genericMethod.TypeParameters.FirstOrDefault(
 152960                typeParameter => !substitution.ContainsKey(typeParameter));
 59961            if (unboundParameter is not null)
 962            {
 18963                var onlyForwarded = IsReachableOnlyThroughForwardedParameters(
 18964                    unboundParameter,
 18965                    parameterType,
 18966                    middleParameters);
 18967                lastReason ??= onlyForwarded
 18968                    ? $"its type parameter '{unboundParameter.Name}' cannot be inferred from the {memberKind}'s type or 
 18969                    : $"its type parameter '{unboundParameter.Name}' cannot be inferred from the {memberKind}'s type";
 18970                lastFailureKind = onlyForwarded
 18971                    ? GuardResolutionFailureKind.ForwardedArgument
 18972                    : GuardResolutionFailureKind.General;
 18973                continue;
 974            }
 975
 41976            var constraintViolation = FindConstraintViolation(
 41977                genericMethod.TypeParameters,
 41978                substitution,
 41979                compilation,
 41980                out var violatingTypeParameter);
 41981            if (constraintViolation is not null)
 982            {
 34983                var onlyForwarded = violatingTypeParameter is not null &&
 34984                    IsReachableOnlyThroughForwardedParameters(
 34985                        violatingTypeParameter,
 34986                        parameterType,
 34987                        middleParameters);
 34988                lastReason ??= constraintViolation;
 34989                lastFailureKind = onlyForwarded
 34990                    ? GuardResolutionFailureKind.ForwardedArgument
 34991                    : GuardResolutionFailureKind.General;
 34992                continue;
 993            }
 994
 7995            reason = null;
 7996            failureKind = GuardResolutionFailureKind.None;
 7997            return true;
 998        }
 999
 81000        reason = lastReason ?? GetMemberTypeIncompatibleReason(memberKind);
 81001        failureKind = lastReason is null
 81002            ? GuardResolutionFailureKind.General
 81003            : lastFailureKind;
 81004        return false;
 71005    }
 1006
 1007    private static string GetMemberTypeIncompatibleReason(string memberKind)
 1008    {
 61009        return $"its value parameter type is not compatible with the {memberKind}'s type";
 1010    }
 1011
 1012    private static string GetMemberKindPlural(string memberKind)
 1013    {
 41014        return memberKind == "property" ? "properties" : memberKind + "s";
 1015    }
 1016
 1017    private static bool ContainsTypeParameter(
 1018        ITypeSymbol type,
 1019        ITypeParameterSymbol typeParameter)
 1020    {
 681021        if (SymbolEqualityComparer.Default.Equals(type, typeParameter))
 341022            return true;
 1023
 341024        if (type is INamedTypeSymbol namedType)
 1025        {
 01026            return namedType.TypeArguments.Any(
 01027                typeArgument => ContainsTypeParameter(typeArgument, typeParameter));
 1028        }
 1029
 341030        if (type is IArrayTypeSymbol arrayType)
 01031            return ContainsTypeParameter(arrayType.ElementType, typeParameter);
 1032
 341033        return false;
 1034    }
 1035
 1036    private static bool IsReachableOnlyThroughForwardedParameters(
 1037        ITypeParameterSymbol typeParameter,
 1038        ITypeSymbol valueParameterType,
 1039        List<IParameterSymbol> middleParameters)
 1040    {
 521041        if (ContainsTypeParameter(valueParameterType, typeParameter))
 181042            return false;
 1043
 341044        return middleParameters.Any(
 501045            parameter => ContainsTypeParameter(parameter.Type, typeParameter));
 1046    }
 1047
 1048    private static string? FindConstraintViolation(
 1049        ImmutableArray<ITypeParameterSymbol> typeParameters,
 1050        Dictionary<ITypeSymbol, ITypeSymbol> substitution,
 1051        Compilation compilation,
 1052        out ITypeParameterSymbol? violatingTypeParameter)
 1053    {
 1621054        foreach (var typeParameter in typeParameters)
 1055        {
 571056            if (!substitution.TryGetValue(typeParameter, out var argumentType))
 1057                continue;
 1058
 571059            if (typeParameter.HasReferenceTypeConstraint &&
 571060                !argumentType.IsReferenceType)
 1061            {
 01062                violatingTypeParameter = typeParameter;
 01063                return $"its type parameter '{typeParameter.Name}' requires a reference type, but '{argumentType.ToDispl
 1064            }
 1065
 571066            if (typeParameter.HasValueTypeConstraint &&
 571067                !argumentType.IsValueType)
 1068            {
 01069                violatingTypeParameter = typeParameter;
 01070                return $"its type parameter '{typeParameter.Name}' requires a non-nullable value type, but '{argumentTyp
 1071            }
 1072
 571073            if (typeParameter.HasUnmanagedTypeConstraint &&
 571074                !argumentType.IsUnmanagedType)
 1075            {
 01076                violatingTypeParameter = typeParameter;
 01077                return $"its type parameter '{typeParameter.Name}' requires an unmanaged type, but '{argumentType.ToDisp
 1078            }
 1079
 571080            if (typeParameter.HasConstructorConstraint &&
 571081                argumentType is INamedTypeSymbol namedArgumentType &&
 571082                namedArgumentType.TypeKind != TypeKind.Struct &&
 571083                !GeneratedConstructorEligibility.HasAccessibleParameterlessConstructor(
 571084                    namedArgumentType))
 1085            {
 01086                violatingTypeParameter = typeParameter;
 01087                return $"its type parameter '{typeParameter.Name}' requires a public parameterless constructor, which '{
 1088            }
 1089
 1481090            foreach (var constraintType in typeParameter.ConstraintTypes)
 1091            {
 341092                if (SymbolEqualityComparer.Default.Equals(
 341093                    argumentType,
 341094                    constraintType))
 1095                {
 1096                    continue;
 1097                }
 1098
 341099                var conversion = compilation.ClassifyConversion(
 341100                    argumentType,
 341101                    constraintType);
 341102                if (conversion.Exists &&
 341103                    (conversion.IsIdentity || conversion.IsImplicit))
 1104                {
 1105                    continue;
 1106                }
 1107
 341108                violatingTypeParameter = typeParameter;
 341109                return $"its type parameter '{typeParameter.Name}' requires '{constraintType.ToDisplayString()}', which 
 1110            }
 1111        }
 1112
 71113        violatingTypeParameter = null;
 71114        return null;
 1115    }
 1116
 1117    private static IEnumerable<ITypeSymbol> GetTypeAndSupertypes(ITypeSymbol type)
 1118    {
 151119        yield return type;
 1120
 81121        var current = (type as INamedTypeSymbol)?.BaseType;
 201122        while (current is not null)
 1123        {
 121124            yield return current;
 121125            current = current.BaseType;
 1126        }
 1127
 1121128        foreach (var iface in type.AllInterfaces)
 1129        {
 481130            yield return iface;
 1131        }
 81132    }
 1133
 1134    private static bool TryUnify(
 1135        ITypeSymbol parameterType,
 1136        ITypeSymbol candidateType,
 1137        HashSet<ITypeSymbol> methodTypeParameters,
 1138        Dictionary<ITypeSymbol, ITypeSymbol> substitution)
 1139    {
 1121140        if (parameterType is ITypeParameterSymbol typeParameter &&
 1121141            methodTypeParameters.Contains(typeParameter))
 1142        {
 1081143            if (substitution.TryGetValue(typeParameter, out var bound))
 171144                return SymbolEqualityComparer.Default.Equals(bound, candidateType);
 1145
 911146            substitution[typeParameter] = candidateType;
 911147            return true;
 1148        }
 1149
 41150        if (parameterType is INamedTypeSymbol namedParameter &&
 41151            candidateType is INamedTypeSymbol namedCandidate)
 1152        {
 41153            if (!SymbolEqualityComparer.Default.Equals(
 41154                namedParameter.OriginalDefinition,
 41155                namedCandidate.OriginalDefinition))
 1156            {
 01157                return false;
 1158            }
 1159
 41160            if (namedParameter.TypeArguments.Length !=
 41161                namedCandidate.TypeArguments.Length)
 1162            {
 01163                return false;
 1164            }
 1165
 121166            for (var i = 0; i < namedParameter.TypeArguments.Length; i++)
 1167            {
 21168                if (!TryUnify(
 21169                    namedParameter.TypeArguments[i],
 21170                    namedCandidate.TypeArguments[i],
 21171                    methodTypeParameters,
 21172                    substitution))
 1173                {
 01174                    return false;
 1175                }
 1176            }
 1177
 41178            return true;
 1179        }
 1180
 01181        if (parameterType is IArrayTypeSymbol arrayParameter &&
 01182            candidateType is IArrayTypeSymbol arrayCandidate)
 1183        {
 01184            return TryUnify(
 01185                arrayParameter.ElementType,
 01186                arrayCandidate.ElementType,
 01187                methodTypeParameters,
 01188                substitution);
 1189        }
 1190
 01191        return SymbolEqualityComparer.Default.Equals(parameterType, candidateType);
 1192    }
 1193
 1194    private static bool InheritsFromSystemAttribute(INamedTypeSymbol type)
 1195    {
 381196        var current = type.BaseType;
 401197        while (current is not null)
 1198        {
 381199            if (current.ToDisplayString() == "System.Attribute")
 361200                return true;
 1201
 21202            current = current.BaseType;
 1203        }
 1204
 21205        return false;
 1206    }
 1207}

Methods/Properties

BuildDirectGuardOccurrence(Microsoft.CodeAnalysis.ISymbol,Microsoft.CodeAnalysis.ITypeSymbol,System.String,Microsoft.CodeAnalysis.AttributeData,System.String)
BuildAliasOccurrence(Microsoft.CodeAnalysis.ISymbol,Microsoft.CodeAnalysis.ITypeSymbol,System.String,Microsoft.CodeAnalysis.AttributeData,System.String,Microsoft.CodeAnalysis.ITypeSymbol,System.String,System.Boolean,System.Boolean)
TryGetGuardDefinition(Microsoft.CodeAnalysis.INamedTypeSymbol,Microsoft.CodeAnalysis.ITypeSymbol&,System.String&,System.Boolean&)
AnalyzePositiveGuardOccurrence(Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext,Microsoft.CodeAnalysis.INamedTypeSymbol,NexusLabs.Needlr.Generators.Models.ConstructorGuardOccurrence,Microsoft.CodeAnalysis.Location)
IsPositiveGuardOccurrenceValidForGeneration(Microsoft.CodeAnalysis.Compilation,Microsoft.CodeAnalysis.INamedTypeSymbol,NexusLabs.Needlr.Generators.Models.ConstructorGuardOccurrence)
TryReportUndefinedEnum(Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext,Microsoft.CodeAnalysis.Location,Microsoft.CodeAnalysis.TypedConstant)
TryResolveGuardMethod(Microsoft.CodeAnalysis.Compilation,Microsoft.CodeAnalysis.INamedTypeSymbol,Microsoft.CodeAnalysis.ITypeSymbol,System.String,Microsoft.CodeAnalysis.ITypeSymbol,System.String,System.Collections.Immutable.ImmutableArray`1<Microsoft.CodeAnalysis.ITypeSymbol>,Microsoft.CodeAnalysis.IMethodSymbol&,System.String&,NexusLabs.Needlr.Generators.Models.GuardResolutionFailureKind&)
GetGuardDefinitionTargetInvalidReason(Microsoft.CodeAnalysis.INamedTypeSymbol)
GetAttributeLocation(Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext,Microsoft.CodeAnalysis.AttributeData)
CreateOccurrence(Microsoft.CodeAnalysis.ISymbol,Microsoft.CodeAnalysis.ITypeSymbol,System.String,Microsoft.CodeAnalysis.AttributeData,NexusLabs.Needlr.Generators.Models.ConstructorGuardOccurrenceKind,System.String,Microsoft.CodeAnalysis.ITypeSymbol,System.String,System.Boolean,System.Boolean)
IsCustomGuardValidForGeneration(Microsoft.CodeAnalysis.Compilation,Microsoft.CodeAnalysis.INamedTypeSymbol,NexusLabs.Needlr.Generators.Models.ConstructorGuardOccurrence,System.Collections.Immutable.ImmutableArray`1<Microsoft.CodeAnalysis.ITypeSymbol>)
IsDefinedEnumValue(Microsoft.CodeAnalysis.TypedConstant)
AnalyzeAliasGuardAtUsage(Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext,Microsoft.CodeAnalysis.INamedTypeSymbol,NexusLabs.Needlr.Generators.Models.ConstructorGuardOccurrence,Microsoft.CodeAnalysis.Location)
AnalyzeBuiltInGuard(Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext,NexusLabs.Needlr.Generators.Models.ConstructorGuardOccurrence,Microsoft.CodeAnalysis.Location)
AnalyzeCustomGuard(Microsoft.CodeAnalysis.Diagnostics.SyntaxNodeAnalysisContext,Microsoft.CodeAnalysis.INamedTypeSymbol,NexusLabs.Needlr.Generators.Models.ConstructorGuardOccurrence,Microsoft.CodeAnalysis.Location,Microsoft.CodeAnalysis.ITypeSymbol,System.String,System.Boolean,System.Collections.Immutable.ImmutableArray`1<Microsoft.CodeAnalysis.ITypeSymbol>)
TryGetForwardedArgumentTypes(Microsoft.CodeAnalysis.AttributeData,System.Collections.Immutable.ImmutableArray`1<Microsoft.CodeAnalysis.ITypeSymbol>&,System.String&)
DescribeUnsupportedConstant(Microsoft.CodeAnalysis.TypedConstant)
CanBeRuntimeNull(Microsoft.CodeAnalysis.ITypeSymbol)
IsAssignableTo(Microsoft.CodeAnalysis.ITypeSymbol,Microsoft.CodeAnalysis.ITypeSymbol,Microsoft.CodeAnalysis.Compilation)
TryCheckNonGenericCompatibility(Microsoft.CodeAnalysis.ITypeSymbol,System.String,Microsoft.CodeAnalysis.ITypeSymbol,System.Collections.Generic.List`1<Microsoft.CodeAnalysis.IParameterSymbol>,System.Collections.Immutable.ImmutableArray`1<Microsoft.CodeAnalysis.ITypeSymbol>,Microsoft.CodeAnalysis.Compilation,System.String&,NexusLabs.Needlr.Generators.Models.GuardResolutionFailureKind&)
TryInferGenericParameterCompatibility(Microsoft.CodeAnalysis.IMethodSymbol,Microsoft.CodeAnalysis.ITypeSymbol,Microsoft.CodeAnalysis.ITypeSymbol,System.String,System.Collections.Generic.List`1<Microsoft.CodeAnalysis.IParameterSymbol>,System.Collections.Immutable.ImmutableArray`1<Microsoft.CodeAnalysis.ITypeSymbol>,Microsoft.CodeAnalysis.Compilation,System.String&,NexusLabs.Needlr.Generators.Models.GuardResolutionFailureKind&)
GetMemberTypeIncompatibleReason(System.String)
GetMemberKindPlural(System.String)
ContainsTypeParameter(Microsoft.CodeAnalysis.ITypeSymbol,Microsoft.CodeAnalysis.ITypeParameterSymbol)
IsReachableOnlyThroughForwardedParameters(Microsoft.CodeAnalysis.ITypeParameterSymbol,Microsoft.CodeAnalysis.ITypeSymbol,System.Collections.Generic.List`1<Microsoft.CodeAnalysis.IParameterSymbol>)
FindConstraintViolation(System.Collections.Immutable.ImmutableArray`1<Microsoft.CodeAnalysis.ITypeParameterSymbol>,System.Collections.Generic.Dictionary`2<Microsoft.CodeAnalysis.ITypeSymbol,Microsoft.CodeAnalysis.ITypeSymbol>,Microsoft.CodeAnalysis.Compilation,Microsoft.CodeAnalysis.ITypeParameterSymbol&)
GetTypeAndSupertypes()
TryUnify(Microsoft.CodeAnalysis.ITypeSymbol,Microsoft.CodeAnalysis.ITypeSymbol,System.Collections.Generic.HashSet`1<Microsoft.CodeAnalysis.ITypeSymbol>,System.Collections.Generic.Dictionary`2<Microsoft.CodeAnalysis.ITypeSymbol,Microsoft.CodeAnalysis.ITypeSymbol>)
InheritsFromSystemAttribute(Microsoft.CodeAnalysis.INamedTypeSymbol)