File: parent\Shared\TaskParameterTypeVerifier.cs
Web Access
Project: Microsoft.Build.Tasks.csproj (Microsoft.Build.Tasks.Core)
// 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.Diagnostics.CodeAnalysis;
using Microsoft.Build.Framework;
using Microsoft.Build.Shared;

namespace Microsoft.Build.BackEnd
{
    /// <summary>
    /// Provide a class which can verify the correct type for both input and output parameters.
    /// </summary>
    internal static class TaskParameterTypeVerifier
    {
        internal static bool TryGetSupportedTaskItemValueType(Type parameterType, [NotNullWhen(true)] out Type? valueType)
        {
            if (TaskItemTypeDetector.TryGetTaskItemValueType(parameterType, out valueType))
            {
                return ValueTypeParser.IsSupportedType(valueType);
            }

            return false;
        }

        private static bool IsValidValueParameterType(Type parameterType)
            => parameterType.IsValueType || ValueTypeParser.IsSupportedType(parameterType);

        private static bool IsValidInputElementType(Type parameterType)
        {
            if (TaskItemTypeDetector.TryGetTaskItemValueType(parameterType, out Type? valueType))
            {
                return ValueTypeParser.IsSupportedType(valueType);
            }

            if (typeof(ITaskItem).IsAssignableFrom(parameterType))
            {
                return parameterType == typeof(ITaskItem);
            }

            return IsValidValueParameterType(parameterType);
        }

        /// <summary>
        /// Is the parameter type a valid scalar input value - meaning not an array and a valid input element type
        /// </summary>
        internal static bool IsValidScalarInputParameter(Type parameterType) =>
            !TryGetArrayElement(parameterType, out _) && IsValidInputElementType(parameterType);

        /// <summary>
        /// Is the passed in parameterType a valid vector input parameter - meaning is an array and the element type is a valid input element type
        /// </summary>
        internal static bool IsValidVectorInputParameter(Type parameterType) =>
            TryGetArrayElement(parameterType, out Type? elementType) && IsValidInputElementType(elementType);

        /// <summary>
        /// Helper that returns the element type of an array parameter type, or null if the parameter type is not an array - useful to handle nullability flow analysis for array element types in the calling code.
        /// </summary>
        private static bool TryGetArrayElement(Type parameterType, [NotNullWhen(true)] out Type? elementType)
        {
            if (parameterType.IsArray)
            {
                elementType = parameterType.GetElementType()!; // GetElementType is non-null because parameterType is an array
                return true;
            }

            elementType = null;
            return false;
        }

        /// <summary>
        /// Is the passed in value type assignable to an ITaskItem or ITaskItem[] object
        /// </summary>
        internal static bool IsAssignableToITaskItem(Type parameterType)
        {
            Type elementType = TryGetArrayElement(parameterType, out Type? arrayElementType) ? arrayElementType : parameterType;
            if (TaskItemTypeDetector.TryGetTaskItemValueType(elementType, out Type? valueType))
            {
                return ValueTypeParser.IsSupportedType(valueType);
            }

            return typeof(ITaskItem).IsAssignableFrom(elementType);
        }

        /// <summary>
        /// Is the passed parameter a valid value type output parameter
        /// </summary>
        internal static bool IsValueTypeOutputParameter(Type parameterType)
        {
            Type elementType = TryGetArrayElement(parameterType, out Type? arrayElementType) ? arrayElementType : parameterType;
            return !typeof(ITaskItem).IsAssignableFrom(elementType)
                && IsValidValueParameterType(elementType);
        }

        /// <summary>
        /// Is the parameter type a valid scalar or value type input parameter
        /// </summary>
        internal static bool IsValidInputParameter(Type parameterType)
        {
            return IsValidScalarInputParameter(parameterType) || IsValidVectorInputParameter(parameterType);
        }

        /// <summary>
        /// Is the parameter type a valid scalar or value type output parameter
        /// </summary>
        internal static bool IsValidOutputParameter(Type parameterType)
        {
            return IsValueTypeOutputParameter(parameterType) || IsAssignableToITaskItem(parameterType);
        }
    }
}