// 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.Diagnostics.CodeAnalysis; using System.Linq; using System.Runtime.InteropServices; using ILLink.Shared.TypeSystemProxy; using MultiValue = ILLink.Shared.DataFlow.ValueSet<ILLink.Shared.DataFlow.SingleValue>; // This is needed due to NativeAOT which doesn't enable nullable globally yet #nullable enable namespace ILLink.Shared.TrimAnalysis { [StructLayout(LayoutKind.Auto)] internal partial struct RequireDynamicallyAccessedMembersAction { private readonly DiagnosticContext _diagnosticContext; public void Invoke(in MultiValue value, ValueWithDynamicallyAccessedMembers targetValue) { if (targetValue.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.None) return; foreach (var uniqueValue in value.AsEnumerable()) { if (targetValue.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.PublicParameterlessConstructor && uniqueValue is GenericParameterValue genericParam && genericParam.GenericParameter.HasDefaultConstructorConstraint()) { // We allow a new() constraint on a generic parameter to satisfy DynamicallyAccessedMemberTypes.PublicParameterlessConstructor } else if (targetValue.DynamicallyAccessedMemberTypes == DynamicallyAccessedMemberTypes.PublicFields && uniqueValue is GenericParameterValue maybeEnumConstrainedGenericParam && maybeEnumConstrainedGenericParam.GenericParameter.HasEnumConstraint()) { // We allow a System.Enum constraint on a generic parameter to satisfy DynamicallyAccessedMemberTypes.PublicFields } else if (uniqueValue is ValueWithDynamicallyAccessedMembers valueWithDynamicallyAccessedMembers) { if (uniqueValue is NullableValueWithDynamicallyAccessedMembers nullableValue) { MarkTypeForDynamicallyAccessedMembers(nullableValue.NullableType, nullableValue.DynamicallyAccessedMemberTypes); } var availableMemberTypes = valueWithDynamicallyAccessedMembers.DynamicallyAccessedMemberTypes; if (!Annotations.SourceHasRequiredAnnotations(availableMemberTypes, targetValue.DynamicallyAccessedMemberTypes, out var missingMemberTypes)) { (var diagnosticId, var diagnosticArguments) = Annotations.GetDiagnosticForAnnotationMismatch(valueWithDynamicallyAccessedMembers, targetValue, missingMemberTypes); _diagnosticContext.AddDiagnostic(diagnosticId, valueWithDynamicallyAccessedMembers, targetValue, diagnosticArguments); } } else if (uniqueValue is SystemTypeValue systemTypeValue) { MarkTypeForDynamicallyAccessedMembers(systemTypeValue.RepresentedType, targetValue.DynamicallyAccessedMemberTypes); } else if (uniqueValue is KnownStringValue knownStringValue) { if (!TryResolveTypeNameAndMark(knownStringValue.Contents, needsAssemblyName: true, out TypeProxy foundType)) { // Intentionally ignore - it's not wrong for code to call Type.GetType on non-existing name, the code might expect null/exception back. } else { MarkTypeForDynamicallyAccessedMembers(foundType, targetValue.DynamicallyAccessedMemberTypes); } } else if (uniqueValue is NullableSystemTypeValue nullableSystemTypeValue) { MarkTypeForDynamicallyAccessedMembers(nullableSystemTypeValue.NullableType, targetValue.DynamicallyAccessedMemberTypes); MarkTypeForDynamicallyAccessedMembers(nullableSystemTypeValue.UnderlyingTypeValue.RepresentedType, targetValue.DynamicallyAccessedMemberTypes); } else if (uniqueValue == NullValue.Instance) { // Ignore - probably unreachable path as it would fail at runtime anyway. } else { DiagnosticId diagnosticId = targetValue switch { MethodParameterValue maybeThis when maybeThis.IsThisParameter() => DiagnosticId.ImplicitThisCannotBeStaticallyDetermined, MethodParameterValue => DiagnosticId.MethodParameterCannotBeStaticallyDetermined, MethodReturnValue => DiagnosticId.MethodReturnValueCannotBeStaticallyDetermined, FieldValue => DiagnosticId.FieldValueCannotBeStaticallyDetermined, GenericParameterValue => DiagnosticId.TypePassedToGenericParameterCannotBeStaticallyDetermined, _ => throw new NotImplementedException($"unsupported target value {targetValue}") }; _diagnosticContext.AddDiagnostic(diagnosticId, targetValue.GetDiagnosticArgumentsForAnnotationMismatch().ToArray()); } } } public partial bool TryResolveTypeNameAndMark(string typeName, bool needsAssemblyName, out TypeProxy type); private partial void MarkTypeForDynamicallyAccessedMembers(in TypeProxy type, DynamicallyAccessedMemberTypes dynamicallyAccessedMemberTypes); } } |