예제 #1
0
        static Attribute ProcessRequiresUnreferencedCodeAttribute(LinkContext context, ICustomAttributeProvider provider, CustomAttribute customAttribute)
        {
            if (!(provider is MethodDefinition method))
            {
                return(null);
            }

            if (customAttribute.HasConstructorArguments && customAttribute.ConstructorArguments[0].Value is string message)
            {
                var ruca = new RequiresUnreferencedCodeAttribute(message);
                if (customAttribute.HasProperties)
                {
                    foreach (var prop in customAttribute.Properties)
                    {
                        if (prop.Name == "Url")
                        {
                            ruca.Url = prop.Argument.Value as string;
                            break;
                        }
                    }
                }

                return(ruca);
            }

            context.LogWarning(
                $"Attribute '{typeof (RequiresUnreferencedCodeAttribute).FullName}' doesn't have the required number of parameters specified",
                2028, method);
            return(null);
        }
예제 #2
0
        public static DynamicDependency?ProcessAttribute(LinkContext context, ICustomAttributeProvider provider, CustomAttribute customAttribute)
        {
            if (!(provider is IMemberDefinition member))
            {
                return(null);
            }

            if (!(member is MethodDefinition || member is FieldDefinition))
            {
                return(null);
            }

            // Don't honor the Condition until we have figured out the behavior for DynamicDependencyAttribute:
            // https://github.com/dotnet/linker/issues/1231
            // if (!ShouldProcess (context, customAttribute))
            //  return null;

            var dynamicDependency = GetDynamicDependency(customAttribute);

            if (dynamicDependency != null)
            {
                return(dynamicDependency);
            }

            context.LogWarning($"The 'DynamicDependencyAttribute' could not be analyzed.", 2034, member);
            return(null);
        }
예제 #3
0
        static Attribute ProcessRequiresUnreferencedCodeAttribute(LinkContext context, ICustomAttributeProvider provider, CustomAttribute customAttribute)
        {
            if (!(provider is MethodDefinition method))
            {
                return(null);
            }

            if (customAttribute.HasConstructorArguments)
            {
                string message = (string)customAttribute.ConstructorArguments[0].Value;
                string url     = null;
                foreach (var prop in customAttribute.Properties)
                {
                    if (prop.Name == "Url")
                    {
                        url = (string)prop.Argument.Value;
                    }
                }

                return(new RequiresUnreferencedCodeAttribute(message)
                {
                    Url = url
                });
            }

            context.LogWarning($"Attribute '{typeof (RequiresUnreferencedCodeAttribute).FullName}' on '{method.GetDisplayName ()}' doesn't have a required constructor argument.", 2028, method);
            return(null);
        }
        IEnumerable <(SuppressMessageInfo Info, ICustomAttributeProvider Target)> DecodeGlobalSuppressions(ModuleDefinition module, ICustomAttributeProvider provider)
        {
            var attributes = _context.CustomAttributes.GetCustomAttributes(provider).
                             Where(a => TypeRefHasUnconditionalSuppressions(a.AttributeType));

            foreach (var instance in attributes)
            {
                SuppressMessageInfo info;
                if (!TryDecodeSuppressMessageAttributeData(instance, out info))
                {
                    continue;
                }

                var scope = info.Scope?.ToLower();
                if (info.Target == null && (scope == "module" || scope == null))
                {
                    yield return(info, provider);

                    continue;
                }

                switch (scope)
                {
                case "module":
                    yield return(info, provider);

                    break;

                case "type":
                case "member":
                    if (info.Target == null)
                    {
                        break;
                    }

                    foreach (var result in DocumentationSignatureParser.GetMembersForDocumentationSignature(info.Target, module, _context))
                    {
                        yield return(info, result);
                    }

                    break;

                default:
                    _context.LogWarning($"Invalid scope '{info.Scope}' used in 'UnconditionalSuppressMessageAttribute' on module '{module.Name}' " +
                                        $"with target '{info.Target}'.",
                                        2108, _context.GetAssemblyLocation(module.Assembly));
                    break;
                }
            }
        }
예제 #5
0
        void PopulateCacheForType(TypeDefinition type)
        {
            // Avoid repeat scans of the same type
            if (!_typesWithPopulatedCache.Add(type))
            {
                return;
            }

            foreach (MethodDefinition method in type.Methods)
            {
                if (!method.HasCustomAttributes)
                {
                    continue;
                }

                foreach (var attribute in method.CustomAttributes)
                {
                    if (attribute.AttributeType.Namespace != "System.Runtime.CompilerServices")
                    {
                        continue;
                    }

                    switch (attribute.AttributeType.Name)
                    {
                    case "AsyncIteratorStateMachineAttribute":
                    case "AsyncStateMachineAttribute":
                    case "IteratorStateMachineAttribute":
                        TypeDefinition stateMachineType = GetFirstConstructorArgumentAsType(attribute);
                        if (stateMachineType != null)
                        {
                            if (!_compilerGeneratedTypeToUserCodeMethod.TryAdd(stateMachineType, method))
                            {
                                var alreadyAssociatedMethod = _compilerGeneratedTypeToUserCodeMethod[stateMachineType];
                                _context.LogWarning(
                                    $"Methods '{method.GetDisplayName ()}' and '{alreadyAssociatedMethod.GetDisplayName ()}' are both associated with state machine type '{stateMachineType.GetDisplayName ()}'. This is currently unsupported and may lead to incorrectly reported warnings.",
                                    2107,
                                    new MessageOrigin(method),
                                    MessageSubCategory.TrimAnalysis);
                            }
                        }

                        break;
                    }
                }
            }
        }
예제 #6
0
        static RemoveAttributeInstancesAttribute BuildRemoveAttributeInstancesAttribute(LinkContext context, TypeDefinition attributeContext, CustomAttribute ca)
        {
            switch (ca.ConstructorArguments.Count)
            {
            case 0:
                return(new RemoveAttributeInstancesAttribute());

            case 1:
                // Argument is always boxed
                return(new RemoveAttributeInstancesAttribute((CustomAttributeArgument)ca.ConstructorArguments[0].Value));

            default:
                context.LogWarning(
                    $"Attribute '{ca.AttributeType.GetDisplayName ()}' doesn't have the required number of arguments specified",
                    2028, attributeContext);
                return(null);
            }
            ;
        }
예제 #7
0
        static bool TryLogSingleWarning(LinkContext context, int code, MessageOrigin origin, string subcategory)
        {
            if (subcategory != MessageSubCategory.TrimAnalysis)
            {
                return(false);
            }

            // There are valid cases where we can't map the message to an assembly
            // For example if it's caused by something in an xml file passed on the command line
            // In that case, give up on single-warn collapse and just print out the warning on its own.
            var assembly = origin.Provider switch {
                AssemblyDefinition asm => asm,
                TypeDefinition type => type.Module.Assembly,
                IMemberDefinition member => member.DeclaringType.Module.Assembly,
                _ => null
            };

            if (assembly == null)
            {
                return(false);
            }

            // Any IL2026 warnings left in an assembly with an IsTrimmable attribute are considered intentional
            // and should not be collapsed, so that the user-visible RUC message gets printed.
            if (code == 2026 && context.IsTrimmable(assembly))
            {
                return(false);
            }

            var assemblyName = assembly.Name.Name;

            if (!context.IsSingleWarn(assemblyName))
            {
                return(false);
            }

            if (context.AssembliesWithGeneratedSingleWarning.Add(assemblyName))
            {
                context.LogWarning(context.GetAssemblyLocation(assembly), DiagnosticId.AssemblyProducedTrimWarnings, assemblyName);
            }

            return(true);
        }
예제 #8
0
        static bool TryLogSingleWarning(LinkContext context, int code, MessageOrigin origin, string subcategory)
        {
            if (subcategory != MessageSubCategory.TrimAnalysis)
            {
                return(false);
            }

            Debug.Assert(origin.Provider != null);
            var assembly = origin.Provider switch {
                AssemblyDefinition asm => asm,
                TypeDefinition type => type.Module.Assembly,
                IMemberDefinition member => member.DeclaringType.Module.Assembly,
                _ => throw new NotSupportedException()
            };

            Debug.Assert(assembly != null);
            if (assembly == null)
            {
                return(false);
            }

            // Any IL2026 warnings left in an assembly with an IsTrimmable attribute are considered intentional
            // and should not be collapsed, so that the user-visible RUC message gets printed.
            if (code == 2026 && context.IsTrimmable(assembly))
            {
                return(false);
            }

            var assemblyName = assembly.Name.Name;

            if (!context.IsSingleWarn(assemblyName))
            {
                return(false);
            }

            if (context.AssembliesWithGeneratedSingleWarning.Add(assemblyName))
            {
                context.LogWarning($"Assembly '{assemblyName}' produced trim warnings. For more information see https://aka.ms/dotnet-illink/libraries", 2104, context.GetAssemblyLocation(assembly));
            }

            return(true);
        }
예제 #9
0
        void PopulateCacheForType(TypeDefinition type)
        {
            // Avoid repeat scans of the same type
            if (!_typesWithPopulatedCache.Add(type))
            {
                return;
            }

            foreach (MethodDefinition method in type.Methods)
            {
                if (!method.HasCustomAttributes)
                {
                    continue;
                }

                foreach (var attribute in method.CustomAttributes)
                {
                    if (attribute.AttributeType.Namespace != "System.Runtime.CompilerServices")
                    {
                        continue;
                    }

                    switch (attribute.AttributeType.Name)
                    {
                    case "AsyncIteratorStateMachineAttribute":
                    case "AsyncStateMachineAttribute":
                    case "IteratorStateMachineAttribute":
                        TypeDefinition?stateMachineType = GetFirstConstructorArgumentAsType(attribute);
                        if (stateMachineType != null)
                        {
                            if (!_compilerGeneratedTypeToUserCodeMethod.TryAdd(stateMachineType, method))
                            {
                                var alreadyAssociatedMethod = _compilerGeneratedTypeToUserCodeMethod[stateMachineType];
                                _context.LogWarning(new MessageOrigin(method), DiagnosticId.MethodsAreAssociatedWithStateMachine, method.GetDisplayName(), alreadyAssociatedMethod.GetDisplayName(), stateMachineType.GetDisplayName());
                            }
                        }

                        break;
                    }
                }
            }
        }
예제 #10
0
        private static MessageContainer CreateWarningMessageContainer(LinkContext context, string text, int code, MessageOrigin origin, WarnVersion version, string subcategory = MessageSubCategory.None)
        {
            if (!(version >= WarnVersion.ILLink0 && version <= WarnVersion.Latest))
            {
                throw new ArgumentException($"The provided warning version '{version}' is invalid.");
            }

            if (context.IsWarningSuppressed(code, origin))
            {
                return(Empty);
            }

            if (version > context.WarnVersion)
            {
                return(Empty);
            }

            if (subcategory == MessageSubCategory.TrimAnalysis)
            {
                Debug.Assert(origin.MemberDefinition != null);
                var declaringType = origin.MemberDefinition?.DeclaringType ?? (origin.MemberDefinition as TypeDefinition);
                var assembly      = declaringType.Module.Assembly;
                var assemblyName  = assembly?.Name.Name;
                if (assemblyName != null && context.IsSingleWarn(assemblyName))
                {
                    if (context.AssembliesWithGeneratedSingleWarning.Add(assemblyName))
                    {
                        context.LogWarning($"Assembly '{assemblyName}' produced trim warnings. For more information see https://aka.ms/dotnet-illink/libraries", 2104, context.GetAssemblyLocation(assembly));
                    }
                    return(Empty);
                }
            }

            if (context.IsWarningAsError(code))
            {
                return(new MessageContainer(MessageCategory.WarningAsError, text, code, subcategory, origin));
            }

            return(new MessageContainer(MessageCategory.Warning, text, code, subcategory, origin));
        }
예제 #11
0
        public bool TryResolveTypeName(string typeNameString, ICustomAttributeProvider?origin, [NotNullWhen(true)] out TypeReference?typeReference, [NotNullWhen(true)] out AssemblyDefinition?typeAssembly, bool needsAssemblyName = true)
        {
            typeReference = null;
            typeAssembly  = null;
            if (string.IsNullOrEmpty(typeNameString))
            {
                return(false);
            }

            TypeName parsedTypeName;

            try {
                parsedTypeName = TypeParser.ParseTypeName(typeNameString);
            } catch (ArgumentException) {
                return(false);
            } catch (System.IO.FileLoadException) {
                return(false);
            }

            if (parsedTypeName is AssemblyQualifiedTypeName assemblyQualifiedTypeName)
            {
                typeAssembly = _context.TryResolve(assemblyQualifiedTypeName.AssemblyName.Name);
                if (typeAssembly == null)
                {
                    return(false);
                }

                typeReference = ResolveTypeName(typeAssembly, assemblyQualifiedTypeName.TypeName);
                return(typeReference != null);
            }

            // If parsedTypeName doesn't have an assembly name in it but it does have a namespace,
            // search for the type in the calling object's assembly. If not found, look in the core
            // assembly.
            typeAssembly = origin switch {
                AssemblyDefinition asm => asm,
                TypeDefinition type => type.Module?.Assembly,
                IMemberDefinition member => member.DeclaringType.Module.Assembly,
                                   null => null,
                                   _ => throw new NotSupportedException()
            };

            if (typeAssembly != null && TryResolveTypeName(typeAssembly, parsedTypeName, out typeReference))
            {
                return(true);
            }

            // If type is not found in the caller's assembly, try in core assembly.
            typeAssembly = _context.TryResolve(PlatformAssemblies.CoreLib);
            if (typeAssembly != null && TryResolveTypeName(typeAssembly, parsedTypeName, out typeReference))
            {
                return(true);
            }

            // It is common to use Type.GetType for looking if a type is available.
            // If no type was found only warn and return null.
            if (needsAssemblyName && origin != null)
            {
                _context.LogWarning(new MessageOrigin(origin), DiagnosticId.TypeWasNotFoundInAssemblyNorBaseLibrary, typeNameString);
            }

            typeAssembly = null;
            return(false);

            bool TryResolveTypeName(AssemblyDefinition assemblyDefinition, TypeName typeName, [NotNullWhen(true)] out TypeReference?typeReference)
            {
                typeReference = null;
                if (assemblyDefinition == null)
                {
                    return(false);
                }

                typeReference = ResolveTypeName(assemblyDefinition, typeName);
                return(typeReference != null);
            }
        }
예제 #12
0
        public void UnrecognizedReflectionAccessPattern(IMemberDefinition source, Instruction sourceInstruction, IMetadataTokenProvider accessedItem, string message, int messageCode)
        {
            var origin = new MessageOrigin(source, sourceInstruction?.Offset);

            _context.LogWarning(message, messageCode, origin, MessageSubCategory.TrimAnalysis);
        }
예제 #13
0
        public TypeReference ResolveTypeName(string typeNameString, ICustomAttributeProvider origin, out AssemblyDefinition typeAssembly, bool needsAssemblyName = true)
        {
            typeAssembly = null;
            if (string.IsNullOrEmpty(typeNameString))
            {
                return(null);
            }

            TypeName parsedTypeName;

            try {
                parsedTypeName = TypeParser.ParseTypeName(typeNameString);
            } catch (ArgumentException) {
                return(null);
            } catch (System.IO.FileLoadException) {
                return(null);
            }

            if (parsedTypeName is AssemblyQualifiedTypeName assemblyQualifiedTypeName)
            {
                typeAssembly = _context.TryResolve(assemblyQualifiedTypeName.AssemblyName.Name);
                if (typeAssembly == null)
                {
                    return(null);
                }

                return(ResolveTypeName(typeAssembly, assemblyQualifiedTypeName.TypeName));
            }

            // If parsedTypeName doesn't have an assembly name in it but it does have a namespace,
            // search for the type in the calling object's assembly. If not found, look in the core
            // assembly.
            typeAssembly = origin switch {
                AssemblyDefinition asm => asm,
                TypeDefinition type => type.Module?.Assembly,
                IMemberDefinition member => member.DeclaringType.Module.Assembly,
                                   null => null,
                                   _ => throw new NotSupportedException()
            };

            if (typeAssembly != null && TryResolveTypeName(typeAssembly, parsedTypeName, out var typeRef))
            {
                return(typeRef);
            }

            // If type is not found in the caller's assembly, try in core assembly.
            typeAssembly = _context.TryResolve(PlatformAssemblies.CoreLib);
            if (typeAssembly != null && TryResolveTypeName(typeAssembly, parsedTypeName, out var typeRefFromSPCL))
            {
                return(typeRefFromSPCL);
            }

            // It is common to use Type.GetType for looking if a type is available.
            // If no type was found only warn and return null.
            if (needsAssemblyName && origin != null)
            {
                _context.LogWarning($"Type '{typeNameString}' was not found in the caller assembly nor in the base library. " +
                                    $"Type name strings used for dynamically accessing a type should be assembly qualified.",
                                    2105, new MessageOrigin(origin));
            }

            typeAssembly = null;
            return(null);

            bool TryResolveTypeName(AssemblyDefinition assemblyDefinition, TypeName typeName, out TypeReference typeReference)
            {
                typeReference = null;
                if (assemblyDefinition == null)
                {
                    return(false);
                }

                typeReference = ResolveTypeName(assemblyDefinition, typeName);
                return(typeReference != null);
            }
        }
예제 #14
0
        void PopulateCacheForType(TypeDefinition type)
        {
            // Avoid repeat scans of the same type
            if (!_typesWithPopulatedCache.Add(type))
            {
                return;
            }

            var callGraph      = new CompilerGeneratedCallGraph();
            var callingMethods = new HashSet <MethodDefinition> ();

            void ProcessMethod(MethodDefinition method)
            {
                bool isStateMachineMember = CompilerGeneratedNames.IsStateMachineType(method.DeclaringType.Name);

                if (!CompilerGeneratedNames.IsLambdaOrLocalFunction(method.Name))
                {
                    if (!isStateMachineMember)
                    {
                        // If it's not a nested function, track as an entry point to the call graph.
                        var added = callingMethods.Add(method);
                        Debug.Assert(added);
                    }
                }
                else
                {
                    // We don't expect lambdas or local functions to be emitted directly into
                    // state machine types.
                    Debug.Assert(!isStateMachineMember);
                }

                // Discover calls or references to lambdas or local functions. This includes
                // calls to local functions, and lambda assignments (which use ldftn).
                if (method.Body != null)
                {
                    foreach (var instruction in method.Body.Instructions)
                    {
                        if (instruction.OpCode.OperandType != OperandType.InlineMethod)
                        {
                            continue;
                        }

                        MethodDefinition?lambdaOrLocalFunction = _context.TryResolve((MethodReference)instruction.Operand);
                        if (lambdaOrLocalFunction == null)
                        {
                            continue;
                        }

                        if (!CompilerGeneratedNames.IsLambdaOrLocalFunction(lambdaOrLocalFunction.Name))
                        {
                            continue;
                        }

                        if (isStateMachineMember)
                        {
                            callGraph.TrackCall(method.DeclaringType, lambdaOrLocalFunction);
                        }
                        else
                        {
                            callGraph.TrackCall(method, lambdaOrLocalFunction);
                        }
                    }
                }

                // Discover state machine methods.
                if (!method.HasCustomAttributes)
                {
                    return;
                }

                foreach (var attribute in method.CustomAttributes)
                {
                    if (attribute.AttributeType.Namespace != "System.Runtime.CompilerServices")
                    {
                        continue;
                    }

                    switch (attribute.AttributeType.Name)
                    {
                    case "AsyncIteratorStateMachineAttribute":
                    case "AsyncStateMachineAttribute":
                    case "IteratorStateMachineAttribute":
                        TypeDefinition?stateMachineType = GetFirstConstructorArgumentAsType(attribute);
                        if (stateMachineType == null)
                        {
                            break;
                        }
                        Debug.Assert(stateMachineType.DeclaringType == type ||
                                     (CompilerGeneratedNames.IsGeneratedMemberName(stateMachineType.DeclaringType.Name) &&
                                      stateMachineType.DeclaringType.DeclaringType == type));
                        callGraph.TrackCall(method, stateMachineType);
                        if (!_compilerGeneratedTypeToUserCodeMethod.TryAdd(stateMachineType, method))
                        {
                            var alreadyAssociatedMethod = _compilerGeneratedTypeToUserCodeMethod[stateMachineType];
                            _context.LogWarning(new MessageOrigin(method), DiagnosticId.MethodsAreAssociatedWithStateMachine, method.GetDisplayName(), alreadyAssociatedMethod.GetDisplayName(), stateMachineType.GetDisplayName());
                        }

                        break;
                    }
                }
            }

            // Look for state machine methods, and methods which call local functions.
            foreach (MethodDefinition method in type.Methods)
            {
                ProcessMethod(method);
            }

            // Also scan compiler-generated state machine methods (in case they have calls to nested functions),
            // and nested functions inside compiler-generated closures (in case they call other nested functions).

            // State machines can be emitted into lambda display classes, so we need to go down at least two
            // levels to find calls from iterator nested functions to other nested functions. We just recurse into
            // all compiler-generated nested types to avoid depending on implementation details.

            foreach (var nestedType in GetCompilerGeneratedNestedTypes(type))
            {
                foreach (var method in nestedType.Methods)
                {
                    ProcessMethod(method);
                }
            }

            // Now we've discovered the call graphs for calls to nested functions.
            // Use this to map back from nested functions to the declaring user methods.

            // Note: This maps all nested functions back to the user code, not to the immediately
            // declaring local function. The IL doesn't contain enough information in general for
            // us to determine the nesting of local functions and lambdas.

            // Note: this only discovers nested functions which are referenced from the user
            // code or its referenced nested functions. There is no reliable way to determine from
            // IL which user code an unused nested function belongs to.
            foreach (var userDefinedMethod in callingMethods)
            {
                foreach (var compilerGeneratedMember in callGraph.GetReachableMembers(userDefinedMethod))
                {
                    switch (compilerGeneratedMember)
                    {
                    case MethodDefinition nestedFunction:
                        Debug.Assert(CompilerGeneratedNames.IsLambdaOrLocalFunction(nestedFunction.Name));
                        // Nested functions get suppressions from the user method only.
                        if (!_compilerGeneratedMethodToUserCodeMethod.TryAdd(nestedFunction, userDefinedMethod))
                        {
                            var alreadyAssociatedMethod = _compilerGeneratedMethodToUserCodeMethod[nestedFunction];
                            _context.LogWarning(new MessageOrigin(userDefinedMethod), DiagnosticId.MethodsAreAssociatedWithUserMethod, userDefinedMethod.GetDisplayName(), alreadyAssociatedMethod.GetDisplayName(), nestedFunction.GetDisplayName());
                        }
                        break;

                    case TypeDefinition stateMachineType:
                        // Types in the call graph are always state machine types
                        // For those all their methods are not tracked explicitly in the call graph; instead, they
                        // are represented by the state machine type itself.
                        // We are already tracking the association of the state machine type to the user code method
                        // above, so no need to track it here.
                        Debug.Assert(CompilerGeneratedNames.IsStateMachineType(stateMachineType.Name));
                        break;

                    default:
                        throw new InvalidOperationException();
                    }
                }
            }
        }
예제 #15
0
        IEnumerable <CustomAttribute> ProcessAttributes(XPathNavigator nav)
        {
            XPathNodeIterator iterator = nav.SelectChildren("attribute", string.Empty);
            var attributes             = new List <CustomAttribute> ();

            while (iterator.MoveNext())
            {
                AssemblyDefinition assembly;
                TypeDefinition     attributeType;

                string attributeFullName = GetFullName(iterator.Current);
                if (attributeFullName == String.Empty)
                {
                    _context.LogWarning($"Attribute element does not contain attribute 'fullname'", 2029, _xmlDocumentLocation);
                    continue;
                }
                string assemblyName = GetAttribute(iterator.Current, "assembly");
                if (assemblyName == String.Empty)
                {
                    attributeType = _context.GetType(attributeFullName);
                }
                else
                {
                    try {
                        assembly = GetAssembly(_context, AssemblyNameReference.Parse(assemblyName));
                    } catch (Exception) {
                        _context.LogWarning($"Could not resolve assembly '{assemblyName}' in attribute '{attributeFullName}' specified in the '{_xmlDocumentLocation}'", 2030, _xmlDocumentLocation);
                        continue;
                    }
                    attributeType = assembly.FindType(attributeFullName);
                }
                if (attributeType == null)
                {
                    _context.LogWarning($"Attribute type '{attributeFullName}' could not be found", 2031, _xmlDocumentLocation);
                    continue;
                }

                ArrayBuilder <string> arguments   = GetAttributeChildren(iterator.Current.SelectChildren("argument", string.Empty));
                MethodDefinition      constructor = attributeType.Methods.Where(method => method.IsInstanceConstructor()).FirstOrDefault(c => c.Parameters.Count == arguments.Count);
                if (constructor == null)
                {
                    _context.LogWarning($"Could not find a constructor for type '{attributeType}' that receives '{arguments.Count}' arguments as parameter", 2022, _xmlDocumentLocation);
                    continue;
                }
                string[] xmlArguments       = arguments.ToArray();
                bool     recognizedArgument = true;

                CustomAttribute attribute = new CustomAttribute(constructor);
                for (int i = 0; i < xmlArguments.Length; i++)
                {
                    object argumentValue = null;

                    if (constructor.Parameters[i].ParameterType.Resolve().IsEnum)
                    {
                        foreach (var field in constructor.Parameters[i].ParameterType.Resolve().Fields)
                        {
                            if (field.IsStatic && field.Name == xmlArguments[i])
                            {
                                argumentValue = Convert.ToInt32(field.Constant);
                                break;
                            }
                        }
                        if (argumentValue == null)
                        {
                            _context.LogWarning($"Could not parse argument '{xmlArguments[i]}' specified in '{_xmlDocumentLocation}' as a {constructor.Parameters[i].ParameterType.FullName}", 2021, _xmlDocumentLocation);
                            recognizedArgument = false;
                        }
                    }
                    else
                    {
                        switch (constructor.Parameters[i].ParameterType.MetadataType)
                        {
                        case MetadataType.String:
                            argumentValue = xmlArguments[i];
                            break;

                        case MetadataType.Int32:
                            int result;
                            if (int.TryParse(xmlArguments[i], out result))
                            {
                                argumentValue = result;
                            }
                            else
                            {
                                _context.LogWarning($"Argument '{xmlArguments[i]}' specified in '{_xmlDocumentLocation}' could not be transformed to the constructor parameter type", 2032, _xmlDocumentLocation);
                            }
                            break;

                        default:
                            _context.LogWarning($"Argument '{xmlArguments[i]}' specified in '{_xmlDocumentLocation}' is of unsupported type '{constructor.Parameters[i].ParameterType}'", 2020, _xmlDocumentLocation);
                            recognizedArgument = false;
                            break;
                        }
                    }
                    attribute.ConstructorArguments.Add(new CustomAttributeArgument(constructor.Parameters[i].ParameterType, argumentValue));
                }
                if (recognizedArgument)
                {
                    attributes.Add(attribute);
                }
            }
            return(attributes);
        }