From 411d12ba5a76ac8231697e74c4c91154ae52316e Mon Sep 17 00:00:00 2001 From: latonz Date: Wed, 2 Aug 2023 11:15:39 +0200 Subject: [PATCH] chore: use source generated polyfills --- .../MappingBenchmarks.cs | 1 - .../Riok.Mapperly.Benchmarks.csproj | 12 +- .../SourceGeneratorBenchmarks.cs | 1 - build/package.sh | 1 + src/Directory.Build.props | 17 +- src/Polyfills/IsExternalInit.cs | 16 -- src/Polyfills/Nullable.cs | 212 ------------------ .../Internal/PreserveReferenceHandler.cs | 2 +- .../Internal/ReferenceEqualityComparer.cs | 23 -- .../Configuration/AttributeDataAccessor.cs | 8 +- .../MappingBuilders/EnumMappingBuilder.cs | 17 +- .../Mappings/MappingBodyBuildingPriority.cs | 10 +- src/Riok.Mapperly/Emit/SyntaxFactoryHelper.cs | 2 +- .../Helpers/DictionaryExtensions.cs | 4 +- .../Helpers/EnumerableExtensions.cs | 19 -- .../Helpers/FieldSymbolEqualityComparer.cs | 8 + src/Riok.Mapperly/Helpers/HashHelper.cs | 12 - src/Riok.Mapperly/Helpers/PriorityQueue.cs | 50 ----- .../Helpers/PriorityQueueExtensions.cs | 18 ++ .../Helpers/ReadOnlyDictionaryExtensions.cs | 10 - src/Riok.Mapperly/Helpers/SymbolExtensions.cs | 4 +- src/Riok.Mapperly/ImmutableEquatableArray.cs | 7 +- src/Riok.Mapperly/MapperGenerator.cs | 2 +- src/Riok.Mapperly/Riok.Mapperly.csproj | 16 +- src/Riok.Mapperly/Symbols/FieldMember.cs | 2 +- src/Riok.Mapperly/Symbols/PropertyMember.cs | 2 +- .../Internal/ReferenceEqualityComparerTest.cs | 44 ---- .../Riok.Mapperly.IntegrationTests.csproj | 5 + .../Helpers/EnumerableExtensionsTest.cs | 23 -- ...Test.cs => PriorityQueueExtensionsTest.cs} | 18 +- .../ReadOnlyDictionaryExtensionsTest.cs | 29 --- 31 files changed, 104 insertions(+), 491 deletions(-) delete mode 100644 src/Polyfills/IsExternalInit.cs delete mode 100644 src/Polyfills/Nullable.cs delete mode 100644 src/Riok.Mapperly.Abstractions/ReferenceHandling/Internal/ReferenceEqualityComparer.cs create mode 100644 src/Riok.Mapperly/Helpers/FieldSymbolEqualityComparer.cs delete mode 100644 src/Riok.Mapperly/Helpers/HashHelper.cs delete mode 100644 src/Riok.Mapperly/Helpers/PriorityQueue.cs create mode 100644 src/Riok.Mapperly/Helpers/PriorityQueueExtensions.cs delete mode 100644 src/Riok.Mapperly/Helpers/ReadOnlyDictionaryExtensions.cs delete mode 100644 test/Riok.Mapperly.Abstractions.Tests/ReferenceHandling/Internal/ReferenceEqualityComparerTest.cs rename test/Riok.Mapperly.Tests/Helpers/{PriorityQueueTest.cs => PriorityQueueExtensionsTest.cs} (56%) delete mode 100644 test/Riok.Mapperly.Tests/Helpers/ReadOnlyDictionaryExtensionsTest.cs diff --git a/benchmarks/Riok.Mapperly.Benchmarks/MappingBenchmarks.cs b/benchmarks/Riok.Mapperly.Benchmarks/MappingBenchmarks.cs index bf6c558df4..c9e0b86ad9 100644 --- a/benchmarks/Riok.Mapperly.Benchmarks/MappingBenchmarks.cs +++ b/benchmarks/Riok.Mapperly.Benchmarks/MappingBenchmarks.cs @@ -8,7 +8,6 @@ namespace Riok.Mapperly.Benchmarks; [MemoryDiagnoser] [InProcess] -[HtmlExporter] [JsonExporterAttribute.Full] public class MappingBenchmarks { diff --git a/benchmarks/Riok.Mapperly.Benchmarks/Riok.Mapperly.Benchmarks.csproj b/benchmarks/Riok.Mapperly.Benchmarks/Riok.Mapperly.Benchmarks.csproj index f651de69d2..e44fe84813 100644 --- a/benchmarks/Riok.Mapperly.Benchmarks/Riok.Mapperly.Benchmarks.csproj +++ b/benchmarks/Riok.Mapperly.Benchmarks/Riok.Mapperly.Benchmarks.csproj @@ -23,9 +23,15 @@ - - - + + TargetFramework=netstandard2.0 + + + TargetFramework=netstandard2.0 + + + TargetFramework=netstandard2.0 + diff --git a/benchmarks/Riok.Mapperly.Benchmarks/SourceGeneratorBenchmarks.cs b/benchmarks/Riok.Mapperly.Benchmarks/SourceGeneratorBenchmarks.cs index e2047ea913..f363633ac7 100644 --- a/benchmarks/Riok.Mapperly.Benchmarks/SourceGeneratorBenchmarks.cs +++ b/benchmarks/Riok.Mapperly.Benchmarks/SourceGeneratorBenchmarks.cs @@ -10,7 +10,6 @@ namespace Riok.Mapperly.Benchmarks; [MemoryDiagnoser] [InProcess] -[HtmlExporter] [JsonExporterAttribute.Full] public class SourceGeneratorBenchmarks { diff --git a/build/package.sh b/build/package.sh index 65a636d7a1..7beb381571 100755 --- a/build/package.sh +++ b/build/package.sh @@ -18,6 +18,7 @@ for roslyn_version in "${roslyn_versions[@]}"; do dotnet pack \ "${script_dir}/../src/Riok.Mapperly" \ -c Release \ + /p:TargetFrameworks="netstandard2.0" \ /p:ROSLYN_VERSION="${roslyn_version}" \ -o "${artifacts_dir}/roslyn-${roslyn_version}" \ /p:Version="${RELEASE_VERSION}" \ diff --git a/src/Directory.Build.props b/src/Directory.Build.props index 49de6fc194..aa6b723c0a 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -3,39 +3,34 @@ - netstandard2.0 + netstandard2.0;net7.0 embedded true true 11 - - - <_Parameter1>Riok.Mapperly.IntegrationTests - <_Parameter1>$(MSBuildProjectName).Tests - - - - - - all runtime; build; native; contentfiles; analyzers + + all + runtime; build; native; contentfiles; analyzers + diff --git a/src/Polyfills/IsExternalInit.cs b/src/Polyfills/IsExternalInit.cs deleted file mode 100644 index e5db0e701c..0000000000 --- a/src/Polyfills/IsExternalInit.cs +++ /dev/null @@ -1,16 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -#if !NET6_0_OR_GREATER -using System.ComponentModel; - -namespace System.Runtime.CompilerServices; - -/// -/// Reserved to be used by the compiler for tracking metadata. -/// This class should not be used by developers in source code. -/// -[EditorBrowsable(EditorBrowsableState.Never)] -internal static class IsExternalInit { } - -#endif diff --git a/src/Polyfills/Nullable.cs b/src/Polyfills/Nullable.cs deleted file mode 100644 index 7ba1508bc3..0000000000 --- a/src/Polyfills/Nullable.cs +++ /dev/null @@ -1,212 +0,0 @@ -// https://github.com/dotnet/runtime/blob/527f9ae88a0ee216b44d556f9bdc84037fe0ebda/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs - -#pragma warning disable -#define INTERNAL_NULLABLE_ATTRIBUTES - -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace System.Diagnostics.CodeAnalysis; - -#if NETSTANDARD2_0 || NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472 || NET48 -/// Specifies that null is allowed as an input even if the corresponding type disallows it. -[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] -#if SYSTEM_PRIVATE_CORELIB - public -#else -internal -#endif -sealed class AllowNullAttribute : Attribute { } - -/// Specifies that null is disallowed as an input even if the corresponding type allows it. -[AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] -#if SYSTEM_PRIVATE_CORELIB - public -#else -internal -#endif -sealed class DisallowNullAttribute : Attribute { } - -/// Specifies that an output may be null even if the corresponding type disallows it. -[AttributeUsage( - AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, - Inherited = false -)] -#if SYSTEM_PRIVATE_CORELIB - public -#else -internal -#endif -sealed class MaybeNullAttribute : Attribute { } - -/// Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input argument was not null when the call returns. -[AttributeUsage( - AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, - Inherited = false -)] -#if SYSTEM_PRIVATE_CORELIB - public -#else -internal -#endif -sealed class NotNullAttribute : Attribute { } - -/// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. -[AttributeUsage(AttributeTargets.Parameter, Inherited = false)] -#if SYSTEM_PRIVATE_CORELIB - public -#else -internal -#endif -sealed class MaybeNullWhenAttribute : Attribute -{ - /// Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter may be null. - /// - public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// Gets the return value condition. - public bool ReturnValue { get; } -} - -/// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. -[AttributeUsage(AttributeTargets.Parameter, Inherited = false)] -#if SYSTEM_PRIVATE_CORELIB - public -#else -internal -#endif -sealed class NotNullWhenAttribute : Attribute -{ - /// Initializes the attribute with the specified return value condition. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; - - /// Gets the return value condition. - public bool ReturnValue { get; } -} - -/// Specifies that the output will be non-null if the named parameter is non-null. -[AttributeUsage( - AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, - AllowMultiple = true, - Inherited = false -)] -#if SYSTEM_PRIVATE_CORELIB - public -#else -internal -#endif -sealed class NotNullIfNotNullAttribute : Attribute -{ - /// Initializes the attribute with the associated parameter name. - /// - /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. - /// - public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; - - /// Gets the associated parameter name. - public string ParameterName { get; } -} - -/// Applied to a method that will never return under any circumstance. -[AttributeUsage(AttributeTargets.Method, Inherited = false)] -#if SYSTEM_PRIVATE_CORELIB - public -#else -internal -#endif -sealed class DoesNotReturnAttribute : Attribute { } - -/// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. -[AttributeUsage(AttributeTargets.Parameter, Inherited = false)] -#if SYSTEM_PRIVATE_CORELIB - public -#else -internal -#endif -sealed class DoesNotReturnIfAttribute : Attribute -{ - /// Initializes the attribute with the specified parameter value. - /// - /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to - /// the associated parameter matches this value. - /// - public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; - - /// Gets the condition parameter value. - public bool ParameterValue { get; } -} -#endif - -#if NETSTANDARD2_0 || NETCOREAPP2_0 || NETCOREAPP2_1 || NETCOREAPP2_2 || NETCOREAPP3_0 || NETCOREAPP3_1 || NET45 || NET451 || NET452 || NET46 || NET461 || NET462 || NET47 || NET471 || NET472 || NET48 -/// Specifies that the method or property will ensure that the listed field and property members have not-null values. -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] -#if SYSTEM_PRIVATE_CORELIB - public -#else -internal -#endif -sealed class MemberNotNullAttribute : Attribute -{ - /// Initializes the attribute with a field or property member. - /// - /// The field or property member that is promised to be not-null. - /// - public MemberNotNullAttribute(string member) => Members = new[] { member }; - - /// Initializes the attribute with the list of field and property members. - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullAttribute(params string[] members) => Members = members; - - /// Gets field or property member names. - public string[] Members { get; } -} - -/// Specifies that the method or property will ensure that the listed field and property members have not-null values when returning with the specified return value condition. -[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)] -#if SYSTEM_PRIVATE_CORELIB - public -#else -internal -#endif -sealed class MemberNotNullWhenAttribute : Attribute -{ - /// Initializes the attribute with the specified return value condition and a field or property member. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - /// - /// The field or property member that is promised to be not-null. - /// - public MemberNotNullWhenAttribute(bool returnValue, string member) - { - ReturnValue = returnValue; - Members = new[] { member }; - } - - /// Initializes the attribute with the specified return value condition and list of field and property members. - /// - /// The return value condition. If the method returns this value, the associated parameter will not be null. - /// - /// - /// The list of field and property members that are promised to be not-null. - /// - public MemberNotNullWhenAttribute(bool returnValue, params string[] members) - { - ReturnValue = returnValue; - Members = members; - } - - /// Gets the return value condition. - public bool ReturnValue { get; } - - /// Gets field or property member names. - public string[] Members { get; } -} -#endif diff --git a/src/Riok.Mapperly.Abstractions/ReferenceHandling/Internal/PreserveReferenceHandler.cs b/src/Riok.Mapperly.Abstractions/ReferenceHandling/Internal/PreserveReferenceHandler.cs index f043e1e832..efa5159eba 100644 --- a/src/Riok.Mapperly.Abstractions/ReferenceHandling/Internal/PreserveReferenceHandler.cs +++ b/src/Riok.Mapperly.Abstractions/ReferenceHandling/Internal/PreserveReferenceHandler.cs @@ -37,7 +37,7 @@ private ReferenceHolder GetReferenceHolder() private class ReferenceHolder { - private readonly Dictionary _references = new(ReferenceEqualityComparer.Instance); + private readonly Dictionary _references = new(ReferenceEqualityComparer.Instance); public bool TryGetRef(TSource source, [NotNullWhen(true)] out TTarget? target) where TSource : notnull diff --git a/src/Riok.Mapperly.Abstractions/ReferenceHandling/Internal/ReferenceEqualityComparer.cs b/src/Riok.Mapperly.Abstractions/ReferenceHandling/Internal/ReferenceEqualityComparer.cs deleted file mode 100644 index c1af3a78e0..0000000000 --- a/src/Riok.Mapperly.Abstractions/ReferenceHandling/Internal/ReferenceEqualityComparer.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Runtime.CompilerServices; - -namespace Riok.Mapperly.Abstractions.ReferenceHandling.Internal; - -/// -/// Defines methods to support the comparison of objects for reference equality. -/// -/// The type of objects to compare. -internal sealed class ReferenceEqualityComparer : IEqualityComparer -{ - // cannot use System.Collections.Generic.ReferenceEqualityComparer since it is not available in netstandard2.0 - - /// - /// A instance. - /// - public static readonly IEqualityComparer Instance = new ReferenceEqualityComparer(); - - private ReferenceEqualityComparer() { } - - bool IEqualityComparer.Equals(T? x, T? y) => ReferenceEquals(x, y); - - int IEqualityComparer.GetHashCode(T obj) => RuntimeHelpers.GetHashCode(obj); -} diff --git a/src/Riok.Mapperly/Configuration/AttributeDataAccessor.cs b/src/Riok.Mapperly/Configuration/AttributeDataAccessor.cs index 797ce49b51..7b2a8e0bff 100644 --- a/src/Riok.Mapperly/Configuration/AttributeDataAccessor.cs +++ b/src/Riok.Mapperly/Configuration/AttributeDataAccessor.cs @@ -21,7 +21,8 @@ public T AccessSingle(ISymbol symbol) where T : Attribute => Access(symbol).Single(); public TData? AccessFirstOrDefault(ISymbol symbol) - where TAttribute : Attribute => Access(symbol).FirstOrDefault(); + where TAttribute : Attribute + where TData : notnull => Access(symbol).FirstOrDefault(); public IEnumerable Access(ISymbol symbol) where TAttribute : Attribute => Access(symbol); @@ -39,6 +40,7 @@ public IEnumerable Access(ISymbol symbol) /// If a property or ctor argument of could not be read on the attribute. public IEnumerable Access(ISymbol symbol) where TAttribute : Attribute + where TData : notnull { var attrType = typeof(TAttribute); var dataType = typeof(TData); @@ -64,6 +66,7 @@ public IEnumerable Access(ISymbol symbol) } private TData Create(IReadOnlyCollection typeArguments, IReadOnlyCollection constructorArguments) + where TData : notnull { // The data class should have a constructor // with generic type parameters of the attribute class @@ -81,7 +84,8 @@ private TData Create(IReadOnlyCollection typeArguments, IRea (arg, i) => BuildArgumentValue(arg, parameters[i + typeArguments.Count].ParameterType) ); var constructorTypeAndValueArguments = typeArguments.Concat(constructorArgumentValues).ToArray(); - return (TData)Activator.CreateInstance(typeof(TData), constructorTypeAndValueArguments); + return (TData?)Activator.CreateInstance(typeof(TData), constructorTypeAndValueArguments) + ?? throw new InvalidOperationException($"Could not create instance of {typeof(TData)}"); } throw new InvalidOperationException($"{typeof(TData)} does not have a constructor with {argCount} parameters"); diff --git a/src/Riok.Mapperly/Descriptors/MappingBuilders/EnumMappingBuilder.cs b/src/Riok.Mapperly/Descriptors/MappingBuilders/EnumMappingBuilder.cs index 5762172a7e..5a4c8a09fc 100644 --- a/src/Riok.Mapperly/Descriptors/MappingBuilders/EnumMappingBuilder.cs +++ b/src/Riok.Mapperly/Descriptors/MappingBuilders/EnumMappingBuilder.cs @@ -123,18 +123,27 @@ private static EnumMemberMappings BuildEnumMemberMappings( Func propertySelector, params IEqualityComparer[] propertyComparer ) + where T : notnull { var ignoredSourceMembers = ignoreExplicitAndIgnoredMappings ? new HashSet(SymbolEqualityComparer.Default) - : ctx.Configuration.Enum.IgnoredSourceMembers.ToHashSet(); + : ctx.Configuration.Enum.IgnoredSourceMembers.ToHashSet(FieldSymbolEqualityComparer.Default); var ignoredTargetMembers = ignoreExplicitAndIgnoredMappings ? new HashSet(SymbolEqualityComparer.Default) - : ctx.Configuration.Enum.IgnoredTargetMembers.ToHashSet(); + : ctx.Configuration.Enum.IgnoredTargetMembers.ToHashSet(FieldSymbolEqualityComparer.Default); var explicitMappings = ignoreExplicitAndIgnoredMappings ? new Dictionary(SymbolEqualityComparer.Default) : BuildExplicitValueMappings(ctx); - var sourceMembers = ctx.Source.GetMembers().OfType().Where(x => !ignoredSourceMembers.Remove(x)).ToHashSet(); - var targetMembers = ctx.Target.GetMembers().OfType().Where(x => !ignoredTargetMembers.Remove(x)).ToHashSet(); + var sourceMembers = ctx.Source + .GetMembers() + .OfType() + .Where(x => !ignoredSourceMembers.Remove(x)) + .ToHashSet(FieldSymbolEqualityComparer.Default); + var targetMembers = ctx.Target + .GetMembers() + .OfType() + .Where(x => !ignoredTargetMembers.Remove(x)) + .ToHashSet(FieldSymbolEqualityComparer.Default); var targetMembersByProperty = propertyComparer .Select(pc => targetMembers.DistinctBy(propertySelector, pc).ToDictionary(propertySelector, x => x, pc)) diff --git a/src/Riok.Mapperly/Descriptors/Mappings/MappingBodyBuildingPriority.cs b/src/Riok.Mapperly/Descriptors/Mappings/MappingBodyBuildingPriority.cs index 9e0d0d2d3a..0909605bc4 100644 --- a/src/Riok.Mapperly/Descriptors/Mappings/MappingBodyBuildingPriority.cs +++ b/src/Riok.Mapperly/Descriptors/Mappings/MappingBodyBuildingPriority.cs @@ -8,13 +8,13 @@ namespace Riok.Mapperly.Descriptors.Mappings; public enum MappingBodyBuildingPriority { /// - /// Priority for mappings which require the body of user mappings to be built. - /// (Depend on the user mapping bodies). + /// Default mapping priority. /// - AfterUserMappings, + Default, /// - /// Default mapping priority. + /// Priority for mappings which require the body of user mappings to be built. + /// (Depend on the user mapping bodies). /// - Default, + AfterUserMappings, } diff --git a/src/Riok.Mapperly/Emit/SyntaxFactoryHelper.cs b/src/Riok.Mapperly/Emit/SyntaxFactoryHelper.cs index 93923452a1..1393175192 100644 --- a/src/Riok.Mapperly/Emit/SyntaxFactoryHelper.cs +++ b/src/Riok.Mapperly/Emit/SyntaxFactoryHelper.cs @@ -129,7 +129,7 @@ public static InterpolatedStringExpressionSyntax InterpolatedString(FormattableS { ExpressionSyntax x => Interpolation(x), string x => InterpolatedStringText(x), - _ => throw new InvalidOperationException(arg.GetType() + " cannot be converted into a string interpolation"), + _ => throw new InvalidOperationException(arg?.GetType() + " cannot be converted into a string interpolation"), }; contents.Add(argSyntax); previousIndex = match.Index + match.Length; diff --git a/src/Riok.Mapperly/Helpers/DictionaryExtensions.cs b/src/Riok.Mapperly/Helpers/DictionaryExtensions.cs index 187ea6adcd..83423350b9 100644 --- a/src/Riok.Mapperly/Helpers/DictionaryExtensions.cs +++ b/src/Riok.Mapperly/Helpers/DictionaryExtensions.cs @@ -1,8 +1,10 @@ +using System.Diagnostics.CodeAnalysis; + namespace Riok.Mapperly.Helpers; public static class DictionaryExtensions { - public static bool Remove(this IDictionary dict, TKey key, out TValue value) + public static bool Remove(this IDictionary dict, TKey key, [MaybeNullWhen(false)] out TValue value) { if (!dict.TryGetValue(key, out value)) return false; diff --git a/src/Riok.Mapperly/Helpers/EnumerableExtensions.cs b/src/Riok.Mapperly/Helpers/EnumerableExtensions.cs index 74572cbbc2..3f5353ccfd 100644 --- a/src/Riok.Mapperly/Helpers/EnumerableExtensions.cs +++ b/src/Riok.Mapperly/Helpers/EnumerableExtensions.cs @@ -18,25 +18,6 @@ public static IEnumerable WhereNotNull(this IEnumerable enumerable) #nullable restore } - public static HashSet ToHashSet(this IEnumerable enumerable, IEqualityComparer? comparer = null) => - new(enumerable, comparer); - - public static IEnumerable DistinctBy( - this IEnumerable enumerable, - Func selector, - IEqualityComparer? equalityComparer = null - ) - { - var set = new HashSet(equalityComparer); - foreach (var item in enumerable) - { - if (set.Add(selector(item))) - { - yield return item; - } - } - } - public static IEnumerable SkipLast(this IEnumerable enumerable) { using var enumerator = enumerable.GetEnumerator(); diff --git a/src/Riok.Mapperly/Helpers/FieldSymbolEqualityComparer.cs b/src/Riok.Mapperly/Helpers/FieldSymbolEqualityComparer.cs new file mode 100644 index 0000000000..c69ee920b7 --- /dev/null +++ b/src/Riok.Mapperly/Helpers/FieldSymbolEqualityComparer.cs @@ -0,0 +1,8 @@ +using Microsoft.CodeAnalysis; + +namespace Riok.Mapperly.Helpers; + +internal static class FieldSymbolEqualityComparer +{ + public static IEqualityComparer Default = SymbolEqualityComparer.Default; +} diff --git a/src/Riok.Mapperly/Helpers/HashHelper.cs b/src/Riok.Mapperly/Helpers/HashHelper.cs deleted file mode 100644 index ad83804cfc..0000000000 --- a/src/Riok.Mapperly/Helpers/HashHelper.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace Riok.Mapperly.Helpers; - -internal class HashHelper -{ - public static int Combine(int h1, int h2) - { - // RyuJIT optimizes this to use the ROL instruction - // Related GitHub pull request: https://github.com/dotnet/coreclr/pull/1830 - uint rol5 = ((uint)h1 << 5) | ((uint)h1 >> 27); - return ((int)rol5 + h1) ^ h2; - } -} diff --git a/src/Riok.Mapperly/Helpers/PriorityQueue.cs b/src/Riok.Mapperly/Helpers/PriorityQueue.cs deleted file mode 100644 index 3992793ed8..0000000000 --- a/src/Riok.Mapperly/Helpers/PriorityQueue.cs +++ /dev/null @@ -1,50 +0,0 @@ -namespace Riok.Mapperly.Helpers; - -/// -/// A simple implementation of a priority queue. -/// -/// The type of the elements. -/// The priority of an element. -public class PriorityQueue -{ - private readonly Dictionary> _nodes = new(); - private readonly SortedSet _knownPriorities = new(); - - public void Enqueue(TElement element, TPriority priority) - { - if (_nodes.TryGetValue(priority, out var node)) - { - node.Enqueue(element); - return; - } - - var queue = new Queue(); - queue.Enqueue(element); - _nodes[priority] = queue; - _knownPriorities.Add(priority); - } - - /// - /// Dequeues all nodes. - /// The nodes with the highest priority are returned first ordered in a FIFO fashion. - /// Items added while this operation is in progress are also considered. - /// - /// An enumerable with all items. - public IEnumerable DequeueAll() - { - while (_knownPriorities.Count > 0) - { - var priority = _knownPriorities.Max; - var queue = _nodes[priority]; - var item = queue.Dequeue(); - - if (queue.Count == 0) - { - _nodes.Remove(priority); - _knownPriorities.Remove(priority); - } - - yield return item; - } - } -} diff --git a/src/Riok.Mapperly/Helpers/PriorityQueueExtensions.cs b/src/Riok.Mapperly/Helpers/PriorityQueueExtensions.cs new file mode 100644 index 0000000000..197fbdd28d --- /dev/null +++ b/src/Riok.Mapperly/Helpers/PriorityQueueExtensions.cs @@ -0,0 +1,18 @@ +namespace Riok.Mapperly.Helpers; + +internal static class PriorityQueueExtensions +{ + /// + /// Dequeues all nodes. + /// The nodes with the highest priority are returned first ordered in a FIFO fashion. + /// Items added while this operation is in progress are also considered. + /// + /// An enumerable with all items. + public static IEnumerable DequeueAll(this PriorityQueue queue) + { + while (queue.TryDequeue(out var element, out _)) + { + yield return element; + } + } +} diff --git a/src/Riok.Mapperly/Helpers/ReadOnlyDictionaryExtensions.cs b/src/Riok.Mapperly/Helpers/ReadOnlyDictionaryExtensions.cs deleted file mode 100644 index 21164f4287..0000000000 --- a/src/Riok.Mapperly/Helpers/ReadOnlyDictionaryExtensions.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Riok.Mapperly.Helpers; - -internal static class ReadOnlyDictionaryExtensions -{ - public static TValue? GetValueOrDefault(this IReadOnlyDictionary dict, TKey key) - { - dict.TryGetValue(key, out var value); - return value; - } -} diff --git a/src/Riok.Mapperly/Helpers/SymbolExtensions.cs b/src/Riok.Mapperly/Helpers/SymbolExtensions.cs index e27b4bbc64..139b6db955 100644 --- a/src/Riok.Mapperly/Helpers/SymbolExtensions.cs +++ b/src/Riok.Mapperly/Helpers/SymbolExtensions.cs @@ -7,8 +7,8 @@ namespace Riok.Mapperly.Helpers; internal static class SymbolExtensions { private static readonly ImmutableHashSet _wellKnownImmutableTypes = ImmutableHashSet.Create( - typeof(Uri).FullName, - typeof(Version).FullName + typeof(Uri).FullName!, + typeof(Version).FullName! ); internal static bool IsImmutable(this ISymbol symbol) => diff --git a/src/Riok.Mapperly/ImmutableEquatableArray.cs b/src/Riok.Mapperly/ImmutableEquatableArray.cs index 95c1ee95b1..119171f87e 100644 --- a/src/Riok.Mapperly/ImmutableEquatableArray.cs +++ b/src/Riok.Mapperly/ImmutableEquatableArray.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections; -using Riok.Mapperly.Helpers; namespace Riok.Mapperly; @@ -26,13 +25,13 @@ public sealed class ImmutableEquatableArray : IEquatable new Enumerator(_values); diff --git a/src/Riok.Mapperly/MapperGenerator.cs b/src/Riok.Mapperly/MapperGenerator.cs index b46f73bdc4..9ab0285259 100644 --- a/src/Riok.Mapperly/MapperGenerator.cs +++ b/src/Riok.Mapperly/MapperGenerator.cs @@ -19,7 +19,7 @@ public class MapperGenerator : IIncrementalGenerator public const string AddMappersStep = "ImplementationSourceOutput"; public const string ReportDiagnosticsStep = "Diagnostics"; - public static readonly string MapperAttributeName = typeof(MapperAttribute).FullName; + public static readonly string MapperAttributeName = typeof(MapperAttribute).FullName!; public void Initialize(IncrementalGeneratorInitializationContext context) { diff --git a/src/Riok.Mapperly/Riok.Mapperly.csproj b/src/Riok.Mapperly/Riok.Mapperly.csproj index 4e05d46562..0d68fa01db 100644 --- a/src/Riok.Mapperly/Riok.Mapperly.csproj +++ b/src/Riok.Mapperly/Riok.Mapperly.csproj @@ -48,13 +48,17 @@ - - + + - - - - + + + + diff --git a/src/Riok.Mapperly/Symbols/FieldMember.cs b/src/Riok.Mapperly/Symbols/FieldMember.cs index b63a20cef7..6fc08b1167 100644 --- a/src/Riok.Mapperly/Symbols/FieldMember.cs +++ b/src/Riok.Mapperly/Symbols/FieldMember.cs @@ -28,7 +28,7 @@ public bool IsRequired => false; #endif - public override bool Equals(object obj) => + public override bool Equals(object? obj) => obj is FieldMember other && SymbolEqualityComparer.IncludeNullability.Equals(_fieldSymbol, other._fieldSymbol); public override int GetHashCode() => SymbolEqualityComparer.IncludeNullability.GetHashCode(_fieldSymbol); diff --git a/src/Riok.Mapperly/Symbols/PropertyMember.cs b/src/Riok.Mapperly/Symbols/PropertyMember.cs index 4b98e2f0cb..5f4c24ad68 100644 --- a/src/Riok.Mapperly/Symbols/PropertyMember.cs +++ b/src/Riok.Mapperly/Symbols/PropertyMember.cs @@ -33,7 +33,7 @@ public bool IsRequired => false; #endif - public override bool Equals(object obj) => + public override bool Equals(object? obj) => obj is PropertyMember other && SymbolEqualityComparer.IncludeNullability.Equals(_propertySymbol, other._propertySymbol); public override int GetHashCode() => SymbolEqualityComparer.IncludeNullability.GetHashCode(_propertySymbol); diff --git a/test/Riok.Mapperly.Abstractions.Tests/ReferenceHandling/Internal/ReferenceEqualityComparerTest.cs b/test/Riok.Mapperly.Abstractions.Tests/ReferenceHandling/Internal/ReferenceEqualityComparerTest.cs deleted file mode 100644 index 47728749d9..0000000000 --- a/test/Riok.Mapperly.Abstractions.Tests/ReferenceHandling/Internal/ReferenceEqualityComparerTest.cs +++ /dev/null @@ -1,44 +0,0 @@ -using Riok.Mapperly.Abstractions.ReferenceHandling.Internal; - -namespace Riok.Mapperly.Abstractions.Tests.ReferenceHandling.Internal; - -public class ReferenceEqualityComparerTest -{ - [Fact] - public void PrimitivesShouldNotBeEqual() - { - ReferenceEqualityComparer.Instance.Equals(20, 10).Should().BeFalse(); - ReferenceEqualityComparer.Instance.GetHashCode(10).Should().NotBe(ReferenceEqualityComparer.Instance.GetHashCode(10)); - } - - [Fact] - public void InternedStringsShouldBeEqual() - { - ReferenceEqualityComparer.Instance.Equals(string.Intern("fooBar"), string.Intern("fooBar")).Should().BeTrue(); - ReferenceEqualityComparer.Instance - .GetHashCode(string.Intern("fooBar")) - .Should() - .Be(ReferenceEqualityComparer.Instance.GetHashCode(string.Intern("fooBar"))); - } - - [Fact] - public void SameObjectRefShouldBeEqual() - { - var obj = new object(); - - ReferenceEqualityComparer.Instance.Equals(obj, obj).Should().BeTrue(); - - ReferenceEqualityComparer.Instance.GetHashCode(obj).Should().Be(ReferenceEqualityComparer.Instance.GetHashCode(obj)); - } - - [Fact] - public void DifferentObjectRefShouldNotBeEqual() - { - ReferenceEqualityComparer.Instance.Equals(new object(), new object()).Should().BeFalse(); - - ReferenceEqualityComparer.Instance - .GetHashCode(new object()) - .Should() - .NotBe(ReferenceEqualityComparer.Instance.GetHashCode(new object())); - } -} diff --git a/test/Riok.Mapperly.IntegrationTests/Riok.Mapperly.IntegrationTests.csproj b/test/Riok.Mapperly.IntegrationTests/Riok.Mapperly.IntegrationTests.csproj index c49551a155..2d91e65a54 100644 --- a/test/Riok.Mapperly.IntegrationTests/Riok.Mapperly.IntegrationTests.csproj +++ b/test/Riok.Mapperly.IntegrationTests/Riok.Mapperly.IntegrationTests.csproj @@ -34,8 +34,13 @@ + + + all + runtime; build; native; contentfiles; analyzers + diff --git a/test/Riok.Mapperly.Tests/Helpers/EnumerableExtensionsTest.cs b/test/Riok.Mapperly.Tests/Helpers/EnumerableExtensionsTest.cs index 5c708e8baa..f29a036d7a 100644 --- a/test/Riok.Mapperly.Tests/Helpers/EnumerableExtensionsTest.cs +++ b/test/Riok.Mapperly.Tests/Helpers/EnumerableExtensionsTest.cs @@ -22,29 +22,6 @@ public void WhereNotNullShouldFilterNulls() .BeEquivalentTo(new[] { "a", "b", "c", "d", "e" }, o => o.WithStrictOrdering()); } - [Fact] - public void ToHashSetShouldWork() - { - var items = new[] { 1, 1, 2, 3, 4, 5, 5 }; - - // can't use extension method due to ambiguous method reference. - // (no support for this method in netstandard2.0) - var hashSet = EnumerableExtensions.ToHashSet(items); - hashSet.Should().BeEquivalentTo(new[] { 1, 2, 3, 4, 5 }); - } - - [Fact] - public void DistinctByShouldWork() - { - var items = new[] { ("item10", 10), ("item11", 10), ("item12", 10), ("item20", 20), ("item30", 30), ("item31", 30), }; - - items - .DistinctBy(x => x.Item2) - .Select(x => x.Item1) - .Should() - .BeEquivalentTo(new[] { "item10", "item20", "item30" }, o => o.WithStrictOrdering()); - } - [Fact] public void SkipLastShouldWork() { diff --git a/test/Riok.Mapperly.Tests/Helpers/PriorityQueueTest.cs b/test/Riok.Mapperly.Tests/Helpers/PriorityQueueExtensionsTest.cs similarity index 56% rename from test/Riok.Mapperly.Tests/Helpers/PriorityQueueTest.cs rename to test/Riok.Mapperly.Tests/Helpers/PriorityQueueExtensionsTest.cs index 73f192f4d2..917f7817cc 100644 --- a/test/Riok.Mapperly.Tests/Helpers/PriorityQueueTest.cs +++ b/test/Riok.Mapperly.Tests/Helpers/PriorityQueueExtensionsTest.cs @@ -1,13 +1,15 @@ +using Riok.Mapperly.Helpers; + namespace Riok.Mapperly.Tests.Helpers; -public class PriorityQueueTest +public class PriorityQueueExtensionsTest { [Fact] - public void EnqueueAndDequeueAllShouldWork() + public void DequeueAllShouldWork() { - var queue = new Mapperly.Helpers.PriorityQueue(); - queue.Enqueue('C', 1); - queue.Enqueue('A', 3); + var queue = new PriorityQueue(); + queue.Enqueue('C', 3); + queue.Enqueue('A', 1); queue.Enqueue('B', 2); var index = 0; @@ -19,9 +21,9 @@ public void EnqueueAndDequeueAllShouldWork() // enqueue during dequeue if (index == 2) { - queue.Enqueue('E', 0); - queue.Enqueue('D', 1); - queue.Enqueue('F', 0); + queue.Enqueue('F', 1); + queue.Enqueue('D', 0); + queue.Enqueue('E', 1); } index++; diff --git a/test/Riok.Mapperly.Tests/Helpers/ReadOnlyDictionaryExtensionsTest.cs b/test/Riok.Mapperly.Tests/Helpers/ReadOnlyDictionaryExtensionsTest.cs deleted file mode 100644 index c087ad72fb..0000000000 --- a/test/Riok.Mapperly.Tests/Helpers/ReadOnlyDictionaryExtensionsTest.cs +++ /dev/null @@ -1,29 +0,0 @@ -using Riok.Mapperly.Helpers; - -namespace Riok.Mapperly.Tests.Helpers; - -// can't use extension methods due to ambiguous method reference. -// (no support for this method in netstandard2.0) -public class ReadOnlyDictionaryExtensionsTest -{ - [Fact] - public void GetValueOrDefaultShouldReturnValueIfFound() - { - var d = new Dictionary { ["a"] = 10, ["b"] = 20, }; - ReadOnlyDictionaryExtensions.GetValueOrDefault(d, "a").Should().Be(10); - } - - [Fact] - public void GetValueOrDefaultShouldReturnDefaultForPrimitiveIfNotFound() - { - var d = new Dictionary { ["a"] = 10, ["b"] = 20, }; - ReadOnlyDictionaryExtensions.GetValueOrDefault(d, "c").Should().Be(0); - } - - [Fact] - public void GetValueOrDefaultShouldReturnDefaultForReferenceTypeIfNotFound() - { - var d = new Dictionary { ["a"] = new(), ["b"] = new(), }; - ReadOnlyDictionaryExtensions.GetValueOrDefault(d, "c").Should().BeNull(); - } -}