Dante Marshal
Dante Marshal

Reputation: 97

Why am I still getting IL2104 after resolving all other trimming warnings ? [ C#, .NET 8.0 ]

I have a library that supports both .NET Standard 2.1 and .NET 8.0. The .NET 8.0 version is marked with <IsAotCompatible>true</IsAotCompatible>and a Test trimmer project is set up according to the link on IL2104 warning

After setting up that environment, I have found many warnings and used DynamicallyAccessedMembers, RequiresUnreferencedCode and UnconditionalSuppressMessage to annotate my library accordingly until all of them were gone except for IL2104 itself.

I'm still getting this warning after getting rid of all the others.

A:\Proj\lib\My.Lib\My.Lib\bin\Release\net8.0\My.Lib.dll : warning IL2104: Assembly 'My.Lib' produced trim warnings. For more information see https://aka.ms/dotnet-illink/libraries 
[A:\Proj\lib\My.Lib\TrimmerBuilder\TrimmerBuilder.csproj]

Shouldn't it be already gone? What am I doing wrong here?


Update

Here is a reproducible example as requested, Keep in mind that these are only one of the places I used and annotated non-trimmable features. I'm not sure if this is the only place I'm getting the warning for, but this example alone does behave in the way I described above.

(Let me know in the comments if more context is required so I'll edit and update the question)

ClassLibCode.cs

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
namespace MinimalExample;
/// <summary><see cref="Type"/> extension methods</summary>
public static class TypeXs
{
    /// <summary>Returns whether the type is a closure class</summary>
    public static bool IsClosureClass(this Type type)
    {
        if (type?.GetCustomAttribute<CompilerGeneratedAttribute>() != null)
        {
            return type.Name.Contains("DisplayClass") || type.Name.Contains("Closure$");
        }
        return false;
    }
    /// <summary>Returns whether the type is anonymous</summary>
    public static bool IsAnonymous(this Type type)
    {
        if (type?.GetCustomAttribute<CompilerGeneratedAttribute>() != null)
        {
            return type.Name.Contains("Anonymous") && type.Name.Contains("<>") && !type.InheritsFromOrImplements<Delegate>();
        }
        return false;
    }
    /// <summary>Returns whether a type Inherits from or Implements another type</summary>
    public static bool InheritsFromOrImplements<T>(this Type type)
    {
        return typeof(T).IsAssignableFrom(type);
    }
    /// <summary>A struct containing types information in a nested array</summary>
    public readonly struct NestedArrayType
    {
        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
        public readonly Type? root;
        [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)]
        public readonly Type current;
        public NestedArrayType(
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type? root,
            [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type current
        )
        {
            this.root = root;
            this.current = current;
        }
#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
    }
    /// <summary>Returns all the nested types of an array of arrays</summary>
    [RequiresUnreferencedCode(message: "Only flat arrays are supported, Nested ones are not supported")]
    public static IEnumerable<NestedArrayType> NestedArrayTypes([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] this Type type)
    {
        Type currentType = type;
        while (currentType.IsArray)
        {
            Type nextType = currentType.GetElementType()!; // not null cuz we checked `IsArray` above
            if (nextType.IsArray)
            {
                yield return new(root: null, current: currentType);
            }
            else
            {
                yield return new(root: nextType, current: currentType);
                break;
            }
            currentType = nextType;
        }
    }
    /// <summary>Return the non-generic part of the type name (before the tick (`) in generics)</summary>
    public static string NonGenericName(this Type t)
    {
        return t.Name[..t.Name.IndexOf('`')];
    }
    /// <summary><see cref="Type.FindMembers"/> doesn't return inheritted private fields on its own. This method recursively gathers all of them</summary>
    public static IEnumerable<MemberInfo> FindMembersIncludingInheritedPrivates([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] this Type? type, MemberTypes memberTypes, BindingFlags bindingFlags, MemberFilter memberFilter, object? filterCriteria = null)
    {
        if (type == null || type == typeof(object) || !type.IsClass || type.IsInterface) { return Enumerable.Empty<MemberInfo>(); }
        IEnumerable<MemberInfo> baseMembers = type.BaseType.FindMembersIncludingInheritedPrivates(memberTypes, bindingFlags, memberFilter, filterCriteria);
        IEnumerable<MemberInfo> interfacesMembers = type.GetInterfaces().SelectMany(([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] x) => x.FindMembersIncludingInheritedPrivates(memberTypes, bindingFlags, memberFilter, filterCriteria));
        IEnumerable<MemberInfo> currentMembers = type.FindMembers(memberTypes, bindingFlags, memberFilter, filterCriteria);
        return baseMembers.Concat(interfacesMembers).Concat(currentMembers);
    }
    /// <summary>Returns a human-readable name for the type</summary>
    [RequiresUnreferencedCode("Trimming is not supported on some types")]
    public static string FriendlyName([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] this Type self)
    {
        if (self == null) { throw new ArgumentNullException(nameof(self)); }
        if (self.IsClosureClass())
        {
            return "<closure>";
        }
        if (self.IsAnonymous())
        {
            return "{ " + string.Join(", ", Array.ConvertAll(self.GetProperties(),
                ([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] PropertyInfo p) =>
                {
                    string name = p.Name;
                    string typename = "UnknownType";
                    return $"{typename} {name}";
                }
            )) + " }";
        }
        if (self.IsArray)
        {
            (string? left, string? right) = ("[", "]");
            var nestedArrayTypes = self.NestedArrayTypes().ToList();
            string arraySpecifiers = string.Join("", nestedArrayTypes.ConvertAll(
                v => left + string.Join("", Enumerable.Repeat("", v.current.GetArrayRank())) + right
            ));
            return nestedArrayTypes.Last().root!.FriendlyName() + arraySpecifiers;
        }
        if (self.IsGenericType)
        {
            if (self.GetGenericTypeDefinition() == typeof(Nullable<>))
            {
                return (Nullable.GetUnderlyingType(self) ?? self).FriendlyName() + "?";
            }
            if (self.IsGenericParameter)
            {
                return self.Name;
            }
            Type[] args = self.GetGenericArguments();
            // var parts = self.GetGenericArguments().Joined(", ", t => t.FriendlyName()!);
            string parts = string.Join(", ", Array.ConvertAll(self.GetGenericArguments(), ([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] t) => t.FriendlyName()!));
            string nongenericName = self.NonGenericName();
            return $"{nongenericName}<{parts}>";
        }
        else
        {
            return self.Name;
        }
    }
    /// <summary>Returns a human-readable full-name for the type</summary>
    [RequiresUnreferencedCode("Trimming is not supported on some types")]
    public static string FriendlyFullName([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] this Type self)
    {
        return $"{self.Namespace}::{self.FriendlyName()}";
    }
}

ClassLib.csproj

<Project Sdk="Microsoft.NET.Sdk">
    <!-- Project -->
    <PropertyGroup>
        <Version>0.0.0.1</Version>
        <TargetFrameworks>net8.0;netstandard2.1</TargetFrameworks>
        <!-- <TargetFramework>netstandard2.1</TargetFramework> -->
        <PublishRelease>true</PublishRelease>
        <Nullable>enable</Nullable>
        <LangVersion>latest</LangVersion>
        <GenerateDocumentationFile>true</GenerateDocumentationFile>
        <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    </PropertyGroup>
    <!-- Net8.0 -->
    <PropertyGroup Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net8.0'))">
        <IsAotCompatible>true</IsAotCompatible>
        <EnableTrimAnalyzer>true</EnableTrimAnalyzer>
        <SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
    </PropertyGroup>
    <!-- Publish -->
    <PropertyGroup Condition="'$(Configuration)'=='Release'">
        <EmbedUntrackedSources>true</EmbedUntrackedSources>
        <EmbedAllSources>true</EmbedAllSources>
        <DebugType>portable</DebugType>
        <CopyDocumentationFilesFromPackages>true</CopyDocumentationFilesFromPackages>
        <CopyDebugSymbolFilesFromPackages>true</CopyDebugSymbolFilesFromPackages>
        <AllowedOutputExtensionsInPackageBuildOutputFolder>$(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
    </PropertyGroup>
</Project>

Trimmer.csproj

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>
        <PublishTrimmed>true</PublishTrimmed>
        <PublishAot>true</PublishAot>
        <OutputType>Exe</OutputType>
        <Nullable>enable</Nullable>
        <EnableTrimAnalyzer>true</EnableTrimAnalyzer>
        <SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>
    </PropertyGroup>
    <ItemGroup>
        <ProjectReference Include="..\ClassLib\ClassLib.csproj" />
        <TrimmerRootAssembly Include="ClassLib" />
    </ItemGroup>
</Project>

Upvotes: 0

Views: 72

Answers (0)

Related Questions