| File: src\runtime\src\tools\illink\src\ILLink.Shared\Annotations.cs | Web Access |
| Project: src\runtime\src\tools\illink\src\ILLink.RoslynAnalyzer\ILLink.RoslynAnalyzer.csproj (ILLink.RoslynAnalyzer) |
// Copyright (c) .NET Foundation and contributors. All rights reserved. // Licensed under the MIT license. See LICENSE file in the project root for full license information. using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; using ILLink.Shared.TrimAnalysis; // This is needed due to NativeAOT which doesn't enable nullable globally yet #nullable enable namespace ILLink.Shared { internal static class Annotations { public static bool SourceHasRequiredAnnotations( DynamicallyAccessedMemberTypes sourceMemberTypes, DynamicallyAccessedMemberTypes targetMemberTypes, out string missingMemberTypesString) { missingMemberTypesString = string.Empty; var missingMemberTypes = GetMissingMemberTypes(targetMemberTypes, sourceMemberTypes); if (missingMemberTypes == DynamicallyAccessedMemberTypes.None) return true; missingMemberTypesString = GetMemberTypesString(missingMemberTypes); return false; } public static DynamicallyAccessedMemberTypes GetMissingMemberTypes(DynamicallyAccessedMemberTypes requiredMemberTypes, DynamicallyAccessedMemberTypes availableMemberTypes) { if (availableMemberTypes.HasFlag(requiredMemberTypes)) return DynamicallyAccessedMemberTypes.None; if (requiredMemberTypes == DynamicallyAccessedMemberTypes.All) return DynamicallyAccessedMemberTypes.All; var missingMemberTypes = requiredMemberTypes & ~availableMemberTypes; // PublicConstructors is a special case since its value is 3 - so PublicParameterlessConstructor (1) | _PublicConstructor_WithMoreThanOneParameter_ (2) // The above bit logic only works for value with single bit set. if (requiredMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicConstructors) && !availableMemberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicConstructors)) missingMemberTypes |= DynamicallyAccessedMemberTypes.PublicConstructors; return missingMemberTypes; } public static string GetMemberTypesString(DynamicallyAccessedMemberTypes memberTypes) { Debug.Assert(memberTypes != DynamicallyAccessedMemberTypes.None); if (memberTypes == DynamicallyAccessedMemberTypes.All) return $"'{nameof(DynamicallyAccessedMemberTypes)}.{nameof(DynamicallyAccessedMemberTypes.All)}'"; var memberTypesList = AllDynamicallyAccessedMemberTypes .Where(damt => (memberTypes & damt) == damt && damt != DynamicallyAccessedMemberTypes.None) .ToList(); if (memberTypes.HasFlag(DynamicallyAccessedMemberTypes.PublicConstructors)) memberTypesList.Remove(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor); return string.Join(", ", memberTypesList.Select(mt => { string mtName = mt == DynamicallyAccessedMemberTypes.Interfaces ? nameof(DynamicallyAccessedMemberTypes.Interfaces) : mt.ToString(); return $"'{nameof(DynamicallyAccessedMemberTypes)}.{mtName}'"; })); } private static readonly DynamicallyAccessedMemberTypes[] AllDynamicallyAccessedMemberTypes = GetAllDynamicallyAccessedMemberTypes(); private static DynamicallyAccessedMemberTypes[] GetAllDynamicallyAccessedMemberTypes() { var values = new HashSet<DynamicallyAccessedMemberTypes>( Enum.GetValues(typeof(DynamicallyAccessedMemberTypes)) .Cast<DynamicallyAccessedMemberTypes>()); values.Add(DynamicallyAccessedMemberTypes.Interfaces); return values.ToArray(); } public static (DiagnosticId Id, string[] Arguments) GetDiagnosticForAnnotationMismatch(ValueWithDynamicallyAccessedMembers source, ValueWithDynamicallyAccessedMembers target, string missingAnnotations) { source = source switch { // FieldValue and MethodReturnValue have only one diagnostic argument, so formatting throws when the source // is a NullableValueWithDynamicallyAccessedMembers. // The correct behavior here is to unwrap always, as the underlying type is the one that has the annotations, // but it is a breaking change for other UnderlyingTypeValues. // https://github.com/dotnet/runtime/issues/93800 NullableValueWithDynamicallyAccessedMembers { UnderlyingTypeValue: FieldValue or MethodReturnValue } nullable => nullable.UnderlyingTypeValue, NullableUnwrappedGenericParameterValue unwrappedGenericParam => unwrappedGenericParam.GenericParameter, _ => source }; DiagnosticId diagnosticId = (source, target) switch { (MethodParameterValue maybeThisSource, MethodParameterValue maybeThisTarget) when maybeThisSource.IsThisParameter() && maybeThisTarget.IsThisParameter() => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsThisParameter, (MethodParameterValue maybeThis, MethodParameterValue) when maybeThis.IsThisParameter() => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsParameter, (MethodParameterValue maybeThis, MethodReturnValue) when maybeThis.IsThisParameter() => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsMethodReturnType, (MethodParameterValue maybeThis, FieldValue) when maybeThis.IsThisParameter() => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsField, (MethodParameterValue maybeThis, GenericParameterValue) when maybeThis.IsThisParameter() => DiagnosticId.DynamicallyAccessedMembersMismatchThisParameterTargetsGenericParameter, (MethodParameterValue, MethodParameterValue maybeThis) when maybeThis.IsThisParameter() => DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsThisParameter, (MethodParameterValue, MethodParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsParameter, (MethodParameterValue, MethodReturnValue) => DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsMethodReturnType, (MethodParameterValue, FieldValue) => DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsField, (MethodParameterValue, GenericParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchParameterTargetsGenericParameter, (MethodReturnValue, MethodParameterValue maybeThis) when maybeThis.IsThisParameter() => DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsThisParameter, (MethodReturnValue, MethodParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsParameter, (MethodReturnValue, MethodReturnValue) => DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsMethodReturnType, (MethodReturnValue, FieldValue) => DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsField, (MethodReturnValue, GenericParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchMethodReturnTypeTargetsGenericParameter, (FieldValue, MethodParameterValue maybeThis) when maybeThis.IsThisParameter() => DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsThisParameter, (FieldValue, MethodParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsParameter, (FieldValue, MethodReturnValue) => DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsMethodReturnType, (FieldValue, FieldValue) => DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsField, (FieldValue, GenericParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchFieldTargetsGenericParameter, (GenericParameterValue, MethodParameterValue maybeThis) when maybeThis.IsThisParameter() => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsThisParameter, (GenericParameterValue, MethodParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsParameter, (GenericParameterValue, MethodReturnValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsMethodReturnType, (GenericParameterValue, FieldValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsField, (GenericParameterValue, GenericParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsGenericParameter, (NullableValueWithDynamicallyAccessedMembers, MethodParameterValue maybeThis) when maybeThis.IsThisParameter() => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsThisParameter, (NullableValueWithDynamicallyAccessedMembers, MethodParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsParameter, (NullableValueWithDynamicallyAccessedMembers, MethodReturnValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsMethodReturnType, (NullableValueWithDynamicallyAccessedMembers, FieldValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsField, (NullableValueWithDynamicallyAccessedMembers, GenericParameterValue) => DiagnosticId.DynamicallyAccessedMembersMismatchTypeArgumentTargetsGenericParameter, _ => throw new NotImplementedException($"Unsupported source context {source} or target context {target}.") }; var args = new List<string>(); args.AddRange(target.GetDiagnosticArgumentsForAnnotationMismatch()); args.AddRange(source.GetDiagnosticArgumentsForAnnotationMismatch()); args.Add(missingAnnotations); return (diagnosticId, args.ToArray()); } } }