Reputation: 97
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?
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