Skip to content

Commit

Permalink
Very first work on trimming compatibility
Browse files Browse the repository at this point in the history
* Annotate composite types as incompatible with trimming, while
  suppressing warnings for applications which don't use them.
* Create a very basic console test program for testing (#3820). We'll
  eventually run this in CI.

Part of #3300
Part of #3820
  • Loading branch information
roji committed Aug 2, 2021
1 parent f62bf43 commit 7389176
Show file tree
Hide file tree
Showing 15 changed files with 175 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<VersionPrefix>6.0.0-preview7</VersionPrefix>
<LangVersion>9.0</LangVersion>
<LangVersion>latest</LangVersion>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<Nullable>enable</Nullable>
<AnalysisLevel>latest</AnalysisLevel>
Expand Down
11 changes: 11 additions & 0 deletions Npgsql.sln
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Npgsql.SourceGenerators", "src\Npgsql.SourceGenerators\Npgsql.SourceGenerators.csproj", "{63026A19-60B8-4906-81CB-216F30E8094B}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Npgsql.TrimmingTests", "test\Npgsql.TrimmingTests\Npgsql.TrimmingTests.csproj", "{844EC023-21B8-448D-93AD-5F6857F15DFF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -133,6 +135,14 @@ Global
{63026A19-60B8-4906-81CB-216F30E8094B}.Release|Any CPU.Build.0 = Release|Any CPU
{63026A19-60B8-4906-81CB-216F30E8094B}.Release|x86.ActiveCfg = Release|Any CPU
{63026A19-60B8-4906-81CB-216F30E8094B}.Release|x86.Build.0 = Release|Any CPU
{844EC023-21B8-448D-93AD-5F6857F15DFF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{844EC023-21B8-448D-93AD-5F6857F15DFF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{844EC023-21B8-448D-93AD-5F6857F15DFF}.Debug|x86.ActiveCfg = Debug|Any CPU
{844EC023-21B8-448D-93AD-5F6857F15DFF}.Debug|x86.Build.0 = Debug|Any CPU
{844EC023-21B8-448D-93AD-5F6857F15DFF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{844EC023-21B8-448D-93AD-5F6857F15DFF}.Release|Any CPU.Build.0 = Release|Any CPU
{844EC023-21B8-448D-93AD-5F6857F15DFF}.Release|x86.ActiveCfg = Release|Any CPU
{844EC023-21B8-448D-93AD-5F6857F15DFF}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -149,6 +159,7 @@ Global
{F7C53EBD-0075-474F-A083-419257D04080} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}
{A77E5FAF-D775-4AB4-8846-8965C2104E60} = {ED612DB1-AB32-4603-95E7-891BACA71C39}
{63026A19-60B8-4906-81CB-216F30E8094B} = {8537E50E-CF7F-49CB-B4EF-3E2A1B11F050}
{844EC023-21B8-448D-93AD-5F6857F15DFF} = {ED612DB1-AB32-4603-95E7-891BACA71C39}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C90AEECD-DB4C-4BE6-B506-16A449852FB8}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
Expand All @@ -11,6 +12,32 @@
using Npgsql.TypeMapping;
using NpgsqlTypes;

#region Trimming warning suppressions

[module: UnconditionalSuppressMessage(
"Composite type mapping currently isn't trimming-safe, and warnings are generated at the MapComposite level.",
"IL2046", Scope = "type", Target = "Npgsql.Internal.TypeHandlers.CompositeHandlers.CompositeHandler")]
[module: UnconditionalSuppressMessage(
"Composite type mapping currently isn't trimming-safe, and warnings are generated at the MapComposite level.",
"IL2080", Scope = "type", Target = "Npgsql.Internal.TypeHandlers.CompositeHandlers.CompositeHandler")]
[module: UnconditionalSuppressMessage(
"Composite type mapping currently isn't trimming-safe, and warnings are generated at the MapComposite level.",
"IL2026", Scope = "type", Target = "Npgsql.Internal.TypeHandlers.CompositeHandlers.CompositeHandler")]
[module: UnconditionalSuppressMessage(
"Composite type mapping currently isn't trimming-safe, and warnings are generated at the MapComposite level.",
"IL2090", Scope = "type", Target = "Npgsql.Internal.TypeHandlers.CompositeHandlers.CompositeHandler")]
[module: UnconditionalSuppressMessage(
"Composite type mapping currently isn't trimming-safe, and warnings are generated at the MapComposite level.",
"IL2087", Scope = "type", Target = "Npgsql.Internal.TypeHandlers.CompositeHandlers.CompositeHandler")]
[module: UnconditionalSuppressMessage(
"Composite type mapping currently isn't trimming-safe, and warnings are generated at the MapComposite level.",
"IL2055", Scope = "type", Target = "Npgsql.Internal.TypeHandlers.CompositeHandlers.CompositeHandler")]
[module: UnconditionalSuppressMessage(
"Composite type mapping currently isn't trimming-safe, and warnings are generated at the MapComposite level.",
"IL2077", Scope = "type", Target = "Npgsql.Internal.TypeHandlers.CompositeHandlers.CompositeHandler")]

#endregion

namespace Npgsql.Internal.TypeHandlers.CompositeHandlers
{
partial class CompositeHandler<T> : NpgsqlTypeHandler<T>, ICompositeHandler
Expand Down
5 changes: 4 additions & 1 deletion src/Npgsql/Internal/TypeHandlers/UnmappedEnumHandler.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Threading;
Expand Down Expand Up @@ -60,6 +61,7 @@ public override int ValidateObjectAndGetLength(object? value, ref NpgsqlLengthCa
protected internal override int ValidateAndGetLengthCustom<TAny>(TAny value, ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter)
=> ValidateAndGetLength(value!, ref lengthCache, parameter);

[UnconditionalSuppressMessage("Unmapped enums currently aren't trimming-safe.", "IL2072")]
int ValidateAndGetLength(object value, ref NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter)
{
var type = value.GetType();
Expand Down Expand Up @@ -97,6 +99,7 @@ async Task WriteWithLengthLong(object value, NpgsqlWriteBuffer buf, NpgsqlLength
}
}

[UnconditionalSuppressMessage("Unmapped enums currently aren't trimming-safe.", "IL2072")]
internal Task Write(object value, NpgsqlWriteBuffer buf, NpgsqlLengthCache? lengthCache, NpgsqlParameter? parameter, bool async, CancellationToken cancellationToken = default)
{
var type = value.GetType();
Expand All @@ -115,7 +118,7 @@ internal Task Write(object value, NpgsqlWriteBuffer buf, NpgsqlLengthCache? leng

#region Misc

void Map(Type type)
void Map([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] Type type)
{
Debug.Assert(_resolvedType != type);

Expand Down
74 changes: 74 additions & 0 deletions src/Npgsql/Netstandard20/CodeAnalysis.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#if !NET5_0_OR_GREATER
using System;
using System.Diagnostics.CodeAnalysis;

// ReSharper disable once CheckNamespace
namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Class, Inherited = false)]
sealed class RequiresUnreferencedCodeAttribute : Attribute
{
public RequiresUnreferencedCodeAttribute(string message)
{
Message = message;
}

public string Message { get; }

public string? Url { get; set; }
}

[AttributeUsage(
AttributeTargets.Field | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter |
AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Method |
AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct,
Inherited = false)]
sealed class DynamicallyAccessedMembersAttribute : Attribute
{
public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes)
{
MemberTypes = memberTypes;
}

public DynamicallyAccessedMemberTypes MemberTypes { get; }
}

[Flags]
enum DynamicallyAccessedMemberTypes
{
None = 0,
PublicParameterlessConstructor = 0x0001,
PublicConstructors = 0x0002 | PublicParameterlessConstructor,
NonPublicConstructors = 0x0004,
PublicMethods = 0x0008,
NonPublicMethods = 0x0010,
PublicFields = 0x0020,
NonPublicFields = 0x0040,
PublicNestedTypes = 0x0080,
NonPublicNestedTypes = 0x0100,
PublicProperties = 0x0200,
NonPublicProperties = 0x0400,
PublicEvents = 0x0800,
NonPublicEvents = 0x1000,
Interfaces = 0x2000,
All = ~None
}

[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
sealed class UnconditionalSuppressMessageAttribute : Attribute
{
public UnconditionalSuppressMessageAttribute(string category, string checkId)
{
Category = category;
CheckId = checkId;
}

public string Category { get; }
public string CheckId { get; }
public string? Scope { get; set; }
public string? Target { get; set; }
public string? MessageId { get; set; }
public string? Justification { get; set; }
}
}
#endif
5 changes: 5 additions & 0 deletions src/Npgsql/NpgsqlConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1492,6 +1492,7 @@ public static void UnmapEnumGlobally<TEnum>(string? pgName = null, INpgsqlNameTr
/// </param>
/// <typeparam name="T">The .NET type to be mapped</typeparam>
[Obsolete("Use NpgsqlConnection.TypeMapper.MapComposite() instead")]
[RequiresUnreferencedCode("Composite type mapping currently isn't trimming-safe.")]
public void MapComposite<T>(string? pgName = null, INpgsqlNameTranslator? nameTranslator = null) where T : new()
=> TypeMapper.MapComposite<T>(pgName, nameTranslator);

Expand All @@ -1518,6 +1519,7 @@ public void MapComposite<T>(string? pgName = null, INpgsqlNameTranslator? nameTr
/// </param>
/// <typeparam name="T">The .NET type to be mapped</typeparam>
[Obsolete("Use NpgsqlConnection.GlobalTypeMapper.MapComposite() instead")]
[RequiresUnreferencedCode("Composite type mapping currently isn't trimming-safe.")]
public static void MapCompositeGlobally<T>(string? pgName = null, INpgsqlNameTranslator? nameTranslator = null) where T : new()
=> GlobalTypeMapper.MapComposite<T>(pgName, nameTranslator);

Expand All @@ -1533,6 +1535,7 @@ public static void MapCompositeGlobally<T>(string? pgName = null, INpgsqlNameTra
/// Defaults to <see cref="NpgsqlSnakeCaseNameTranslator"/>
/// </param>
[Obsolete("Use NpgsqlConnection.GlobalTypeMapper.UnmapComposite() instead")]
[RequiresUnreferencedCode("Composite type mapping currently isn't trimming-safe.")]
public static void UnmapCompositeGlobally<T>(string pgName, INpgsqlNameTranslator? nameTranslator = null) where T : new()
=> GlobalTypeMapper.UnmapComposite<T>(pgName, nameTranslator);

Expand Down Expand Up @@ -1820,6 +1823,8 @@ internal void EndBindingScope(ConnectorBindingScope scope)
/// <summary>
/// Returns the supported collections
/// </summary>
[UnconditionalSuppressMessage(
"Composite type mapping currently isn't trimming-safe, and warnings are generated at the MapComposite level.", "IL2026")]
public override DataTable GetSchema()
=> GetSchema("MetaDataCollections", null);

Expand Down
7 changes: 7 additions & 0 deletions src/Npgsql/NpgsqlDataReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Data;
using System.Data.Common;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.CompilerServices;
Expand Down Expand Up @@ -1931,12 +1932,16 @@ Task<ReadOnlyCollection<NpgsqlDbColumn>> GetColumnSchema(bool async, Cancellatio
/// <summary>
/// Returns a System.Data.DataTable that describes the column metadata of the DataReader.
/// </summary>
[UnconditionalSuppressMessage(
"Composite type mapping currently isn't trimming-safe, and warnings are generated at the MapComposite level.", "IL2026")]
public override DataTable? GetSchemaTable()
=> GetSchemaTable(async: false).GetAwaiter().GetResult();
/// <summary>
/// Asynchronously returns a System.Data.DataTable that describes the column metadata of the DataReader.
/// </summary>
[UnconditionalSuppressMessage(
"Composite type mapping currently isn't trimming-safe, and warnings are generated at the MapComposite level.", "IL2026")]
#if NET5_0_OR_GREATER
public override Task<DataTable?> GetSchemaTableAsync(CancellationToken cancellationToken = default)
#else
Expand All @@ -1947,6 +1952,8 @@ Task<ReadOnlyCollection<NpgsqlDbColumn>> GetColumnSchema(bool async, Cancellatio
return GetSchemaTable(async: true, cancellationToken);
}
[UnconditionalSuppressMessage(
"Composite type mapping currently isn't trimming-safe, and warnings are generated at the MapComposite level.", "IL2026")]
async Task<DataTable?> GetSchemaTable(bool async, CancellationToken cancellationToken = default)
{
if (FieldCount == 0) // No resultset
Expand Down
3 changes: 2 additions & 1 deletion src/Npgsql/NpgsqlFactory.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Data.Common;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Npgsql.Logging;

Expand Down Expand Up @@ -68,7 +69,7 @@ public sealed class NpgsqlFactory : DbProviderFactory, IServiceProvider
/// </summary>
/// <param name="serviceType">An object that specifies the type of service object to get.</param>
/// <returns>A service object of type serviceType, or null if there is no service object of type serviceType.</returns>

[RequiresUnreferencedCode("Legacy EF5 method, not trimming-safe.")]
public object? GetService(Type serviceType)
{
if (serviceType == null)
Expand Down
1 change: 1 addition & 0 deletions src/Npgsql/NpgsqlParameter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,7 @@ public sealed override DbType DbType
[DbProviderSpecificTypeProperty(true)]
public NpgsqlDbType NpgsqlDbType
{
[RequiresUnreferencedCodeAttribute("The NpgsqlDbType getter isn't trimming-safe")]
get
{
if (_npgsqlDbType.HasValue)
Expand Down
4 changes: 2 additions & 2 deletions src/Npgsql/NpgsqlTypes/NpgsqlRange.cs
Original file line number Diff line number Diff line change
Expand Up @@ -429,8 +429,8 @@ public static NpgsqlRange<T> Parse(string value)
string.Equals(upperSegment, NullLiteral, StringComparison.OrdinalIgnoreCase) ||
string.Equals(upperSegment, UpperInfinityLiteral, StringComparison.OrdinalIgnoreCase);

var lower = lowerInfinite ? default : (T)BoundConverter.ConvertFromString(lowerSegment);
var upper = upperInfinite ? default : (T)BoundConverter.ConvertFromString(upperSegment);
var lower = lowerInfinite ? default : (T)BoundConverter.ConvertFromString(lowerSegment)!;
var upper = upperInfinite ? default : (T)BoundConverter.ConvertFromString(upperSegment)!;

return new NpgsqlRange<T>(lower, lowerInclusive, lowerInfinite, upper, upperInclusive, upperInfinite);
}
Expand Down
1 change: 1 addition & 0 deletions src/Npgsql/TypeMapping/GlobalTypeMapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ internal DbType ToDbType(Type type)
internal string? ToPgTypeName(Type type)
=> _typeToPgTypeName.TryGetValue(type, out var pgTypeName) ? pgTypeName : null;

[RequiresUnreferencedCodeAttribute("ToNpgsqlDbType uses interface-based reflection and isn't trimming-safe")]
internal NpgsqlDbType ToNpgsqlDbType(Type type)
{
if (_typeToNpgsqlDbType.TryGetValue(type, out var npgsqlDbType))
Expand Down
5 changes: 5 additions & 0 deletions src/Npgsql/TypeMapping/INpgsqlTypeMapper.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Npgsql.NameTranslation;
using NpgsqlTypes;

Expand Down Expand Up @@ -98,6 +99,7 @@ public interface INpgsqlTypeMapper
/// Defaults to <see cref="NpgsqlSnakeCaseNameTranslator"/>
/// </param>
/// <typeparam name="T">The .NET type to be mapped</typeparam>
[RequiresUnreferencedCode("Composite type mapping currently isn't trimming-safe.")]
INpgsqlTypeMapper MapComposite<T>(
string? pgName = null,
INpgsqlNameTranslator? nameTranslator = null);
Expand All @@ -113,6 +115,7 @@ public interface INpgsqlTypeMapper
/// A component which will be used to translate CLR names (e.g. SomeClass) into database names (e.g. some_class).
/// Defaults to <see cref="NpgsqlSnakeCaseNameTranslator"/>
/// </param>
[RequiresUnreferencedCode("Composite type mapping currently isn't trimming-safe.")]
bool UnmapComposite<T>(
string? pgName = null,
INpgsqlNameTranslator? nameTranslator = null);
Expand All @@ -136,6 +139,7 @@ public interface INpgsqlTypeMapper
/// A component which will be used to translate CLR names (e.g. SomeClass) into database names (e.g. some_class).
/// Defaults to <see cref="NpgsqlSnakeCaseNameTranslator"/>
/// </param>
[RequiresUnreferencedCode("Composite type mapping currently isn't trimming-safe.")]
INpgsqlTypeMapper MapComposite(
Type clrType,
string? pgName = null,
Expand All @@ -153,6 +157,7 @@ public interface INpgsqlTypeMapper
/// A component which will be used to translate CLR names (e.g. SomeClass) into database names (e.g. some_class).
/// Defaults to <see cref="NpgsqlSnakeCaseNameTranslator"/>
/// </param>
[RequiresUnreferencedCode("Composite type mapping currently isn't trimming-safe.")]
bool UnmapComposite(
Type clrType,
string? pgName = null,
Expand Down
5 changes: 5 additions & 0 deletions src/Npgsql/TypeMapping/TypeMapperBase.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Npgsql.Internal.TypeHandlers;
using Npgsql.Internal.TypeHandlers.CompositeHandlers;
Expand Down Expand Up @@ -69,9 +70,11 @@ public bool UnmapEnum<TEnum>(string? pgName = null, INpgsqlNameTranslator? nameT

#region Composite mapping

[RequiresUnreferencedCode("Composite type mapping currently isn't trimming-safe.")]
public INpgsqlTypeMapper MapComposite<T>(string? pgName = null, INpgsqlNameTranslator? nameTranslator = null)
=> MapComposite(pgName, nameTranslator, typeof(T), t => new CompositeTypeHandlerFactory<T>(t));

[RequiresUnreferencedCode("Composite type mapping currently isn't trimming-safe.")]
public INpgsqlTypeMapper MapComposite(Type clrType, string? pgName = null, INpgsqlNameTranslator? nameTranslator = null)
=> MapComposite(pgName, nameTranslator, clrType, t => (NpgsqlTypeHandlerFactory)
Activator.CreateInstance(typeof(CompositeTypeHandlerFactory<>).MakeGenericType(clrType), BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new object[] { t }, null)!);
Expand All @@ -94,9 +97,11 @@ INpgsqlTypeMapper MapComposite(string? pgName, INpgsqlNameTranslator? nameTransl
.Build());
}

[RequiresUnreferencedCode("Composite type mapping currently isn't trimming-safe.")]
public bool UnmapComposite<T>(string? pgName = null, INpgsqlNameTranslator? nameTranslator = null)
=> UnmapComposite(typeof(T), pgName, nameTranslator);

[RequiresUnreferencedCode("Composite type mapping currently isn't trimming-safe.")]
public bool UnmapComposite(Type clrType, string? pgName = null, INpgsqlNameTranslator? nameTranslator = null)
{
if (pgName != null && string.IsNullOrWhiteSpace(pgName))
Expand Down
14 changes: 14 additions & 0 deletions test/Npgsql.TrimmingTests/Npgsql.TrimmingTests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>exe</OutputType>
<RuntimeIdentifier>linux-x64</RuntimeIdentifier>
<PublishTrimmed>true</PublishTrimmed>
<TrimMode>link</TrimMode>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../src/Npgsql/Npgsql.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Remove="GitHubActionsTestLogger" />
</ItemGroup>
</Project>
Loading

0 comments on commit 7389176

Please sign in to comment.