Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unactionable Trim warnings when referencing both Npgsql and Microsoft.Extensions.Http.Resilience #97057

Open
eerhardt opened this issue Jan 16, 2024 · 6 comments
Assignees
Milestone

Comments

@eerhardt
Copy link
Member

Description

When referencing 2 totally different packages independently, I'm able to publish an app without any warnings. But when I combine them and use them together, I'm getting trim warnings that I can't fix as a end-user developer.

Reproduction Steps

dotnet publish the following app:

csproj:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <InvariantGlobalization>true</InvariantGlobalization>
    <PublishAot>true</PublishAot>
    <TrimmerSingleWarn>false</TrimmerSingleWarn>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Npgsql.DependencyInjection" Version="8.0.0" />
    <PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.2.0-preview.24066.3" />
  </ItemGroup>
  
</Project>

(Note that <PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.2.0-preview.24066.3" /> needs a nuget.config entry for <add key="dotnet8" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet8/nuget/v3/index.json" />)

Program.cs:

var builder = WebApplication.CreateSlimBuilder(args);

builder.Services.AddNpgsqlDataSource(builder.Configuration.GetConnectionString("npgsql") ?? throw new Exception("configure npgsql"));

builder.Services.ConfigureHttpClientDefaults(http =>
{
    http.AddStandardResilienceHandler();
});

var app = builder.Build();
app.MapGet("/", () => "hello, world");
app.Run();

Expected behavior

No publish warnings.

Actual behavior

ILC : Trim analysis warning IL2113: Npgsql.NpgsqlConnectionStringBuilder: 'DynamicallyAccessedMembersAttribute' on 'Npgsql.NpgsqlConnectionStringBuilder' or one of its base types references 'System.ComponentModel.ICustomTypeDescriptor.GetConverter()' which requires unreferenced code. Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All. [C:\DotNetTest\ApiTest\ApiTest.csproj]
ILC : Trim analysis warning IL2113: Npgsql.NpgsqlConnectionStringBuilder: 'DynamicallyAccessedMembersAttribute' on 'Npgsql.NpgsqlConnectionStringBuilder' or one of its base types references 'System.ComponentModel.ICustomTypeDescriptor.GetDefaultEvent()' which requires unreferenced code. The built-in EventDescriptor implementation uses Reflection which requires unreferenced code. [C:\DotNetTest\ApiTest\ApiTest.csproj]
ILC : Trim analysis warning IL2113: Npgsql.NpgsqlConnectionStringBuilder: 'DynamicallyAccessedMembersAttribute' on 'Npgsql.NpgsqlConnectionStringBuilder' or one of its base types references 'System.ComponentModel.ICustomTypeDescriptor.GetDefaultProperty()' which requires unreferenced code. PropertyDescriptor's PropertyType cannot be statically discovered. [C:\DotNetTest\ApiTest\ApiTest.csproj]
ILC : Trim analysis warning IL2113: Npgsql.NpgsqlConnectionStringBuilder: 'DynamicallyAccessedMembersAttribute' on 'Npgsql.NpgsqlConnectionStringBuilder' or one of its base types references 'System.ComponentModel.ICustomTypeDescriptor.GetEditor(Type)' which requires unreferenced code. Editors registered in TypeDescriptor.AddEditorTable may be trimmed. [C:\DotNetTest\ApiTest\ApiTest.csproj]
ILC : Trim analysis warning IL2113: Npgsql.NpgsqlConnectionStringBuilder: 'DynamicallyAccessedMembersAttribute' on 'Npgsql.NpgsqlConnectionStringBuilder' or one of its base types references 'System.ComponentModel.ICustomTypeDescriptor.GetEvents(Attribute[])' which requires unreferenced code. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type. [C:\DotNetTest\ApiTest\ApiTest.csproj]
ILC : Trim analysis warning IL2113: Npgsql.NpgsqlConnectionStringBuilder: 'DynamicallyAccessedMembersAttribute' on 'Npgsql.NpgsqlConnectionStringBuilder' or one of its base types references 'System.ComponentModel.ICustomTypeDescriptor.GetProperties()' which requires unreferenced code. PropertyDescriptor's PropertyType cannot be statically discovered. [C:\DotNetTest\ApiTest\ApiTest.csproj]
ILC : Trim analysis warning IL2113: Npgsql.NpgsqlConnectionStringBuilder: 'DynamicallyAccessedMembersAttribute' on 'Npgsql.NpgsqlConnectionStringBuilder' or one of its base types references 'System.ComponentModel.ICustomTypeDescriptor.GetProperties(Attribute[])' which requires unreferenced code. PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type. [C:\DotNetTest\ApiTest\ApiTest.csproj]
ILC : Trim analysis warning IL2112: Npgsql.NpgsqlConnectionStringBuilder.GetProperties(Hashtable): 'DynamicallyAccessedMembersAttribute' on 'Npgsql.NpgsqlConnectionStringBuilder' or one of its base types references 'Npgsql.NpgsqlConnectionStringBuilder.GetProperties(Hashtable)' which requires unreferenced code. PropertyDescriptor's PropertyType cannot be statically discovered. [C:\DotNetTest\ApiTest\ApiTest.csproj]

Note that if you comment out either line 2 or 3 in the Program.cs, there are no warnings. But when you have both, then the warnings show up.

Regression?

No response

Known Workarounds

No response

Configuration

No response

Other information

The reason this shows up is because AddStandardResilienceHandler() brings in a bunch of System.ComponentModel interfaces like
ICustomTypeDescriptor.

I see the same warnings are being suppressed on the base DbConnectionStringBuilder:

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2113:ReflectionToRequiresUnreferencedCode",
Justification = "The use of GetType preserves ICustomTypeDescriptor members with RequiresUnreferencedCode, but the GetType callsites either "
+ "occur in RequiresUnreferencedCode scopes, or have individually justified suppressions.")]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2112:ReflectionToRequiresUnreferencedCode",
Justification = "The use of GetType preserves implementation of ICustomTypeDescriptor members with RequiresUnreferencedCode, but the GetType callsites either "
+ "occur in RequiresUnreferencedCode scopes, or have individually justified suppressions.")]

See also:

Should NpgsqlConnectionStringBuilder be suppressing these warnings as well?

cc @vitek-karas @sbomer @roji

@dotnet-issue-labeler dotnet-issue-labeler bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label Jan 16, 2024
@ghost ghost added the untriaged New issue has not been triaged by the area owner label Jan 16, 2024
@MichalStrehovsky MichalStrehovsky added area-NativeAOT-coreclr and removed needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners labels Jan 17, 2024
@ghost
Copy link

ghost commented Jan 17, 2024

Tagging subscribers to this area: @agocke, @MichalStrehovsky, @jkotas
See info in area-owners.md if you want to be subscribed.

Issue Details

Description

When referencing 2 totally different packages independently, I'm able to publish an app without any warnings. But when I combine them and use them together, I'm getting trim warnings that I can't fix as a end-user developer.

Reproduction Steps

dotnet publish the following app:

csproj:

<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
    <InvariantGlobalization>true</InvariantGlobalization>
    <PublishAot>true</PublishAot>
    <TrimmerSingleWarn>false</TrimmerSingleWarn>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Npgsql.DependencyInjection" Version="8.0.0" />
    <PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.2.0-preview.24066.3" />
  </ItemGroup>
  
</Project>

(Note that <PackageReference Include="Microsoft.Extensions.Http.Resilience" Version="8.2.0-preview.24066.3" /> needs a nuget.config entry for <add key="dotnet8" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet8/nuget/v3/index.json" />)

Program.cs:

var builder = WebApplication.CreateSlimBuilder(args);

builder.Services.AddNpgsqlDataSource(builder.Configuration.GetConnectionString("npgsql") ?? throw new Exception("configure npgsql"));

builder.Services.ConfigureHttpClientDefaults(http =>
{
    http.AddStandardResilienceHandler();
});

var app = builder.Build();
app.MapGet("/", () => "hello, world");
app.Run();

Expected behavior

No publish warnings.

Actual behavior

ILC : Trim analysis warning IL2113: Npgsql.NpgsqlConnectionStringBuilder: 'DynamicallyAccessedMembersAttribute' on 'Npgsql.NpgsqlConnectionStringBuilder' or one of its base types references 'System.ComponentModel.ICustomTypeDescriptor.GetConverter()' which requires unreferenced code. Generic TypeConverters may require the generic types to be annotated. For example, NullableConverter requires the underlying type to be DynamicallyAccessedMembers All. [C:\DotNetTest\ApiTest\ApiTest.csproj]
ILC : Trim analysis warning IL2113: Npgsql.NpgsqlConnectionStringBuilder: 'DynamicallyAccessedMembersAttribute' on 'Npgsql.NpgsqlConnectionStringBuilder' or one of its base types references 'System.ComponentModel.ICustomTypeDescriptor.GetDefaultEvent()' which requires unreferenced code. The built-in EventDescriptor implementation uses Reflection which requires unreferenced code. [C:\DotNetTest\ApiTest\ApiTest.csproj]
ILC : Trim analysis warning IL2113: Npgsql.NpgsqlConnectionStringBuilder: 'DynamicallyAccessedMembersAttribute' on 'Npgsql.NpgsqlConnectionStringBuilder' or one of its base types references 'System.ComponentModel.ICustomTypeDescriptor.GetDefaultProperty()' which requires unreferenced code. PropertyDescriptor's PropertyType cannot be statically discovered. [C:\DotNetTest\ApiTest\ApiTest.csproj]
ILC : Trim analysis warning IL2113: Npgsql.NpgsqlConnectionStringBuilder: 'DynamicallyAccessedMembersAttribute' on 'Npgsql.NpgsqlConnectionStringBuilder' or one of its base types references 'System.ComponentModel.ICustomTypeDescriptor.GetEditor(Type)' which requires unreferenced code. Editors registered in TypeDescriptor.AddEditorTable may be trimmed. [C:\DotNetTest\ApiTest\ApiTest.csproj]
ILC : Trim analysis warning IL2113: Npgsql.NpgsqlConnectionStringBuilder: 'DynamicallyAccessedMembersAttribute' on 'Npgsql.NpgsqlConnectionStringBuilder' or one of its base types references 'System.ComponentModel.ICustomTypeDescriptor.GetEvents(Attribute[])' which requires unreferenced code. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type. [C:\DotNetTest\ApiTest\ApiTest.csproj]
ILC : Trim analysis warning IL2113: Npgsql.NpgsqlConnectionStringBuilder: 'DynamicallyAccessedMembersAttribute' on 'Npgsql.NpgsqlConnectionStringBuilder' or one of its base types references 'System.ComponentModel.ICustomTypeDescriptor.GetProperties()' which requires unreferenced code. PropertyDescriptor's PropertyType cannot be statically discovered. [C:\DotNetTest\ApiTest\ApiTest.csproj]
ILC : Trim analysis warning IL2113: Npgsql.NpgsqlConnectionStringBuilder: 'DynamicallyAccessedMembersAttribute' on 'Npgsql.NpgsqlConnectionStringBuilder' or one of its base types references 'System.ComponentModel.ICustomTypeDescriptor.GetProperties(Attribute[])' which requires unreferenced code. PropertyDescriptor's PropertyType cannot be statically discovered. The public parameterless constructor or the 'Default' static field may be trimmed from the Attribute's Type. [C:\DotNetTest\ApiTest\ApiTest.csproj]
ILC : Trim analysis warning IL2112: Npgsql.NpgsqlConnectionStringBuilder.GetProperties(Hashtable): 'DynamicallyAccessedMembersAttribute' on 'Npgsql.NpgsqlConnectionStringBuilder' or one of its base types references 'Npgsql.NpgsqlConnectionStringBuilder.GetProperties(Hashtable)' which requires unreferenced code. PropertyDescriptor's PropertyType cannot be statically discovered. [C:\DotNetTest\ApiTest\ApiTest.csproj]

Note that if you comment out either line 2 or 3 in the Program.cs, there are no warnings. But when you have both, then the warnings show up.

Regression?

No response

Known Workarounds

No response

Configuration

No response

Other information

The reason this shows up is because AddStandardResilienceHandler() brings in a bunch of System.ComponentModel interfaces like
ICustomTypeDescriptor.

I see the same warnings are being suppressed on the base DbConnectionStringBuilder:

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2113:ReflectionToRequiresUnreferencedCode",
Justification = "The use of GetType preserves ICustomTypeDescriptor members with RequiresUnreferencedCode, but the GetType callsites either "
+ "occur in RequiresUnreferencedCode scopes, or have individually justified suppressions.")]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2112:ReflectionToRequiresUnreferencedCode",
Justification = "The use of GetType preserves implementation of ICustomTypeDescriptor members with RequiresUnreferencedCode, but the GetType callsites either "
+ "occur in RequiresUnreferencedCode scopes, or have individually justified suppressions.")]

See also:

Should NpgsqlConnectionStringBuilder be suppressing these warnings as well?

cc @vitek-karas @sbomer @roji

Author: eerhardt
Assignees: -
Labels:

untriaged, area-NativeAOT-coreclr, needs-area-label

Milestone: -

@MichalStrehovsky
Copy link
Member

Looks like the IL2113 warnings only show up for PublishAot. The IL2112 shows up for both PublishAot and PublishTrimmed.

This is also potentially related to #88512 that was filed due to the same .All being applied on DbConnectionStringBuilder. We probably don't need .All. We probably just need something like .AllProperties and .AllEvents. The problems that .All is causing and then we need to suppress and the suppressions are hard, etc. is very likely caused by not having a mechanism for more targeted preservation that would avoid the problematic cases.

@vitek-karas
Copy link
Member

@LakshanF as this is related to TypeDescriptor and potential improvements in that area.

This will teach us to think more than twice before putting any annotation on a virtual/interface method :-)

@eerhardt
Copy link
Member Author

Should NpgsqlConnectionStringBuilder be suppressing these warnings as well?

Since these warnings are being suppressed on the base DbConnectionStringBuilder, is it safe to assume that we can suppress them on NpgsqlConnectionStringBuilder as well? NpgsqlConnectionStringBuilder isn't doing anything special here - it is just trying to implement the ICustomTypeDescriptor overrides (which have RequiresUnreferencedCode on them - so any real callers of those methods will get warnings).

We probably don't need .All.

The root of need for .All (as far as I can tell) comes from a couple places:

[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2062:UnrecognizedReflectionPattern",
Justification = "_type is annotated as preserve All members, so any Types returned from GetInterfaces should be preserved as well once https://github.com/mono/linker/issues/1731 is fixed.")]

Type[] interfaces = _type.GetInterfaces();
for (int idx = 0; idx < interfaces.Length; idx++)
{
// Only do this for public interfaces.
Type iface = interfaces[idx];
if ((iface.Attributes & (TypeAttributes.Public | TypeAttributes.NestedPublic)) != 0)
{
// No need to pass an instance into GetTypeDescriptor here because, if someone provided a custom
// provider based on object, it already would have hit.
attributes.AddRange(TypeDescriptor.GetAttributes(iface).Attributes);

In order to get all the attributes for a Type, we need to walk all the interfaces the Type implements and get events, properties, etc. on it.

I tried lessening the .All annotations (see this branch), but I ran into these places that recurse up the hierarchy:

Type? start = _componentClass.BaseType;
while (start != null && start != typeof(object))
{
BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
EventInfo test = start.GetEvent(_realEvent.Name, bindingFlags)!;

Type? baseType = _type;
Type objType = typeof(object);
do
{
eventArray = ReflectGetEvents(baseType);

AFAIK, the trim analysis has special handling of .All, won't trim things from the base types, and won't warn about hierarchy walking like this. But now that we aren't using .All, these places could be broken.

Also note that I ended up needing to preserve:

  • PublicProperties
  • Interfaces
  • PublicEvents
  • NonPublicEvents
  • PublicMethods (needed because of this code)

Which would probably cause us to have this same problem anyway - it would still need to preserve the ICustomTypeDescriptor interface and all its methods.

@MichalStrehovsky
Copy link
Member

AFAIK, the trim analysis has special handling of .All, won't trim things from the base types, and won't warn about hierarchy walking like this. But now that we aren't using .All, these places could be broken.

#88512 is about introducing annotations that would allow walking base hierarchies and looking for members without having to preserve things to the extent that .All does. (we could preserve e.g. all methods, including those on base types, without having to go all in with .All)

The problem that .All is causing here is that it will walk into the list of implemented interfaces and apply .All to all of the methods on those interfaces as well (that's what the warnings are about). If we could say AllEventsInHierarchy | AllPropertiesInHierarchy, it would avoid the problem with marking the methods on the interface as reflection-exposed because it would only go to bases.

The fact we need to mark PublicMethods is annoying and the linked code is atrocious, but maybe it would still be okay because it's just the public methods and there don't appear to be any public methods on DbConnectionStringBuilder that would be RUC (explicitly implemented interface methods are not public).

Being more targeted causes fewer ripple effects.

Not saying we don't have a bug with the suppression not working right, but we wouldn't run into it if we didn't use .All.

eerhardt added a commit to eerhardt/npgsql that referenced this issue Feb 14, 2024
- Split ThrowIfUnsupported out into a nested class of JsonDynamicTypeInfoResolverFactory to avoid erroneous warnings.
- Suppress warnings on NpgsqlConnectionStringBuilder to match base DbConnectionStringBuilder suppressions. See dotnet/runtime#97057 for an explanation of why these warnings happen.

Fix npgsql#5577
eerhardt added a commit to eerhardt/npgsql that referenced this issue Feb 14, 2024
- Split ThrowIfUnsupported out into a nested class of JsonDynamicTypeInfoResolverFactory to avoid erroneous warnings.
- Suppress warnings on NpgsqlConnectionStringBuilder to match base DbConnectionStringBuilder suppressions. See dotnet/runtime#97057 for an explanation of why these warnings happen.

Fix npgsql#5577
@eerhardt
Copy link
Member Author

FYI - I opened npgsql/npgsql#5578 to add the same suppressions to NpgsqlConnectionStringBuilder as we have on DbConnectionStringBuilder. But it would be good to see if there is something we can do in dotnet/runtime to fix this general problem.

@sbomer sbomer added this to the 9.0.0 milestone Apr 25, 2024
@sbomer sbomer removed the untriaged New issue has not been triaged by the area owner label Apr 25, 2024
@sbomer sbomer self-assigned this Apr 25, 2024
@agocke agocke modified the milestones: 9.0.0, 10.0.0 Jul 8, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

No branches or pull requests

5 participants