diff --git a/src/Vogen/GenerateCodeForHashCodes.cs b/src/Vogen/GenerateCodeForHashCodes.cs index 724217f981..bdea6ceb97 100644 --- a/src/Vogen/GenerateCodeForHashCodes.cs +++ b/src/Vogen/GenerateCodeForHashCodes.cs @@ -1,3 +1,5 @@ +using Analyzer.Utilities.Extensions; + namespace Vogen; public static class GenerateCodeForHashCodes @@ -11,6 +13,13 @@ public static string GenerateGetHashCodeForAClass(VoWorkItem item) string itemUnderlyingType = item.UnderlyingTypeFullNameWithGlobalAlias; + // When the underlying type is a nullable value type (e.g. ushort?), EqualityComparer.Default.GetHashCode + // has [DisallowNull] on its parameter in .NET 10+, causing CS8607. Use null-safe approach instead. + // Wrap the expression in parentheses to ensure correct precedence with the ^ operator. + string hashCodeExpression = item.UnderlyingType.IsNullableValueType() + ? "(Value is null ? 0 : Value.GetHashCode())" + : $"global::System.Collections.Generic.EqualityComparer<{itemUnderlyingType}>.Default.GetHashCode(Value)"; + return $$""" @@ -20,7 +29,7 @@ public static string GenerateGetHashCodeForAClass(VoWorkItem item) { global::System.Int32 hash = (global::System.Int32) 2166136261; hash = (hash * 16777619) ^ GetType().GetHashCode(); - hash = (hash * 16777619) ^ global::System.Collections.Generic.EqualityComparer<{{itemUnderlyingType}}>.Default.GetHashCode(Value); + hash = (hash * 16777619) ^ {{hashCodeExpression}}; return hash; } } @@ -36,12 +45,18 @@ public static string GenerateForAStruct(VoWorkItem item) string itemUnderlyingType = item.UnderlyingTypeFullNameWithGlobalAlias; + // When the underlying type is a nullable value type (e.g. ushort?), EqualityComparer.Default.GetHashCode + // has [DisallowNull] on its parameter in .NET 10+, causing CS8607. Use null-safe approach instead. + string hashCodeExpression = item.UnderlyingType.IsNullableValueType() + ? "Value is null ? 0 : Value.GetHashCode()" + : $"global::System.Collections.Generic.EqualityComparer<{itemUnderlyingType}>.Default.GetHashCode(Value)"; + return $$""" public readonly override global::System.Int32 GetHashCode() { - return global::System.Collections.Generic.EqualityComparer<{{itemUnderlyingType}}>.Default.GetHashCode(Value); + return {{hashCodeExpression}}; } """; } diff --git a/tests/SnapshotTests/BugFixes/Bug914_NullableValueType_GetHashCode.cs b/tests/SnapshotTests/BugFixes/Bug914_NullableValueType_GetHashCode.cs new file mode 100644 index 0000000000..8fa2e6d88b --- /dev/null +++ b/tests/SnapshotTests/BugFixes/Bug914_NullableValueType_GetHashCode.cs @@ -0,0 +1,40 @@ +using System.Threading.Tasks; +using Vogen; + +namespace SnapshotTests.BugFixes; + +// See https://github.com/SteveDunn/Vogen/issues/914 +// ValueObject with a nullable value type underlying (e.g. ushort?) generated CS8607 because +// EqualityComparer.Default.GetHashCode has [DisallowNull] in .NET 10+. +public class Bug914Tests +{ + [Theory] + [InlineData("partial struct")] + [InlineData("readonly partial struct")] + [InlineData("partial class")] + [InlineData("partial record class")] + [InlineData("partial record struct")] + [InlineData("readonly partial record struct")] + public async Task NullableValueType_underlying_generates_null_safe_GetHashCode(string type) + { + var source = $$""" + using Vogen; + + namespace ConsumerTests; + + [ValueObject] + internal {{type}} Speed + { + private const ushort UnknownSpeed = 255; + + private static ushort? NormalizeInput(ushort? input) => + input == UnknownSpeed ? null : input; + } + """; + + await new SnapshotRunner() + .WithSource(source) + .CustomizeSettings(s => s.UseHashedParameters(type)) + .RunOnAllFrameworks(); + } +} diff --git a/tests/SnapshotTests/BugFixes/snapshots/snap-v4.8/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_1ffb354a8af60055.verified.txt b/tests/SnapshotTests/BugFixes/snapshots/snap-v4.8/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_1ffb354a8af60055.verified.txt new file mode 100644 index 0000000000..8df3671f73 --- /dev/null +++ b/tests/SnapshotTests/BugFixes/snapshots/snap-v4.8/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_1ffb354a8af60055.verified.txt @@ -0,0 +1,397 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace generator +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory + { + public VogenTypesFactory() + { + } + + private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary> + { + { + typeof(global::ConsumerTests.Speed), + new global::System.Lazy(() => new global::ConsumerTests.Speed.SpeedSystemTextJsonConverter()) + } + }; + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value; + } +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace ConsumerTests +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + [global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] + [global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(SpeedDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.Nullable, Value = { _value }")] + internal partial class Speed : global::System.IEquatable, global::System.IEquatable> + { +#if DEBUG +private readonly global::System.Diagnostics.StackTrace _stackTrace = null!; +#endif +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + private readonly global::System.Nullable _value; + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public global::System.Nullable Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get + { + EnsureInitialized(); + return _value; + } + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Speed() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Speed(global::System.Nullable value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static Speed From(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + /// + /// Tries to build an instance from the provided underlying type. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, false will be returned. + /// + /// The underlying type. + /// An instance of the value object. + /// True if the value object can be built, otherwise false. + public static bool TryFrom( +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + global::System.Nullable value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] +#endif + out Speed vo) + { + value = Speed.NormalizeInput(value); + vo = new Speed(value); + return true; + } + + /// + /// Tries to build an instance from the provided underlying value. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, an error will be returned. + /// + /// The primitive value. + /// A containing either the value object, or an error. + public static global::Vogen.ValueObjectOrError TryFrom(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new global::Vogen.ValueObjectOrError(new Speed(value)); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION +#pragma warning disable CS8775 + public bool IsInitialized() => true; +#pragma warning restore CS8775 +#else + public bool IsInitialized() => _isInitialized; +#endif + // only called internally when something has been deserialized into + // its primitive type. + private static Speed __Deserialize(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + public global::System.Boolean Equals(Speed other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if (!IsInitialized() || !other.IsInitialized()) + return false; + if (ReferenceEquals(this, other)) + { + return true; + } + + return GetType() == other.GetType() && global::System.Collections.Generic.EqualityComparer>.Default.Equals(Value, other.Value); + } + + public global::System.Boolean Equals(Speed other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + public global::System.Boolean Equals(global::System.Nullable primitive) + { + return Value.Equals(primitive); + } + + public override global::System.Boolean Equals(global::System.Object obj) + { + return Equals(obj as Speed); + } + + public static global::System.Boolean operator ==(Speed left, Speed right) => Equals(left, right); + public static global::System.Boolean operator !=(Speed left, Speed right) => !Equals(left, right); + public static global::System.Boolean operator ==(Speed left, global::System.Nullable right) => left.Value.Equals(right); + public static global::System.Boolean operator ==(global::System.Nullable left, Speed right) => right.Value.Equals(left); + public static global::System.Boolean operator !=(global::System.Nullable left, Speed right) => !(left == right); + public static global::System.Boolean operator !=(Speed left, global::System.Nullable right) => !(left == right); + public static explicit operator Speed(global::System.Nullable value) => From(value); + public static explicit operator global::System.Nullable(Speed value) => value.Value; +#nullable disable +#nullable restore + public override global::System.Int32 GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + global::System.Int32 hash = (global::System.Int32)2166136261; + hash = (hash * 16777619) ^ GetType().GetHashCode(); + hash = (hash * 16777619) ^ (Value is null ? 0 : Value.GetHashCode()); + return hash; + } + } + + /// + public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]"; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + ThrowHelper.ThrowWhenNotInitialized(_stackTrace); +#else + ThrowHelper.ThrowWhenNotInitialized(); +#endif + } + } + +#nullable disable + /// + /// Converts a Speed to or from JSON. + /// + public partial class SpeedSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter + { + public override Speed Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, value.Value, options); + } + +#if NET6_0_OR_GREATER + public override Speed ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + writer.WritePropertyName(global::System.Text.Json.JsonSerializer.Serialize(value.Value)); + } +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation) + { + var e = ThrowHelper.CreateValidationException(validation); + throw new global::System.Text.Json.JsonException(null, e); + } + + private static Speed DeserializeJson(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + } + +#nullable restore +#nullable disable + class SpeedTypeConverter : global::System.ComponentModel.TypeConverter + { + public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value) + { + global::System.Nullable ut = (global::System.Nullable)value; + return Speed.__Deserialize(ut); + } + + public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType) + { + if (value is Speed idValue) + { + return idValue.Value; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + +#nullable restore + internal sealed class SpeedDebugView + { + private readonly Speed _t; + SpeedDebugView(Speed t) + { + _t = t; + } + + public global::System.String UnderlyingType => "global::System.Nullable"; + public global::System.Nullable Value => _t.Value; + public global::System.String Conversions => @"[global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] +[global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] +"; + } + + static class ThrowHelper + { +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException(); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object."); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? ""); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenValidationFails(global::Vogen.Validation validation) + { + throw CreateValidationException(validation); + } + + internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message); + internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null."); + internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation) + { + var ex = CreateValidationException(validation.ErrorMessage); + if (validation.Data != null) + { + foreach (var kvp in validation.Data) + { + ex.Data[kvp.Key] = kvp.Value; + } + } + + return ex; + } + } + } +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/BugFixes/snapshots/snap-v4.8/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_319eefac00fdc78d.verified.txt b/tests/SnapshotTests/BugFixes/snapshots/snap-v4.8/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_319eefac00fdc78d.verified.txt new file mode 100644 index 0000000000..21be3ec3f6 --- /dev/null +++ b/tests/SnapshotTests/BugFixes/snapshots/snap-v4.8/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_319eefac00fdc78d.verified.txt @@ -0,0 +1,386 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace generator +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory + { + public VogenTypesFactory() + { + } + + private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary> + { + { + typeof(global::ConsumerTests.Speed), + new global::System.Lazy(() => new global::ConsumerTests.Speed.SpeedSystemTextJsonConverter()) + } + }; + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value; + } +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace ConsumerTests +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + [global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] + [global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(SpeedDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.Nullable, Value = { _value }")] + internal readonly partial record struct Speed : global::System.IEquatable, global::System.IEquatable> + { +#if DEBUG +private readonly global::System.Diagnostics.StackTrace _stackTrace = null!; +#endif +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + private readonly global::System.Nullable _value; + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly global::System.Nullable Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get + { + EnsureInitialized(); + return _value; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + init + { + value = Speed.NormalizeInput(value); + _value = value; + } + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Speed() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Speed(global::System.Nullable value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static Speed From(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + /// + /// Tries to build an instance from the provided underlying type. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, false will be returned. + /// + /// The underlying type. + /// An instance of the value object. + /// True if the value object can be built, otherwise false. + public static bool TryFrom( +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + global::System.Nullable value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] +#endif + out Speed vo) + { + value = Speed.NormalizeInput(value); + vo = new Speed(value); + return true; + } + + /// + /// Tries to build an instance from the provided underlying value. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, an error will be returned. + /// + /// The primitive value. + /// A containing either the value object, or an error. + public static global::Vogen.ValueObjectOrError TryFrom(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new global::Vogen.ValueObjectOrError(new Speed(value)); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION +#pragma warning disable CS8775 + public readonly bool IsInitialized() => true; +#pragma warning restore CS8775 +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + public static explicit operator Speed(global::System.Nullable value) => From(value); + public static explicit operator global::System.Nullable(Speed value) => value.Value; + // only called internally when something has been deserialized into + // its primitive type. + private static Speed __Deserialize(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + public readonly global::System.Boolean Equals(Speed other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if (!IsInitialized() || !other.IsInitialized()) + return false; + return global::System.Collections.Generic.EqualityComparer>.Default.Equals(Value, other.Value); + } + + public global::System.Boolean Equals(Speed other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + public readonly global::System.Boolean Equals(global::System.Nullable primitive) + { + return Value.Equals(primitive); + } + + public static global::System.Boolean operator ==(Speed left, global::System.Nullable right) => left.Value.Equals(right); + public static global::System.Boolean operator ==(global::System.Nullable left, Speed right) => right.Value.Equals(left); + public static global::System.Boolean operator !=(global::System.Nullable left, Speed right) => !(left == right); + public static global::System.Boolean operator !=(Speed left, global::System.Nullable right) => !(left == right); +#nullable disable +#nullable restore + public readonly override global::System.Int32 GetHashCode() + { + return Value is null ? 0 : Value.GetHashCode(); + } + + // record enumerates fields - we just want our Value and to throw if it's not initialized. + /// + public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]"; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + ThrowHelper.ThrowWhenNotInitialized(_stackTrace); +#else + ThrowHelper.ThrowWhenNotInitialized(); +#endif + } + } + +#nullable disable + /// + /// Converts a Speed to or from JSON. + /// + public partial class SpeedSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter + { + public override Speed Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, value.Value, options); + } + +#if NET6_0_OR_GREATER + public override Speed ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + writer.WritePropertyName(global::System.Text.Json.JsonSerializer.Serialize(value.Value)); + } +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation) + { + var e = ThrowHelper.CreateValidationException(validation); + throw new global::System.Text.Json.JsonException(null, e); + } + + private static Speed DeserializeJson(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + } + +#nullable restore +#nullable disable + class SpeedTypeConverter : global::System.ComponentModel.TypeConverter + { + public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value) + { + global::System.Nullable ut = (global::System.Nullable)value; + return Speed.__Deserialize(ut); + } + + public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType) + { + if (value is Speed idValue) + { + return idValue.Value; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + +#nullable restore +#nullable disable + internal sealed class SpeedDebugView + { + private readonly Speed _t; + SpeedDebugView(Speed t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.Nullable"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]"; +#if DEBUG + public global::System.String CreatedWith => _t._stackTrace.ToString() ?? "the From method"; +#endif + public global::System.String Conversions => @"Default"; + } + +#nullable restore + static class ThrowHelper + { +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException(); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object."); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? ""); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenValidationFails(global::Vogen.Validation validation) + { + throw CreateValidationException(validation); + } + + internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message); + internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null."); + internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation) + { + var ex = CreateValidationException(validation.ErrorMessage); + if (validation.Data != null) + { + foreach (var kvp in validation.Data) + { + ex.Data[kvp.Key] = kvp.Value; + } + } + + return ex; + } + } + } +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/BugFixes/snapshots/snap-v4.8/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_69223026b0859a11.verified.txt b/tests/SnapshotTests/BugFixes/snapshots/snap-v4.8/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_69223026b0859a11.verified.txt new file mode 100644 index 0000000000..faacab2917 --- /dev/null +++ b/tests/SnapshotTests/BugFixes/snapshots/snap-v4.8/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_69223026b0859a11.verified.txt @@ -0,0 +1,389 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace generator +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory + { + public VogenTypesFactory() + { + } + + private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary> + { + { + typeof(global::ConsumerTests.Speed), + new global::System.Lazy(() => new global::ConsumerTests.Speed.SpeedSystemTextJsonConverter()) + } + }; + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value; + } +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace ConsumerTests +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + [global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] + [global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(SpeedDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.Nullable, Value = { _value }")] + // ReSharper disable once UnusedType.Global + internal partial struct Speed : global::System.IEquatable, global::System.IEquatable> + { +#if DEBUG +private readonly global::System.Diagnostics.StackTrace _stackTrace = null!; +#endif +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + private readonly global::System.Nullable _value; + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly global::System.Nullable Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get + { + EnsureInitialized(); + return _value; + } + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Speed() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Speed(global::System.Nullable value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif +#if DEBUG + _stackTrace = null; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static Speed From(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + /// + /// Tries to build an instance from the provided underlying type. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, false will be returned. + /// + /// The underlying type. + /// An instance of the value object. + /// True if the value object can be built, otherwise false. + public static bool TryFrom( +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + global::System.Nullable value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] +#endif + out Speed vo) + { + value = Speed.NormalizeInput(value); + vo = new Speed(value); + return true; + } + + /// + /// Tries to build an instance from the provided underlying value. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, an error will be returned. + /// + /// The primitive value. + /// A containing either the value object, or an error. + public static global::Vogen.ValueObjectOrError TryFrom(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new global::Vogen.ValueObjectOrError(new Speed(value)); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION +#pragma warning disable CS8775 + public readonly bool IsInitialized() => true; +#pragma warning restore CS8775 +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + public static explicit operator Speed(global::System.Nullable value) => From(value); + public static explicit operator global::System.Nullable(Speed value) => value.Value; + // only called internally when something has been deserialized into + // its primitive type. + private static Speed __Deserialize(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + public readonly global::System.Boolean Equals(Speed other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if (!IsInitialized() || !other.IsInitialized()) + return false; + return global::System.Collections.Generic.EqualityComparer>.Default.Equals(Value, other.Value); + } + + public global::System.Boolean Equals(Speed other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + public readonly global::System.Boolean Equals(global::System.Nullable primitive) + { + return Value.Equals(primitive); + } + + public readonly override global::System.Boolean Equals(global::System.Object obj) + { + return obj is Speed && Equals((Speed)obj); + } + + public static global::System.Boolean operator ==(Speed left, Speed right) => left.Equals(right); + public static global::System.Boolean operator !=(Speed left, Speed right) => !(left == right); + public static global::System.Boolean operator ==(Speed left, global::System.Nullable right) => left.Value.Equals(right); + public static global::System.Boolean operator ==(global::System.Nullable left, Speed right) => right.Value.Equals(left); + public static global::System.Boolean operator !=(global::System.Nullable left, Speed right) => !(left == right); + public static global::System.Boolean operator !=(Speed left, global::System.Nullable right) => !(left == right); +#nullable disable +#nullable restore + public readonly override global::System.Int32 GetHashCode() + { + return Value is null ? 0 : Value.GetHashCode(); + } + + /// + public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]"; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + ThrowHelper.ThrowWhenNotInitialized(_stackTrace); +#else + ThrowHelper.ThrowWhenNotInitialized(); +#endif + } + } + +#nullable disable + /// + /// Converts a Speed to or from JSON. + /// + public partial class SpeedSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter + { + public override Speed Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, value.Value, options); + } + +#if NET6_0_OR_GREATER + public override Speed ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + writer.WritePropertyName(global::System.Text.Json.JsonSerializer.Serialize(value.Value)); + } +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation) + { + var e = ThrowHelper.CreateValidationException(validation); + throw new global::System.Text.Json.JsonException(null, e); + } + + private static Speed DeserializeJson(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + } + +#nullable restore +#nullable disable + class SpeedTypeConverter : global::System.ComponentModel.TypeConverter + { + public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value) + { + global::System.Nullable ut = (global::System.Nullable)value; + return Speed.__Deserialize(ut); + } + + public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType) + { + if (value is Speed idValue) + { + return idValue.Value; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + +#nullable restore +#nullable disable + internal sealed class SpeedDebugView + { + private readonly Speed _t; + SpeedDebugView(Speed t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.Nullable"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]"; +#if DEBUG + public global::System.String CreatedWith => _t._stackTrace.ToString() ?? "the From method"; +#endif + public global::System.String Conversions => @"Default"; + } + +#nullable restore + static class ThrowHelper + { +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException(); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object."); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? ""); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenValidationFails(global::Vogen.Validation validation) + { + throw CreateValidationException(validation); + } + + internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message); + internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null."); + internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation) + { + var ex = CreateValidationException(validation.ErrorMessage); + if (validation.Data != null) + { + foreach (var kvp in validation.Data) + { + ex.Data[kvp.Key] = kvp.Value; + } + } + + return ex; + } + } + } +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/BugFixes/snapshots/snap-v4.8/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_93100ba0c1a5ceda.verified.txt b/tests/SnapshotTests/BugFixes/snapshots/snap-v4.8/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_93100ba0c1a5ceda.verified.txt new file mode 100644 index 0000000000..d2c7dfd69c --- /dev/null +++ b/tests/SnapshotTests/BugFixes/snapshots/snap-v4.8/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_93100ba0c1a5ceda.verified.txt @@ -0,0 +1,398 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace generator +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory + { + public VogenTypesFactory() + { + } + + private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary> + { + { + typeof(global::ConsumerTests.Speed), + new global::System.Lazy(() => new global::ConsumerTests.Speed.SpeedSystemTextJsonConverter()) + } + }; + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value; + } +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace ConsumerTests +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + [global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] + [global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(SpeedDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.Nullable, Value = { _value }")] + internal partial record class Speed : global::System.IEquatable, global::System.IEquatable> + { +#if DEBUG +private readonly global::System.Diagnostics.StackTrace _stackTrace = null!; +#endif +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + private readonly global::System.Nullable _value; + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public global::System.Nullable Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get + { + EnsureInitialized(); + return _value; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + init + { + value = Speed.NormalizeInput(value); + _value = value; + } + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Speed() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Speed(global::System.Nullable value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static Speed From(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + /// + /// Tries to build an instance from the provided underlying type. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, false will be returned. + /// + /// The underlying type. + /// An instance of the value object. + /// True if the value object can be built, otherwise false. + public static bool TryFrom( +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + global::System.Nullable value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] +#endif + out Speed vo) + { + value = Speed.NormalizeInput(value); + vo = new Speed(value); + return true; + } + + /// + /// Tries to build an instance from the provided underlying value. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, an error will be returned. + /// + /// The primitive value. + /// A containing either the value object, or an error. + public static global::Vogen.ValueObjectOrError TryFrom(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new global::Vogen.ValueObjectOrError(new Speed(value)); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION +#pragma warning disable CS8775 + public bool IsInitialized() => true; +#pragma warning restore CS8775 +#else + public bool IsInitialized() => _isInitialized; +#endif + // only called internally when something has been deserialized into + // its primitive type. + private static Speed __Deserialize(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + public virtual global::System.Boolean Equals(Speed other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if (!IsInitialized() || !other.IsInitialized()) + return false; + if (ReferenceEquals(this, other)) + { + return true; + } + + return GetType() == other.GetType() && global::System.Collections.Generic.EqualityComparer>.Default.Equals(Value, other.Value); + } + + public global::System.Boolean Equals(Speed other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + public global::System.Boolean Equals(global::System.Nullable primitive) + { + return Value.Equals(primitive); + } + + public static global::System.Boolean operator ==(Speed left, global::System.Nullable right) => left.Value.Equals(right); + public static global::System.Boolean operator ==(global::System.Nullable left, Speed right) => right.Value.Equals(left); + public static global::System.Boolean operator !=(global::System.Nullable left, Speed right) => !(left == right); + public static global::System.Boolean operator !=(Speed left, global::System.Nullable right) => !(left == right); + public static explicit operator Speed(global::System.Nullable value) => From(value); + public static explicit operator global::System.Nullable(Speed value) => value.Value; +#nullable disable +#nullable restore + public override global::System.Int32 GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + global::System.Int32 hash = (global::System.Int32)2166136261; + hash = (hash * 16777619) ^ GetType().GetHashCode(); + hash = (hash * 16777619) ^ (Value is null ? 0 : Value.GetHashCode()); + return hash; + } + } + + // record enumerates fields - we just want our Value and to throw if it's not initialized. + /// + public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]"; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + ThrowHelper.ThrowWhenNotInitialized(_stackTrace); +#else + ThrowHelper.ThrowWhenNotInitialized(); +#endif + } + } + +#nullable disable + /// + /// Converts a Speed to or from JSON. + /// + public partial class SpeedSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter + { + public override Speed Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, value.Value, options); + } + +#if NET6_0_OR_GREATER + public override Speed ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + writer.WritePropertyName(global::System.Text.Json.JsonSerializer.Serialize(value.Value)); + } +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation) + { + var e = ThrowHelper.CreateValidationException(validation); + throw new global::System.Text.Json.JsonException(null, e); + } + + private static Speed DeserializeJson(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + } + +#nullable restore +#nullable disable + class SpeedTypeConverter : global::System.ComponentModel.TypeConverter + { + public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value) + { + global::System.Nullable ut = (global::System.Nullable)value; + return Speed.__Deserialize(ut); + } + + public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType) + { + if (value is Speed idValue) + { + return idValue.Value; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + +#nullable restore + internal sealed class SpeedDebugView + { + private readonly Speed _t; + SpeedDebugView(Speed t) + { + _t = t; + } + + public global::System.String UnderlyingType => "global::System.Nullable"; + public global::System.Nullable Value => _t.Value; + public global::System.String Conversions => @"[global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] +[global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] +"; + } + + static class ThrowHelper + { +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException(); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object."); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? ""); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenValidationFails(global::Vogen.Validation validation) + { + throw CreateValidationException(validation); + } + + internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message); + internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null."); + internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation) + { + var ex = CreateValidationException(validation.ErrorMessage); + if (validation.Data != null) + { + foreach (var kvp in validation.Data) + { + ex.Data[kvp.Key] = kvp.Value; + } + } + + return ex; + } + } + } +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/BugFixes/snapshots/snap-v4.8/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_bcb0b5e156ac4743.verified.txt b/tests/SnapshotTests/BugFixes/snapshots/snap-v4.8/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_bcb0b5e156ac4743.verified.txt new file mode 100644 index 0000000000..109b540df1 --- /dev/null +++ b/tests/SnapshotTests/BugFixes/snapshots/snap-v4.8/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_bcb0b5e156ac4743.verified.txt @@ -0,0 +1,386 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace generator +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory + { + public VogenTypesFactory() + { + } + + private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary> + { + { + typeof(global::ConsumerTests.Speed), + new global::System.Lazy(() => new global::ConsumerTests.Speed.SpeedSystemTextJsonConverter()) + } + }; + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value; + } +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace ConsumerTests +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + [global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] + [global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(SpeedDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.Nullable, Value = { _value }")] + internal partial record struct Speed : global::System.IEquatable, global::System.IEquatable> + { +#if DEBUG +private readonly global::System.Diagnostics.StackTrace _stackTrace = null!; +#endif +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + private readonly global::System.Nullable _value; + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly global::System.Nullable Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get + { + EnsureInitialized(); + return _value; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + init + { + value = Speed.NormalizeInput(value); + _value = value; + } + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Speed() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Speed(global::System.Nullable value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static Speed From(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + /// + /// Tries to build an instance from the provided underlying type. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, false will be returned. + /// + /// The underlying type. + /// An instance of the value object. + /// True if the value object can be built, otherwise false. + public static bool TryFrom( +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + global::System.Nullable value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] +#endif + out Speed vo) + { + value = Speed.NormalizeInput(value); + vo = new Speed(value); + return true; + } + + /// + /// Tries to build an instance from the provided underlying value. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, an error will be returned. + /// + /// The primitive value. + /// A containing either the value object, or an error. + public static global::Vogen.ValueObjectOrError TryFrom(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new global::Vogen.ValueObjectOrError(new Speed(value)); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION +#pragma warning disable CS8775 + public readonly bool IsInitialized() => true; +#pragma warning restore CS8775 +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + public static explicit operator Speed(global::System.Nullable value) => From(value); + public static explicit operator global::System.Nullable(Speed value) => value.Value; + // only called internally when something has been deserialized into + // its primitive type. + private static Speed __Deserialize(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + public readonly global::System.Boolean Equals(Speed other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if (!IsInitialized() || !other.IsInitialized()) + return false; + return global::System.Collections.Generic.EqualityComparer>.Default.Equals(Value, other.Value); + } + + public global::System.Boolean Equals(Speed other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + public readonly global::System.Boolean Equals(global::System.Nullable primitive) + { + return Value.Equals(primitive); + } + + public static global::System.Boolean operator ==(Speed left, global::System.Nullable right) => left.Value.Equals(right); + public static global::System.Boolean operator ==(global::System.Nullable left, Speed right) => right.Value.Equals(left); + public static global::System.Boolean operator !=(global::System.Nullable left, Speed right) => !(left == right); + public static global::System.Boolean operator !=(Speed left, global::System.Nullable right) => !(left == right); +#nullable disable +#nullable restore + public readonly override global::System.Int32 GetHashCode() + { + return Value is null ? 0 : Value.GetHashCode(); + } + + // record enumerates fields - we just want our Value and to throw if it's not initialized. + /// + public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]"; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + ThrowHelper.ThrowWhenNotInitialized(_stackTrace); +#else + ThrowHelper.ThrowWhenNotInitialized(); +#endif + } + } + +#nullable disable + /// + /// Converts a Speed to or from JSON. + /// + public partial class SpeedSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter + { + public override Speed Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, value.Value, options); + } + +#if NET6_0_OR_GREATER + public override Speed ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + writer.WritePropertyName(global::System.Text.Json.JsonSerializer.Serialize(value.Value)); + } +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation) + { + var e = ThrowHelper.CreateValidationException(validation); + throw new global::System.Text.Json.JsonException(null, e); + } + + private static Speed DeserializeJson(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + } + +#nullable restore +#nullable disable + class SpeedTypeConverter : global::System.ComponentModel.TypeConverter + { + public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value) + { + global::System.Nullable ut = (global::System.Nullable)value; + return Speed.__Deserialize(ut); + } + + public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType) + { + if (value is Speed idValue) + { + return idValue.Value; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + +#nullable restore +#nullable disable + internal sealed class SpeedDebugView + { + private readonly Speed _t; + SpeedDebugView(Speed t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.Nullable"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]"; +#if DEBUG + public global::System.String CreatedWith => _t._stackTrace.ToString() ?? "the From method"; +#endif + public global::System.String Conversions => @"Default"; + } + +#nullable restore + static class ThrowHelper + { +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException(); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object."); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? ""); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenValidationFails(global::Vogen.Validation validation) + { + throw CreateValidationException(validation); + } + + internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message); + internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null."); + internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation) + { + var ex = CreateValidationException(validation.ErrorMessage); + if (validation.Data != null) + { + foreach (var kvp in validation.Data) + { + ex.Data[kvp.Key] = kvp.Value; + } + } + + return ex; + } + } + } +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/BugFixes/snapshots/snap-v4.8/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_dbd487d12d15aac0.verified.txt b/tests/SnapshotTests/BugFixes/snapshots/snap-v4.8/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_dbd487d12d15aac0.verified.txt new file mode 100644 index 0000000000..dcbafea324 --- /dev/null +++ b/tests/SnapshotTests/BugFixes/snapshots/snap-v4.8/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_dbd487d12d15aac0.verified.txt @@ -0,0 +1,389 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace generator +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory + { + public VogenTypesFactory() + { + } + + private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary> + { + { + typeof(global::ConsumerTests.Speed), + new global::System.Lazy(() => new global::ConsumerTests.Speed.SpeedSystemTextJsonConverter()) + } + }; + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value; + } +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace ConsumerTests +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + [global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] + [global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(SpeedDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.Nullable, Value = { _value }")] + // ReSharper disable once UnusedType.Global + internal readonly partial struct Speed : global::System.IEquatable, global::System.IEquatable> + { +#if DEBUG +private readonly global::System.Diagnostics.StackTrace _stackTrace = null!; +#endif +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + private readonly global::System.Nullable _value; + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly global::System.Nullable Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get + { + EnsureInitialized(); + return _value; + } + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Speed() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Speed(global::System.Nullable value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif +#if DEBUG + _stackTrace = null; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static Speed From(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + /// + /// Tries to build an instance from the provided underlying type. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, false will be returned. + /// + /// The underlying type. + /// An instance of the value object. + /// True if the value object can be built, otherwise false. + public static bool TryFrom( +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + global::System.Nullable value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] +#endif + out Speed vo) + { + value = Speed.NormalizeInput(value); + vo = new Speed(value); + return true; + } + + /// + /// Tries to build an instance from the provided underlying value. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, an error will be returned. + /// + /// The primitive value. + /// A containing either the value object, or an error. + public static global::Vogen.ValueObjectOrError TryFrom(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new global::Vogen.ValueObjectOrError(new Speed(value)); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION +#pragma warning disable CS8775 + public readonly bool IsInitialized() => true; +#pragma warning restore CS8775 +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + public static explicit operator Speed(global::System.Nullable value) => From(value); + public static explicit operator global::System.Nullable(Speed value) => value.Value; + // only called internally when something has been deserialized into + // its primitive type. + private static Speed __Deserialize(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + public readonly global::System.Boolean Equals(Speed other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if (!IsInitialized() || !other.IsInitialized()) + return false; + return global::System.Collections.Generic.EqualityComparer>.Default.Equals(Value, other.Value); + } + + public global::System.Boolean Equals(Speed other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + public readonly global::System.Boolean Equals(global::System.Nullable primitive) + { + return Value.Equals(primitive); + } + + public readonly override global::System.Boolean Equals(global::System.Object obj) + { + return obj is Speed && Equals((Speed)obj); + } + + public static global::System.Boolean operator ==(Speed left, Speed right) => left.Equals(right); + public static global::System.Boolean operator !=(Speed left, Speed right) => !(left == right); + public static global::System.Boolean operator ==(Speed left, global::System.Nullable right) => left.Value.Equals(right); + public static global::System.Boolean operator ==(global::System.Nullable left, Speed right) => right.Value.Equals(left); + public static global::System.Boolean operator !=(global::System.Nullable left, Speed right) => !(left == right); + public static global::System.Boolean operator !=(Speed left, global::System.Nullable right) => !(left == right); +#nullable disable +#nullable restore + public readonly override global::System.Int32 GetHashCode() + { + return Value is null ? 0 : Value.GetHashCode(); + } + + /// + public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]"; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + ThrowHelper.ThrowWhenNotInitialized(_stackTrace); +#else + ThrowHelper.ThrowWhenNotInitialized(); +#endif + } + } + +#nullable disable + /// + /// Converts a Speed to or from JSON. + /// + public partial class SpeedSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter + { + public override Speed Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, value.Value, options); + } + +#if NET6_0_OR_GREATER + public override Speed ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + writer.WritePropertyName(global::System.Text.Json.JsonSerializer.Serialize(value.Value)); + } +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation) + { + var e = ThrowHelper.CreateValidationException(validation); + throw new global::System.Text.Json.JsonException(null, e); + } + + private static Speed DeserializeJson(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + } + +#nullable restore +#nullable disable + class SpeedTypeConverter : global::System.ComponentModel.TypeConverter + { + public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value) + { + global::System.Nullable ut = (global::System.Nullable)value; + return Speed.__Deserialize(ut); + } + + public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType) + { + if (value is Speed idValue) + { + return idValue.Value; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + +#nullable restore +#nullable disable + internal sealed class SpeedDebugView + { + private readonly Speed _t; + SpeedDebugView(Speed t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.Nullable"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]"; +#if DEBUG + public global::System.String CreatedWith => _t._stackTrace.ToString() ?? "the From method"; +#endif + public global::System.String Conversions => @"Default"; + } + +#nullable restore + static class ThrowHelper + { +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException(); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object."); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? ""); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenValidationFails(global::Vogen.Validation validation) + { + throw CreateValidationException(validation); + } + + internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message); + internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null."); + internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation) + { + var ex = CreateValidationException(validation.ErrorMessage); + if (validation.Data != null) + { + foreach (var kvp in validation.Data) + { + ex.Data[kvp.Key] = kvp.Value; + } + } + + return ex; + } + } + } +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/BugFixes/snapshots/snap-v8.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_1ffb354a8af60055.verified.txt b/tests/SnapshotTests/BugFixes/snapshots/snap-v8.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_1ffb354a8af60055.verified.txt new file mode 100644 index 0000000000..8df3671f73 --- /dev/null +++ b/tests/SnapshotTests/BugFixes/snapshots/snap-v8.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_1ffb354a8af60055.verified.txt @@ -0,0 +1,397 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace generator +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory + { + public VogenTypesFactory() + { + } + + private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary> + { + { + typeof(global::ConsumerTests.Speed), + new global::System.Lazy(() => new global::ConsumerTests.Speed.SpeedSystemTextJsonConverter()) + } + }; + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value; + } +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace ConsumerTests +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + [global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] + [global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(SpeedDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.Nullable, Value = { _value }")] + internal partial class Speed : global::System.IEquatable, global::System.IEquatable> + { +#if DEBUG +private readonly global::System.Diagnostics.StackTrace _stackTrace = null!; +#endif +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + private readonly global::System.Nullable _value; + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public global::System.Nullable Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get + { + EnsureInitialized(); + return _value; + } + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Speed() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Speed(global::System.Nullable value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static Speed From(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + /// + /// Tries to build an instance from the provided underlying type. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, false will be returned. + /// + /// The underlying type. + /// An instance of the value object. + /// True if the value object can be built, otherwise false. + public static bool TryFrom( +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + global::System.Nullable value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] +#endif + out Speed vo) + { + value = Speed.NormalizeInput(value); + vo = new Speed(value); + return true; + } + + /// + /// Tries to build an instance from the provided underlying value. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, an error will be returned. + /// + /// The primitive value. + /// A containing either the value object, or an error. + public static global::Vogen.ValueObjectOrError TryFrom(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new global::Vogen.ValueObjectOrError(new Speed(value)); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION +#pragma warning disable CS8775 + public bool IsInitialized() => true; +#pragma warning restore CS8775 +#else + public bool IsInitialized() => _isInitialized; +#endif + // only called internally when something has been deserialized into + // its primitive type. + private static Speed __Deserialize(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + public global::System.Boolean Equals(Speed other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if (!IsInitialized() || !other.IsInitialized()) + return false; + if (ReferenceEquals(this, other)) + { + return true; + } + + return GetType() == other.GetType() && global::System.Collections.Generic.EqualityComparer>.Default.Equals(Value, other.Value); + } + + public global::System.Boolean Equals(Speed other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + public global::System.Boolean Equals(global::System.Nullable primitive) + { + return Value.Equals(primitive); + } + + public override global::System.Boolean Equals(global::System.Object obj) + { + return Equals(obj as Speed); + } + + public static global::System.Boolean operator ==(Speed left, Speed right) => Equals(left, right); + public static global::System.Boolean operator !=(Speed left, Speed right) => !Equals(left, right); + public static global::System.Boolean operator ==(Speed left, global::System.Nullable right) => left.Value.Equals(right); + public static global::System.Boolean operator ==(global::System.Nullable left, Speed right) => right.Value.Equals(left); + public static global::System.Boolean operator !=(global::System.Nullable left, Speed right) => !(left == right); + public static global::System.Boolean operator !=(Speed left, global::System.Nullable right) => !(left == right); + public static explicit operator Speed(global::System.Nullable value) => From(value); + public static explicit operator global::System.Nullable(Speed value) => value.Value; +#nullable disable +#nullable restore + public override global::System.Int32 GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + global::System.Int32 hash = (global::System.Int32)2166136261; + hash = (hash * 16777619) ^ GetType().GetHashCode(); + hash = (hash * 16777619) ^ (Value is null ? 0 : Value.GetHashCode()); + return hash; + } + } + + /// + public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]"; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + ThrowHelper.ThrowWhenNotInitialized(_stackTrace); +#else + ThrowHelper.ThrowWhenNotInitialized(); +#endif + } + } + +#nullable disable + /// + /// Converts a Speed to or from JSON. + /// + public partial class SpeedSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter + { + public override Speed Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, value.Value, options); + } + +#if NET6_0_OR_GREATER + public override Speed ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + writer.WritePropertyName(global::System.Text.Json.JsonSerializer.Serialize(value.Value)); + } +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation) + { + var e = ThrowHelper.CreateValidationException(validation); + throw new global::System.Text.Json.JsonException(null, e); + } + + private static Speed DeserializeJson(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + } + +#nullable restore +#nullable disable + class SpeedTypeConverter : global::System.ComponentModel.TypeConverter + { + public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value) + { + global::System.Nullable ut = (global::System.Nullable)value; + return Speed.__Deserialize(ut); + } + + public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType) + { + if (value is Speed idValue) + { + return idValue.Value; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + +#nullable restore + internal sealed class SpeedDebugView + { + private readonly Speed _t; + SpeedDebugView(Speed t) + { + _t = t; + } + + public global::System.String UnderlyingType => "global::System.Nullable"; + public global::System.Nullable Value => _t.Value; + public global::System.String Conversions => @"[global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] +[global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] +"; + } + + static class ThrowHelper + { +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException(); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object."); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? ""); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenValidationFails(global::Vogen.Validation validation) + { + throw CreateValidationException(validation); + } + + internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message); + internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null."); + internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation) + { + var ex = CreateValidationException(validation.ErrorMessage); + if (validation.Data != null) + { + foreach (var kvp in validation.Data) + { + ex.Data[kvp.Key] = kvp.Value; + } + } + + return ex; + } + } + } +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/BugFixes/snapshots/snap-v8.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_319eefac00fdc78d.verified.txt b/tests/SnapshotTests/BugFixes/snapshots/snap-v8.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_319eefac00fdc78d.verified.txt new file mode 100644 index 0000000000..21be3ec3f6 --- /dev/null +++ b/tests/SnapshotTests/BugFixes/snapshots/snap-v8.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_319eefac00fdc78d.verified.txt @@ -0,0 +1,386 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace generator +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory + { + public VogenTypesFactory() + { + } + + private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary> + { + { + typeof(global::ConsumerTests.Speed), + new global::System.Lazy(() => new global::ConsumerTests.Speed.SpeedSystemTextJsonConverter()) + } + }; + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value; + } +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace ConsumerTests +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + [global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] + [global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(SpeedDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.Nullable, Value = { _value }")] + internal readonly partial record struct Speed : global::System.IEquatable, global::System.IEquatable> + { +#if DEBUG +private readonly global::System.Diagnostics.StackTrace _stackTrace = null!; +#endif +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + private readonly global::System.Nullable _value; + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly global::System.Nullable Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get + { + EnsureInitialized(); + return _value; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + init + { + value = Speed.NormalizeInput(value); + _value = value; + } + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Speed() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Speed(global::System.Nullable value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static Speed From(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + /// + /// Tries to build an instance from the provided underlying type. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, false will be returned. + /// + /// The underlying type. + /// An instance of the value object. + /// True if the value object can be built, otherwise false. + public static bool TryFrom( +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + global::System.Nullable value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] +#endif + out Speed vo) + { + value = Speed.NormalizeInput(value); + vo = new Speed(value); + return true; + } + + /// + /// Tries to build an instance from the provided underlying value. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, an error will be returned. + /// + /// The primitive value. + /// A containing either the value object, or an error. + public static global::Vogen.ValueObjectOrError TryFrom(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new global::Vogen.ValueObjectOrError(new Speed(value)); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION +#pragma warning disable CS8775 + public readonly bool IsInitialized() => true; +#pragma warning restore CS8775 +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + public static explicit operator Speed(global::System.Nullable value) => From(value); + public static explicit operator global::System.Nullable(Speed value) => value.Value; + // only called internally when something has been deserialized into + // its primitive type. + private static Speed __Deserialize(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + public readonly global::System.Boolean Equals(Speed other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if (!IsInitialized() || !other.IsInitialized()) + return false; + return global::System.Collections.Generic.EqualityComparer>.Default.Equals(Value, other.Value); + } + + public global::System.Boolean Equals(Speed other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + public readonly global::System.Boolean Equals(global::System.Nullable primitive) + { + return Value.Equals(primitive); + } + + public static global::System.Boolean operator ==(Speed left, global::System.Nullable right) => left.Value.Equals(right); + public static global::System.Boolean operator ==(global::System.Nullable left, Speed right) => right.Value.Equals(left); + public static global::System.Boolean operator !=(global::System.Nullable left, Speed right) => !(left == right); + public static global::System.Boolean operator !=(Speed left, global::System.Nullable right) => !(left == right); +#nullable disable +#nullable restore + public readonly override global::System.Int32 GetHashCode() + { + return Value is null ? 0 : Value.GetHashCode(); + } + + // record enumerates fields - we just want our Value and to throw if it's not initialized. + /// + public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]"; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + ThrowHelper.ThrowWhenNotInitialized(_stackTrace); +#else + ThrowHelper.ThrowWhenNotInitialized(); +#endif + } + } + +#nullable disable + /// + /// Converts a Speed to or from JSON. + /// + public partial class SpeedSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter + { + public override Speed Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, value.Value, options); + } + +#if NET6_0_OR_GREATER + public override Speed ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + writer.WritePropertyName(global::System.Text.Json.JsonSerializer.Serialize(value.Value)); + } +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation) + { + var e = ThrowHelper.CreateValidationException(validation); + throw new global::System.Text.Json.JsonException(null, e); + } + + private static Speed DeserializeJson(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + } + +#nullable restore +#nullable disable + class SpeedTypeConverter : global::System.ComponentModel.TypeConverter + { + public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value) + { + global::System.Nullable ut = (global::System.Nullable)value; + return Speed.__Deserialize(ut); + } + + public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType) + { + if (value is Speed idValue) + { + return idValue.Value; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + +#nullable restore +#nullable disable + internal sealed class SpeedDebugView + { + private readonly Speed _t; + SpeedDebugView(Speed t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.Nullable"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]"; +#if DEBUG + public global::System.String CreatedWith => _t._stackTrace.ToString() ?? "the From method"; +#endif + public global::System.String Conversions => @"Default"; + } + +#nullable restore + static class ThrowHelper + { +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException(); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object."); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? ""); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenValidationFails(global::Vogen.Validation validation) + { + throw CreateValidationException(validation); + } + + internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message); + internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null."); + internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation) + { + var ex = CreateValidationException(validation.ErrorMessage); + if (validation.Data != null) + { + foreach (var kvp in validation.Data) + { + ex.Data[kvp.Key] = kvp.Value; + } + } + + return ex; + } + } + } +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/BugFixes/snapshots/snap-v8.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_69223026b0859a11.verified.txt b/tests/SnapshotTests/BugFixes/snapshots/snap-v8.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_69223026b0859a11.verified.txt new file mode 100644 index 0000000000..faacab2917 --- /dev/null +++ b/tests/SnapshotTests/BugFixes/snapshots/snap-v8.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_69223026b0859a11.verified.txt @@ -0,0 +1,389 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace generator +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory + { + public VogenTypesFactory() + { + } + + private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary> + { + { + typeof(global::ConsumerTests.Speed), + new global::System.Lazy(() => new global::ConsumerTests.Speed.SpeedSystemTextJsonConverter()) + } + }; + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value; + } +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace ConsumerTests +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + [global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] + [global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(SpeedDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.Nullable, Value = { _value }")] + // ReSharper disable once UnusedType.Global + internal partial struct Speed : global::System.IEquatable, global::System.IEquatable> + { +#if DEBUG +private readonly global::System.Diagnostics.StackTrace _stackTrace = null!; +#endif +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + private readonly global::System.Nullable _value; + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly global::System.Nullable Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get + { + EnsureInitialized(); + return _value; + } + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Speed() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Speed(global::System.Nullable value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif +#if DEBUG + _stackTrace = null; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static Speed From(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + /// + /// Tries to build an instance from the provided underlying type. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, false will be returned. + /// + /// The underlying type. + /// An instance of the value object. + /// True if the value object can be built, otherwise false. + public static bool TryFrom( +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + global::System.Nullable value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] +#endif + out Speed vo) + { + value = Speed.NormalizeInput(value); + vo = new Speed(value); + return true; + } + + /// + /// Tries to build an instance from the provided underlying value. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, an error will be returned. + /// + /// The primitive value. + /// A containing either the value object, or an error. + public static global::Vogen.ValueObjectOrError TryFrom(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new global::Vogen.ValueObjectOrError(new Speed(value)); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION +#pragma warning disable CS8775 + public readonly bool IsInitialized() => true; +#pragma warning restore CS8775 +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + public static explicit operator Speed(global::System.Nullable value) => From(value); + public static explicit operator global::System.Nullable(Speed value) => value.Value; + // only called internally when something has been deserialized into + // its primitive type. + private static Speed __Deserialize(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + public readonly global::System.Boolean Equals(Speed other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if (!IsInitialized() || !other.IsInitialized()) + return false; + return global::System.Collections.Generic.EqualityComparer>.Default.Equals(Value, other.Value); + } + + public global::System.Boolean Equals(Speed other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + public readonly global::System.Boolean Equals(global::System.Nullable primitive) + { + return Value.Equals(primitive); + } + + public readonly override global::System.Boolean Equals(global::System.Object obj) + { + return obj is Speed && Equals((Speed)obj); + } + + public static global::System.Boolean operator ==(Speed left, Speed right) => left.Equals(right); + public static global::System.Boolean operator !=(Speed left, Speed right) => !(left == right); + public static global::System.Boolean operator ==(Speed left, global::System.Nullable right) => left.Value.Equals(right); + public static global::System.Boolean operator ==(global::System.Nullable left, Speed right) => right.Value.Equals(left); + public static global::System.Boolean operator !=(global::System.Nullable left, Speed right) => !(left == right); + public static global::System.Boolean operator !=(Speed left, global::System.Nullable right) => !(left == right); +#nullable disable +#nullable restore + public readonly override global::System.Int32 GetHashCode() + { + return Value is null ? 0 : Value.GetHashCode(); + } + + /// + public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]"; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + ThrowHelper.ThrowWhenNotInitialized(_stackTrace); +#else + ThrowHelper.ThrowWhenNotInitialized(); +#endif + } + } + +#nullable disable + /// + /// Converts a Speed to or from JSON. + /// + public partial class SpeedSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter + { + public override Speed Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, value.Value, options); + } + +#if NET6_0_OR_GREATER + public override Speed ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + writer.WritePropertyName(global::System.Text.Json.JsonSerializer.Serialize(value.Value)); + } +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation) + { + var e = ThrowHelper.CreateValidationException(validation); + throw new global::System.Text.Json.JsonException(null, e); + } + + private static Speed DeserializeJson(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + } + +#nullable restore +#nullable disable + class SpeedTypeConverter : global::System.ComponentModel.TypeConverter + { + public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value) + { + global::System.Nullable ut = (global::System.Nullable)value; + return Speed.__Deserialize(ut); + } + + public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType) + { + if (value is Speed idValue) + { + return idValue.Value; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + +#nullable restore +#nullable disable + internal sealed class SpeedDebugView + { + private readonly Speed _t; + SpeedDebugView(Speed t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.Nullable"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]"; +#if DEBUG + public global::System.String CreatedWith => _t._stackTrace.ToString() ?? "the From method"; +#endif + public global::System.String Conversions => @"Default"; + } + +#nullable restore + static class ThrowHelper + { +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException(); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object."); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? ""); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenValidationFails(global::Vogen.Validation validation) + { + throw CreateValidationException(validation); + } + + internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message); + internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null."); + internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation) + { + var ex = CreateValidationException(validation.ErrorMessage); + if (validation.Data != null) + { + foreach (var kvp in validation.Data) + { + ex.Data[kvp.Key] = kvp.Value; + } + } + + return ex; + } + } + } +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/BugFixes/snapshots/snap-v8.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_93100ba0c1a5ceda.verified.txt b/tests/SnapshotTests/BugFixes/snapshots/snap-v8.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_93100ba0c1a5ceda.verified.txt new file mode 100644 index 0000000000..d2c7dfd69c --- /dev/null +++ b/tests/SnapshotTests/BugFixes/snapshots/snap-v8.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_93100ba0c1a5ceda.verified.txt @@ -0,0 +1,398 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace generator +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory + { + public VogenTypesFactory() + { + } + + private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary> + { + { + typeof(global::ConsumerTests.Speed), + new global::System.Lazy(() => new global::ConsumerTests.Speed.SpeedSystemTextJsonConverter()) + } + }; + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value; + } +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace ConsumerTests +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + [global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] + [global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(SpeedDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.Nullable, Value = { _value }")] + internal partial record class Speed : global::System.IEquatable, global::System.IEquatable> + { +#if DEBUG +private readonly global::System.Diagnostics.StackTrace _stackTrace = null!; +#endif +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + private readonly global::System.Nullable _value; + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public global::System.Nullable Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get + { + EnsureInitialized(); + return _value; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + init + { + value = Speed.NormalizeInput(value); + _value = value; + } + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Speed() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Speed(global::System.Nullable value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static Speed From(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + /// + /// Tries to build an instance from the provided underlying type. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, false will be returned. + /// + /// The underlying type. + /// An instance of the value object. + /// True if the value object can be built, otherwise false. + public static bool TryFrom( +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + global::System.Nullable value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] +#endif + out Speed vo) + { + value = Speed.NormalizeInput(value); + vo = new Speed(value); + return true; + } + + /// + /// Tries to build an instance from the provided underlying value. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, an error will be returned. + /// + /// The primitive value. + /// A containing either the value object, or an error. + public static global::Vogen.ValueObjectOrError TryFrom(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new global::Vogen.ValueObjectOrError(new Speed(value)); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION +#pragma warning disable CS8775 + public bool IsInitialized() => true; +#pragma warning restore CS8775 +#else + public bool IsInitialized() => _isInitialized; +#endif + // only called internally when something has been deserialized into + // its primitive type. + private static Speed __Deserialize(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + public virtual global::System.Boolean Equals(Speed other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if (!IsInitialized() || !other.IsInitialized()) + return false; + if (ReferenceEquals(this, other)) + { + return true; + } + + return GetType() == other.GetType() && global::System.Collections.Generic.EqualityComparer>.Default.Equals(Value, other.Value); + } + + public global::System.Boolean Equals(Speed other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + public global::System.Boolean Equals(global::System.Nullable primitive) + { + return Value.Equals(primitive); + } + + public static global::System.Boolean operator ==(Speed left, global::System.Nullable right) => left.Value.Equals(right); + public static global::System.Boolean operator ==(global::System.Nullable left, Speed right) => right.Value.Equals(left); + public static global::System.Boolean operator !=(global::System.Nullable left, Speed right) => !(left == right); + public static global::System.Boolean operator !=(Speed left, global::System.Nullable right) => !(left == right); + public static explicit operator Speed(global::System.Nullable value) => From(value); + public static explicit operator global::System.Nullable(Speed value) => value.Value; +#nullable disable +#nullable restore + public override global::System.Int32 GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + global::System.Int32 hash = (global::System.Int32)2166136261; + hash = (hash * 16777619) ^ GetType().GetHashCode(); + hash = (hash * 16777619) ^ (Value is null ? 0 : Value.GetHashCode()); + return hash; + } + } + + // record enumerates fields - we just want our Value and to throw if it's not initialized. + /// + public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]"; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + ThrowHelper.ThrowWhenNotInitialized(_stackTrace); +#else + ThrowHelper.ThrowWhenNotInitialized(); +#endif + } + } + +#nullable disable + /// + /// Converts a Speed to or from JSON. + /// + public partial class SpeedSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter + { + public override Speed Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, value.Value, options); + } + +#if NET6_0_OR_GREATER + public override Speed ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + writer.WritePropertyName(global::System.Text.Json.JsonSerializer.Serialize(value.Value)); + } +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation) + { + var e = ThrowHelper.CreateValidationException(validation); + throw new global::System.Text.Json.JsonException(null, e); + } + + private static Speed DeserializeJson(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + } + +#nullable restore +#nullable disable + class SpeedTypeConverter : global::System.ComponentModel.TypeConverter + { + public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value) + { + global::System.Nullable ut = (global::System.Nullable)value; + return Speed.__Deserialize(ut); + } + + public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType) + { + if (value is Speed idValue) + { + return idValue.Value; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + +#nullable restore + internal sealed class SpeedDebugView + { + private readonly Speed _t; + SpeedDebugView(Speed t) + { + _t = t; + } + + public global::System.String UnderlyingType => "global::System.Nullable"; + public global::System.Nullable Value => _t.Value; + public global::System.String Conversions => @"[global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] +[global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] +"; + } + + static class ThrowHelper + { +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException(); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object."); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? ""); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenValidationFails(global::Vogen.Validation validation) + { + throw CreateValidationException(validation); + } + + internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message); + internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null."); + internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation) + { + var ex = CreateValidationException(validation.ErrorMessage); + if (validation.Data != null) + { + foreach (var kvp in validation.Data) + { + ex.Data[kvp.Key] = kvp.Value; + } + } + + return ex; + } + } + } +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/BugFixes/snapshots/snap-v8.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_bcb0b5e156ac4743.verified.txt b/tests/SnapshotTests/BugFixes/snapshots/snap-v8.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_bcb0b5e156ac4743.verified.txt new file mode 100644 index 0000000000..109b540df1 --- /dev/null +++ b/tests/SnapshotTests/BugFixes/snapshots/snap-v8.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_bcb0b5e156ac4743.verified.txt @@ -0,0 +1,386 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace generator +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory + { + public VogenTypesFactory() + { + } + + private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary> + { + { + typeof(global::ConsumerTests.Speed), + new global::System.Lazy(() => new global::ConsumerTests.Speed.SpeedSystemTextJsonConverter()) + } + }; + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value; + } +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace ConsumerTests +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + [global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] + [global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(SpeedDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.Nullable, Value = { _value }")] + internal partial record struct Speed : global::System.IEquatable, global::System.IEquatable> + { +#if DEBUG +private readonly global::System.Diagnostics.StackTrace _stackTrace = null!; +#endif +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + private readonly global::System.Nullable _value; + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly global::System.Nullable Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get + { + EnsureInitialized(); + return _value; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + init + { + value = Speed.NormalizeInput(value); + _value = value; + } + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Speed() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Speed(global::System.Nullable value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static Speed From(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + /// + /// Tries to build an instance from the provided underlying type. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, false will be returned. + /// + /// The underlying type. + /// An instance of the value object. + /// True if the value object can be built, otherwise false. + public static bool TryFrom( +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + global::System.Nullable value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] +#endif + out Speed vo) + { + value = Speed.NormalizeInput(value); + vo = new Speed(value); + return true; + } + + /// + /// Tries to build an instance from the provided underlying value. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, an error will be returned. + /// + /// The primitive value. + /// A containing either the value object, or an error. + public static global::Vogen.ValueObjectOrError TryFrom(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new global::Vogen.ValueObjectOrError(new Speed(value)); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION +#pragma warning disable CS8775 + public readonly bool IsInitialized() => true; +#pragma warning restore CS8775 +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + public static explicit operator Speed(global::System.Nullable value) => From(value); + public static explicit operator global::System.Nullable(Speed value) => value.Value; + // only called internally when something has been deserialized into + // its primitive type. + private static Speed __Deserialize(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + public readonly global::System.Boolean Equals(Speed other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if (!IsInitialized() || !other.IsInitialized()) + return false; + return global::System.Collections.Generic.EqualityComparer>.Default.Equals(Value, other.Value); + } + + public global::System.Boolean Equals(Speed other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + public readonly global::System.Boolean Equals(global::System.Nullable primitive) + { + return Value.Equals(primitive); + } + + public static global::System.Boolean operator ==(Speed left, global::System.Nullable right) => left.Value.Equals(right); + public static global::System.Boolean operator ==(global::System.Nullable left, Speed right) => right.Value.Equals(left); + public static global::System.Boolean operator !=(global::System.Nullable left, Speed right) => !(left == right); + public static global::System.Boolean operator !=(Speed left, global::System.Nullable right) => !(left == right); +#nullable disable +#nullable restore + public readonly override global::System.Int32 GetHashCode() + { + return Value is null ? 0 : Value.GetHashCode(); + } + + // record enumerates fields - we just want our Value and to throw if it's not initialized. + /// + public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]"; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + ThrowHelper.ThrowWhenNotInitialized(_stackTrace); +#else + ThrowHelper.ThrowWhenNotInitialized(); +#endif + } + } + +#nullable disable + /// + /// Converts a Speed to or from JSON. + /// + public partial class SpeedSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter + { + public override Speed Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, value.Value, options); + } + +#if NET6_0_OR_GREATER + public override Speed ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + writer.WritePropertyName(global::System.Text.Json.JsonSerializer.Serialize(value.Value)); + } +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation) + { + var e = ThrowHelper.CreateValidationException(validation); + throw new global::System.Text.Json.JsonException(null, e); + } + + private static Speed DeserializeJson(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + } + +#nullable restore +#nullable disable + class SpeedTypeConverter : global::System.ComponentModel.TypeConverter + { + public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value) + { + global::System.Nullable ut = (global::System.Nullable)value; + return Speed.__Deserialize(ut); + } + + public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType) + { + if (value is Speed idValue) + { + return idValue.Value; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + +#nullable restore +#nullable disable + internal sealed class SpeedDebugView + { + private readonly Speed _t; + SpeedDebugView(Speed t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.Nullable"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]"; +#if DEBUG + public global::System.String CreatedWith => _t._stackTrace.ToString() ?? "the From method"; +#endif + public global::System.String Conversions => @"Default"; + } + +#nullable restore + static class ThrowHelper + { +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException(); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object."); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? ""); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenValidationFails(global::Vogen.Validation validation) + { + throw CreateValidationException(validation); + } + + internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message); + internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null."); + internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation) + { + var ex = CreateValidationException(validation.ErrorMessage); + if (validation.Data != null) + { + foreach (var kvp in validation.Data) + { + ex.Data[kvp.Key] = kvp.Value; + } + } + + return ex; + } + } + } +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/BugFixes/snapshots/snap-v8.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_dbd487d12d15aac0.verified.txt b/tests/SnapshotTests/BugFixes/snapshots/snap-v8.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_dbd487d12d15aac0.verified.txt new file mode 100644 index 0000000000..dcbafea324 --- /dev/null +++ b/tests/SnapshotTests/BugFixes/snapshots/snap-v8.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_dbd487d12d15aac0.verified.txt @@ -0,0 +1,389 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace generator +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory + { + public VogenTypesFactory() + { + } + + private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary> + { + { + typeof(global::ConsumerTests.Speed), + new global::System.Lazy(() => new global::ConsumerTests.Speed.SpeedSystemTextJsonConverter()) + } + }; + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value; + } +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace ConsumerTests +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + [global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] + [global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(SpeedDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.Nullable, Value = { _value }")] + // ReSharper disable once UnusedType.Global + internal readonly partial struct Speed : global::System.IEquatable, global::System.IEquatable> + { +#if DEBUG +private readonly global::System.Diagnostics.StackTrace _stackTrace = null!; +#endif +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + private readonly global::System.Nullable _value; + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly global::System.Nullable Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get + { + EnsureInitialized(); + return _value; + } + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Speed() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Speed(global::System.Nullable value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif +#if DEBUG + _stackTrace = null; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static Speed From(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + /// + /// Tries to build an instance from the provided underlying type. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, false will be returned. + /// + /// The underlying type. + /// An instance of the value object. + /// True if the value object can be built, otherwise false. + public static bool TryFrom( +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + global::System.Nullable value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] +#endif + out Speed vo) + { + value = Speed.NormalizeInput(value); + vo = new Speed(value); + return true; + } + + /// + /// Tries to build an instance from the provided underlying value. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, an error will be returned. + /// + /// The primitive value. + /// A containing either the value object, or an error. + public static global::Vogen.ValueObjectOrError TryFrom(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new global::Vogen.ValueObjectOrError(new Speed(value)); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION +#pragma warning disable CS8775 + public readonly bool IsInitialized() => true; +#pragma warning restore CS8775 +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + public static explicit operator Speed(global::System.Nullable value) => From(value); + public static explicit operator global::System.Nullable(Speed value) => value.Value; + // only called internally when something has been deserialized into + // its primitive type. + private static Speed __Deserialize(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + public readonly global::System.Boolean Equals(Speed other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if (!IsInitialized() || !other.IsInitialized()) + return false; + return global::System.Collections.Generic.EqualityComparer>.Default.Equals(Value, other.Value); + } + + public global::System.Boolean Equals(Speed other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + public readonly global::System.Boolean Equals(global::System.Nullable primitive) + { + return Value.Equals(primitive); + } + + public readonly override global::System.Boolean Equals(global::System.Object obj) + { + return obj is Speed && Equals((Speed)obj); + } + + public static global::System.Boolean operator ==(Speed left, Speed right) => left.Equals(right); + public static global::System.Boolean operator !=(Speed left, Speed right) => !(left == right); + public static global::System.Boolean operator ==(Speed left, global::System.Nullable right) => left.Value.Equals(right); + public static global::System.Boolean operator ==(global::System.Nullable left, Speed right) => right.Value.Equals(left); + public static global::System.Boolean operator !=(global::System.Nullable left, Speed right) => !(left == right); + public static global::System.Boolean operator !=(Speed left, global::System.Nullable right) => !(left == right); +#nullable disable +#nullable restore + public readonly override global::System.Int32 GetHashCode() + { + return Value is null ? 0 : Value.GetHashCode(); + } + + /// + public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]"; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + ThrowHelper.ThrowWhenNotInitialized(_stackTrace); +#else + ThrowHelper.ThrowWhenNotInitialized(); +#endif + } + } + +#nullable disable + /// + /// Converts a Speed to or from JSON. + /// + public partial class SpeedSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter + { + public override Speed Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, value.Value, options); + } + +#if NET6_0_OR_GREATER + public override Speed ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + writer.WritePropertyName(global::System.Text.Json.JsonSerializer.Serialize(value.Value)); + } +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation) + { + var e = ThrowHelper.CreateValidationException(validation); + throw new global::System.Text.Json.JsonException(null, e); + } + + private static Speed DeserializeJson(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + } + +#nullable restore +#nullable disable + class SpeedTypeConverter : global::System.ComponentModel.TypeConverter + { + public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value) + { + global::System.Nullable ut = (global::System.Nullable)value; + return Speed.__Deserialize(ut); + } + + public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType) + { + if (value is Speed idValue) + { + return idValue.Value; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + +#nullable restore +#nullable disable + internal sealed class SpeedDebugView + { + private readonly Speed _t; + SpeedDebugView(Speed t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.Nullable"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]"; +#if DEBUG + public global::System.String CreatedWith => _t._stackTrace.ToString() ?? "the From method"; +#endif + public global::System.String Conversions => @"Default"; + } + +#nullable restore + static class ThrowHelper + { +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException(); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object."); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? ""); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenValidationFails(global::Vogen.Validation validation) + { + throw CreateValidationException(validation); + } + + internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message); + internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null."); + internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation) + { + var ex = CreateValidationException(validation.ErrorMessage); + if (validation.Data != null) + { + foreach (var kvp in validation.Data) + { + ex.Data[kvp.Key] = kvp.Value; + } + } + + return ex; + } + } + } +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/BugFixes/snapshots/snap-v9.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_1ffb354a8af60055.verified.txt b/tests/SnapshotTests/BugFixes/snapshots/snap-v9.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_1ffb354a8af60055.verified.txt new file mode 100644 index 0000000000..8df3671f73 --- /dev/null +++ b/tests/SnapshotTests/BugFixes/snapshots/snap-v9.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_1ffb354a8af60055.verified.txt @@ -0,0 +1,397 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace generator +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory + { + public VogenTypesFactory() + { + } + + private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary> + { + { + typeof(global::ConsumerTests.Speed), + new global::System.Lazy(() => new global::ConsumerTests.Speed.SpeedSystemTextJsonConverter()) + } + }; + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value; + } +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace ConsumerTests +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + [global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] + [global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(SpeedDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.Nullable, Value = { _value }")] + internal partial class Speed : global::System.IEquatable, global::System.IEquatable> + { +#if DEBUG +private readonly global::System.Diagnostics.StackTrace _stackTrace = null!; +#endif +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + private readonly global::System.Nullable _value; + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public global::System.Nullable Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get + { + EnsureInitialized(); + return _value; + } + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Speed() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Speed(global::System.Nullable value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static Speed From(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + /// + /// Tries to build an instance from the provided underlying type. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, false will be returned. + /// + /// The underlying type. + /// An instance of the value object. + /// True if the value object can be built, otherwise false. + public static bool TryFrom( +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + global::System.Nullable value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] +#endif + out Speed vo) + { + value = Speed.NormalizeInput(value); + vo = new Speed(value); + return true; + } + + /// + /// Tries to build an instance from the provided underlying value. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, an error will be returned. + /// + /// The primitive value. + /// A containing either the value object, or an error. + public static global::Vogen.ValueObjectOrError TryFrom(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new global::Vogen.ValueObjectOrError(new Speed(value)); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION +#pragma warning disable CS8775 + public bool IsInitialized() => true; +#pragma warning restore CS8775 +#else + public bool IsInitialized() => _isInitialized; +#endif + // only called internally when something has been deserialized into + // its primitive type. + private static Speed __Deserialize(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + public global::System.Boolean Equals(Speed other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if (!IsInitialized() || !other.IsInitialized()) + return false; + if (ReferenceEquals(this, other)) + { + return true; + } + + return GetType() == other.GetType() && global::System.Collections.Generic.EqualityComparer>.Default.Equals(Value, other.Value); + } + + public global::System.Boolean Equals(Speed other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + public global::System.Boolean Equals(global::System.Nullable primitive) + { + return Value.Equals(primitive); + } + + public override global::System.Boolean Equals(global::System.Object obj) + { + return Equals(obj as Speed); + } + + public static global::System.Boolean operator ==(Speed left, Speed right) => Equals(left, right); + public static global::System.Boolean operator !=(Speed left, Speed right) => !Equals(left, right); + public static global::System.Boolean operator ==(Speed left, global::System.Nullable right) => left.Value.Equals(right); + public static global::System.Boolean operator ==(global::System.Nullable left, Speed right) => right.Value.Equals(left); + public static global::System.Boolean operator !=(global::System.Nullable left, Speed right) => !(left == right); + public static global::System.Boolean operator !=(Speed left, global::System.Nullable right) => !(left == right); + public static explicit operator Speed(global::System.Nullable value) => From(value); + public static explicit operator global::System.Nullable(Speed value) => value.Value; +#nullable disable +#nullable restore + public override global::System.Int32 GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + global::System.Int32 hash = (global::System.Int32)2166136261; + hash = (hash * 16777619) ^ GetType().GetHashCode(); + hash = (hash * 16777619) ^ (Value is null ? 0 : Value.GetHashCode()); + return hash; + } + } + + /// + public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]"; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + ThrowHelper.ThrowWhenNotInitialized(_stackTrace); +#else + ThrowHelper.ThrowWhenNotInitialized(); +#endif + } + } + +#nullable disable + /// + /// Converts a Speed to or from JSON. + /// + public partial class SpeedSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter + { + public override Speed Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, value.Value, options); + } + +#if NET6_0_OR_GREATER + public override Speed ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + writer.WritePropertyName(global::System.Text.Json.JsonSerializer.Serialize(value.Value)); + } +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation) + { + var e = ThrowHelper.CreateValidationException(validation); + throw new global::System.Text.Json.JsonException(null, e); + } + + private static Speed DeserializeJson(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + } + +#nullable restore +#nullable disable + class SpeedTypeConverter : global::System.ComponentModel.TypeConverter + { + public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value) + { + global::System.Nullable ut = (global::System.Nullable)value; + return Speed.__Deserialize(ut); + } + + public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType) + { + if (value is Speed idValue) + { + return idValue.Value; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + +#nullable restore + internal sealed class SpeedDebugView + { + private readonly Speed _t; + SpeedDebugView(Speed t) + { + _t = t; + } + + public global::System.String UnderlyingType => "global::System.Nullable"; + public global::System.Nullable Value => _t.Value; + public global::System.String Conversions => @"[global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] +[global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] +"; + } + + static class ThrowHelper + { +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException(); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object."); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? ""); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenValidationFails(global::Vogen.Validation validation) + { + throw CreateValidationException(validation); + } + + internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message); + internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null."); + internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation) + { + var ex = CreateValidationException(validation.ErrorMessage); + if (validation.Data != null) + { + foreach (var kvp in validation.Data) + { + ex.Data[kvp.Key] = kvp.Value; + } + } + + return ex; + } + } + } +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/BugFixes/snapshots/snap-v9.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_319eefac00fdc78d.verified.txt b/tests/SnapshotTests/BugFixes/snapshots/snap-v9.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_319eefac00fdc78d.verified.txt new file mode 100644 index 0000000000..21be3ec3f6 --- /dev/null +++ b/tests/SnapshotTests/BugFixes/snapshots/snap-v9.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_319eefac00fdc78d.verified.txt @@ -0,0 +1,386 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace generator +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory + { + public VogenTypesFactory() + { + } + + private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary> + { + { + typeof(global::ConsumerTests.Speed), + new global::System.Lazy(() => new global::ConsumerTests.Speed.SpeedSystemTextJsonConverter()) + } + }; + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value; + } +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace ConsumerTests +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + [global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] + [global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(SpeedDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.Nullable, Value = { _value }")] + internal readonly partial record struct Speed : global::System.IEquatable, global::System.IEquatable> + { +#if DEBUG +private readonly global::System.Diagnostics.StackTrace _stackTrace = null!; +#endif +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + private readonly global::System.Nullable _value; + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly global::System.Nullable Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get + { + EnsureInitialized(); + return _value; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + init + { + value = Speed.NormalizeInput(value); + _value = value; + } + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Speed() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Speed(global::System.Nullable value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static Speed From(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + /// + /// Tries to build an instance from the provided underlying type. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, false will be returned. + /// + /// The underlying type. + /// An instance of the value object. + /// True if the value object can be built, otherwise false. + public static bool TryFrom( +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + global::System.Nullable value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] +#endif + out Speed vo) + { + value = Speed.NormalizeInput(value); + vo = new Speed(value); + return true; + } + + /// + /// Tries to build an instance from the provided underlying value. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, an error will be returned. + /// + /// The primitive value. + /// A containing either the value object, or an error. + public static global::Vogen.ValueObjectOrError TryFrom(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new global::Vogen.ValueObjectOrError(new Speed(value)); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION +#pragma warning disable CS8775 + public readonly bool IsInitialized() => true; +#pragma warning restore CS8775 +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + public static explicit operator Speed(global::System.Nullable value) => From(value); + public static explicit operator global::System.Nullable(Speed value) => value.Value; + // only called internally when something has been deserialized into + // its primitive type. + private static Speed __Deserialize(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + public readonly global::System.Boolean Equals(Speed other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if (!IsInitialized() || !other.IsInitialized()) + return false; + return global::System.Collections.Generic.EqualityComparer>.Default.Equals(Value, other.Value); + } + + public global::System.Boolean Equals(Speed other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + public readonly global::System.Boolean Equals(global::System.Nullable primitive) + { + return Value.Equals(primitive); + } + + public static global::System.Boolean operator ==(Speed left, global::System.Nullable right) => left.Value.Equals(right); + public static global::System.Boolean operator ==(global::System.Nullable left, Speed right) => right.Value.Equals(left); + public static global::System.Boolean operator !=(global::System.Nullable left, Speed right) => !(left == right); + public static global::System.Boolean operator !=(Speed left, global::System.Nullable right) => !(left == right); +#nullable disable +#nullable restore + public readonly override global::System.Int32 GetHashCode() + { + return Value is null ? 0 : Value.GetHashCode(); + } + + // record enumerates fields - we just want our Value and to throw if it's not initialized. + /// + public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]"; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + ThrowHelper.ThrowWhenNotInitialized(_stackTrace); +#else + ThrowHelper.ThrowWhenNotInitialized(); +#endif + } + } + +#nullable disable + /// + /// Converts a Speed to or from JSON. + /// + public partial class SpeedSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter + { + public override Speed Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, value.Value, options); + } + +#if NET6_0_OR_GREATER + public override Speed ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + writer.WritePropertyName(global::System.Text.Json.JsonSerializer.Serialize(value.Value)); + } +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation) + { + var e = ThrowHelper.CreateValidationException(validation); + throw new global::System.Text.Json.JsonException(null, e); + } + + private static Speed DeserializeJson(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + } + +#nullable restore +#nullable disable + class SpeedTypeConverter : global::System.ComponentModel.TypeConverter + { + public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value) + { + global::System.Nullable ut = (global::System.Nullable)value; + return Speed.__Deserialize(ut); + } + + public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType) + { + if (value is Speed idValue) + { + return idValue.Value; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + +#nullable restore +#nullable disable + internal sealed class SpeedDebugView + { + private readonly Speed _t; + SpeedDebugView(Speed t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.Nullable"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]"; +#if DEBUG + public global::System.String CreatedWith => _t._stackTrace.ToString() ?? "the From method"; +#endif + public global::System.String Conversions => @"Default"; + } + +#nullable restore + static class ThrowHelper + { +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException(); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object."); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? ""); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenValidationFails(global::Vogen.Validation validation) + { + throw CreateValidationException(validation); + } + + internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message); + internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null."); + internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation) + { + var ex = CreateValidationException(validation.ErrorMessage); + if (validation.Data != null) + { + foreach (var kvp in validation.Data) + { + ex.Data[kvp.Key] = kvp.Value; + } + } + + return ex; + } + } + } +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/BugFixes/snapshots/snap-v9.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_69223026b0859a11.verified.txt b/tests/SnapshotTests/BugFixes/snapshots/snap-v9.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_69223026b0859a11.verified.txt new file mode 100644 index 0000000000..faacab2917 --- /dev/null +++ b/tests/SnapshotTests/BugFixes/snapshots/snap-v9.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_69223026b0859a11.verified.txt @@ -0,0 +1,389 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace generator +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory + { + public VogenTypesFactory() + { + } + + private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary> + { + { + typeof(global::ConsumerTests.Speed), + new global::System.Lazy(() => new global::ConsumerTests.Speed.SpeedSystemTextJsonConverter()) + } + }; + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value; + } +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace ConsumerTests +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + [global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] + [global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(SpeedDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.Nullable, Value = { _value }")] + // ReSharper disable once UnusedType.Global + internal partial struct Speed : global::System.IEquatable, global::System.IEquatable> + { +#if DEBUG +private readonly global::System.Diagnostics.StackTrace _stackTrace = null!; +#endif +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + private readonly global::System.Nullable _value; + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly global::System.Nullable Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get + { + EnsureInitialized(); + return _value; + } + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Speed() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Speed(global::System.Nullable value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif +#if DEBUG + _stackTrace = null; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static Speed From(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + /// + /// Tries to build an instance from the provided underlying type. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, false will be returned. + /// + /// The underlying type. + /// An instance of the value object. + /// True if the value object can be built, otherwise false. + public static bool TryFrom( +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + global::System.Nullable value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] +#endif + out Speed vo) + { + value = Speed.NormalizeInput(value); + vo = new Speed(value); + return true; + } + + /// + /// Tries to build an instance from the provided underlying value. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, an error will be returned. + /// + /// The primitive value. + /// A containing either the value object, or an error. + public static global::Vogen.ValueObjectOrError TryFrom(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new global::Vogen.ValueObjectOrError(new Speed(value)); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION +#pragma warning disable CS8775 + public readonly bool IsInitialized() => true; +#pragma warning restore CS8775 +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + public static explicit operator Speed(global::System.Nullable value) => From(value); + public static explicit operator global::System.Nullable(Speed value) => value.Value; + // only called internally when something has been deserialized into + // its primitive type. + private static Speed __Deserialize(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + public readonly global::System.Boolean Equals(Speed other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if (!IsInitialized() || !other.IsInitialized()) + return false; + return global::System.Collections.Generic.EqualityComparer>.Default.Equals(Value, other.Value); + } + + public global::System.Boolean Equals(Speed other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + public readonly global::System.Boolean Equals(global::System.Nullable primitive) + { + return Value.Equals(primitive); + } + + public readonly override global::System.Boolean Equals(global::System.Object obj) + { + return obj is Speed && Equals((Speed)obj); + } + + public static global::System.Boolean operator ==(Speed left, Speed right) => left.Equals(right); + public static global::System.Boolean operator !=(Speed left, Speed right) => !(left == right); + public static global::System.Boolean operator ==(Speed left, global::System.Nullable right) => left.Value.Equals(right); + public static global::System.Boolean operator ==(global::System.Nullable left, Speed right) => right.Value.Equals(left); + public static global::System.Boolean operator !=(global::System.Nullable left, Speed right) => !(left == right); + public static global::System.Boolean operator !=(Speed left, global::System.Nullable right) => !(left == right); +#nullable disable +#nullable restore + public readonly override global::System.Int32 GetHashCode() + { + return Value is null ? 0 : Value.GetHashCode(); + } + + /// + public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]"; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + ThrowHelper.ThrowWhenNotInitialized(_stackTrace); +#else + ThrowHelper.ThrowWhenNotInitialized(); +#endif + } + } + +#nullable disable + /// + /// Converts a Speed to or from JSON. + /// + public partial class SpeedSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter + { + public override Speed Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, value.Value, options); + } + +#if NET6_0_OR_GREATER + public override Speed ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + writer.WritePropertyName(global::System.Text.Json.JsonSerializer.Serialize(value.Value)); + } +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation) + { + var e = ThrowHelper.CreateValidationException(validation); + throw new global::System.Text.Json.JsonException(null, e); + } + + private static Speed DeserializeJson(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + } + +#nullable restore +#nullable disable + class SpeedTypeConverter : global::System.ComponentModel.TypeConverter + { + public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value) + { + global::System.Nullable ut = (global::System.Nullable)value; + return Speed.__Deserialize(ut); + } + + public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType) + { + if (value is Speed idValue) + { + return idValue.Value; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + +#nullable restore +#nullable disable + internal sealed class SpeedDebugView + { + private readonly Speed _t; + SpeedDebugView(Speed t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.Nullable"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]"; +#if DEBUG + public global::System.String CreatedWith => _t._stackTrace.ToString() ?? "the From method"; +#endif + public global::System.String Conversions => @"Default"; + } + +#nullable restore + static class ThrowHelper + { +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException(); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object."); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? ""); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenValidationFails(global::Vogen.Validation validation) + { + throw CreateValidationException(validation); + } + + internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message); + internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null."); + internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation) + { + var ex = CreateValidationException(validation.ErrorMessage); + if (validation.Data != null) + { + foreach (var kvp in validation.Data) + { + ex.Data[kvp.Key] = kvp.Value; + } + } + + return ex; + } + } + } +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/BugFixes/snapshots/snap-v9.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_93100ba0c1a5ceda.verified.txt b/tests/SnapshotTests/BugFixes/snapshots/snap-v9.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_93100ba0c1a5ceda.verified.txt new file mode 100644 index 0000000000..d2c7dfd69c --- /dev/null +++ b/tests/SnapshotTests/BugFixes/snapshots/snap-v9.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_93100ba0c1a5ceda.verified.txt @@ -0,0 +1,398 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace generator +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory + { + public VogenTypesFactory() + { + } + + private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary> + { + { + typeof(global::ConsumerTests.Speed), + new global::System.Lazy(() => new global::ConsumerTests.Speed.SpeedSystemTextJsonConverter()) + } + }; + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value; + } +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace ConsumerTests +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + [global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] + [global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(SpeedDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.Nullable, Value = { _value }")] + internal partial record class Speed : global::System.IEquatable, global::System.IEquatable> + { +#if DEBUG +private readonly global::System.Diagnostics.StackTrace _stackTrace = null!; +#endif +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + private readonly global::System.Nullable _value; + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public global::System.Nullable Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get + { + EnsureInitialized(); + return _value; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + init + { + value = Speed.NormalizeInput(value); + _value = value; + } + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Speed() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Speed(global::System.Nullable value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static Speed From(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + /// + /// Tries to build an instance from the provided underlying type. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, false will be returned. + /// + /// The underlying type. + /// An instance of the value object. + /// True if the value object can be built, otherwise false. + public static bool TryFrom( +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + global::System.Nullable value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] +#endif + out Speed vo) + { + value = Speed.NormalizeInput(value); + vo = new Speed(value); + return true; + } + + /// + /// Tries to build an instance from the provided underlying value. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, an error will be returned. + /// + /// The primitive value. + /// A containing either the value object, or an error. + public static global::Vogen.ValueObjectOrError TryFrom(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new global::Vogen.ValueObjectOrError(new Speed(value)); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION +#pragma warning disable CS8775 + public bool IsInitialized() => true; +#pragma warning restore CS8775 +#else + public bool IsInitialized() => _isInitialized; +#endif + // only called internally when something has been deserialized into + // its primitive type. + private static Speed __Deserialize(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + public virtual global::System.Boolean Equals(Speed other) + { + if (ReferenceEquals(null, other)) + { + return false; + } + + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if (!IsInitialized() || !other.IsInitialized()) + return false; + if (ReferenceEquals(this, other)) + { + return true; + } + + return GetType() == other.GetType() && global::System.Collections.Generic.EqualityComparer>.Default.Equals(Value, other.Value); + } + + public global::System.Boolean Equals(Speed other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + public global::System.Boolean Equals(global::System.Nullable primitive) + { + return Value.Equals(primitive); + } + + public static global::System.Boolean operator ==(Speed left, global::System.Nullable right) => left.Value.Equals(right); + public static global::System.Boolean operator ==(global::System.Nullable left, Speed right) => right.Value.Equals(left); + public static global::System.Boolean operator !=(global::System.Nullable left, Speed right) => !(left == right); + public static global::System.Boolean operator !=(Speed left, global::System.Nullable right) => !(left == right); + public static explicit operator Speed(global::System.Nullable value) => From(value); + public static explicit operator global::System.Nullable(Speed value) => value.Value; +#nullable disable +#nullable restore + public override global::System.Int32 GetHashCode() + { + unchecked // Overflow is fine, just wrap + { + global::System.Int32 hash = (global::System.Int32)2166136261; + hash = (hash * 16777619) ^ GetType().GetHashCode(); + hash = (hash * 16777619) ^ (Value is null ? 0 : Value.GetHashCode()); + return hash; + } + } + + // record enumerates fields - we just want our Value and to throw if it's not initialized. + /// + public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]"; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + ThrowHelper.ThrowWhenNotInitialized(_stackTrace); +#else + ThrowHelper.ThrowWhenNotInitialized(); +#endif + } + } + +#nullable disable + /// + /// Converts a Speed to or from JSON. + /// + public partial class SpeedSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter + { + public override Speed Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, value.Value, options); + } + +#if NET6_0_OR_GREATER + public override Speed ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + writer.WritePropertyName(global::System.Text.Json.JsonSerializer.Serialize(value.Value)); + } +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation) + { + var e = ThrowHelper.CreateValidationException(validation); + throw new global::System.Text.Json.JsonException(null, e); + } + + private static Speed DeserializeJson(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + } + +#nullable restore +#nullable disable + class SpeedTypeConverter : global::System.ComponentModel.TypeConverter + { + public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value) + { + global::System.Nullable ut = (global::System.Nullable)value; + return Speed.__Deserialize(ut); + } + + public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType) + { + if (value is Speed idValue) + { + return idValue.Value; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + +#nullable restore + internal sealed class SpeedDebugView + { + private readonly Speed _t; + SpeedDebugView(Speed t) + { + _t = t; + } + + public global::System.String UnderlyingType => "global::System.Nullable"; + public global::System.Nullable Value => _t.Value; + public global::System.String Conversions => @"[global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] +[global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] +"; + } + + static class ThrowHelper + { +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException(); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object."); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? ""); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenValidationFails(global::Vogen.Validation validation) + { + throw CreateValidationException(validation); + } + + internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message); + internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null."); + internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation) + { + var ex = CreateValidationException(validation.ErrorMessage); + if (validation.Data != null) + { + foreach (var kvp in validation.Data) + { + ex.Data[kvp.Key] = kvp.Value; + } + } + + return ex; + } + } + } +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/BugFixes/snapshots/snap-v9.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_bcb0b5e156ac4743.verified.txt b/tests/SnapshotTests/BugFixes/snapshots/snap-v9.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_bcb0b5e156ac4743.verified.txt new file mode 100644 index 0000000000..109b540df1 --- /dev/null +++ b/tests/SnapshotTests/BugFixes/snapshots/snap-v9.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_bcb0b5e156ac4743.verified.txt @@ -0,0 +1,386 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace generator +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory + { + public VogenTypesFactory() + { + } + + private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary> + { + { + typeof(global::ConsumerTests.Speed), + new global::System.Lazy(() => new global::ConsumerTests.Speed.SpeedSystemTextJsonConverter()) + } + }; + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value; + } +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace ConsumerTests +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + [global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] + [global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(SpeedDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.Nullable, Value = { _value }")] + internal partial record struct Speed : global::System.IEquatable, global::System.IEquatable> + { +#if DEBUG +private readonly global::System.Diagnostics.StackTrace _stackTrace = null!; +#endif +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + private readonly global::System.Nullable _value; + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly global::System.Nullable Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get + { + EnsureInitialized(); + return _value; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + init + { + value = Speed.NormalizeInput(value); + _value = value; + } + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Speed() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Speed(global::System.Nullable value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static Speed From(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + /// + /// Tries to build an instance from the provided underlying type. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, false will be returned. + /// + /// The underlying type. + /// An instance of the value object. + /// True if the value object can be built, otherwise false. + public static bool TryFrom( +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + global::System.Nullable value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] +#endif + out Speed vo) + { + value = Speed.NormalizeInput(value); + vo = new Speed(value); + return true; + } + + /// + /// Tries to build an instance from the provided underlying value. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, an error will be returned. + /// + /// The primitive value. + /// A containing either the value object, or an error. + public static global::Vogen.ValueObjectOrError TryFrom(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new global::Vogen.ValueObjectOrError(new Speed(value)); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION +#pragma warning disable CS8775 + public readonly bool IsInitialized() => true; +#pragma warning restore CS8775 +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + public static explicit operator Speed(global::System.Nullable value) => From(value); + public static explicit operator global::System.Nullable(Speed value) => value.Value; + // only called internally when something has been deserialized into + // its primitive type. + private static Speed __Deserialize(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + public readonly global::System.Boolean Equals(Speed other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if (!IsInitialized() || !other.IsInitialized()) + return false; + return global::System.Collections.Generic.EqualityComparer>.Default.Equals(Value, other.Value); + } + + public global::System.Boolean Equals(Speed other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + public readonly global::System.Boolean Equals(global::System.Nullable primitive) + { + return Value.Equals(primitive); + } + + public static global::System.Boolean operator ==(Speed left, global::System.Nullable right) => left.Value.Equals(right); + public static global::System.Boolean operator ==(global::System.Nullable left, Speed right) => right.Value.Equals(left); + public static global::System.Boolean operator !=(global::System.Nullable left, Speed right) => !(left == right); + public static global::System.Boolean operator !=(Speed left, global::System.Nullable right) => !(left == right); +#nullable disable +#nullable restore + public readonly override global::System.Int32 GetHashCode() + { + return Value is null ? 0 : Value.GetHashCode(); + } + + // record enumerates fields - we just want our Value and to throw if it's not initialized. + /// + public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]"; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + ThrowHelper.ThrowWhenNotInitialized(_stackTrace); +#else + ThrowHelper.ThrowWhenNotInitialized(); +#endif + } + } + +#nullable disable + /// + /// Converts a Speed to or from JSON. + /// + public partial class SpeedSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter + { + public override Speed Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, value.Value, options); + } + +#if NET6_0_OR_GREATER + public override Speed ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + writer.WritePropertyName(global::System.Text.Json.JsonSerializer.Serialize(value.Value)); + } +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation) + { + var e = ThrowHelper.CreateValidationException(validation); + throw new global::System.Text.Json.JsonException(null, e); + } + + private static Speed DeserializeJson(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + } + +#nullable restore +#nullable disable + class SpeedTypeConverter : global::System.ComponentModel.TypeConverter + { + public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value) + { + global::System.Nullable ut = (global::System.Nullable)value; + return Speed.__Deserialize(ut); + } + + public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType) + { + if (value is Speed idValue) + { + return idValue.Value; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + +#nullable restore +#nullable disable + internal sealed class SpeedDebugView + { + private readonly Speed _t; + SpeedDebugView(Speed t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.Nullable"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]"; +#if DEBUG + public global::System.String CreatedWith => _t._stackTrace.ToString() ?? "the From method"; +#endif + public global::System.String Conversions => @"Default"; + } + +#nullable restore + static class ThrowHelper + { +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException(); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object."); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? ""); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenValidationFails(global::Vogen.Validation validation) + { + throw CreateValidationException(validation); + } + + internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message); + internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null."); + internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation) + { + var ex = CreateValidationException(validation.ErrorMessage); + if (validation.Data != null) + { + foreach (var kvp in validation.Data) + { + ex.Data[kvp.Key] = kvp.Value; + } + } + + return ex; + } + } + } +} +] \ No newline at end of file diff --git a/tests/SnapshotTests/BugFixes/snapshots/snap-v9.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_dbd487d12d15aac0.verified.txt b/tests/SnapshotTests/BugFixes/snapshots/snap-v9.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_dbd487d12d15aac0.verified.txt new file mode 100644 index 0000000000..dcbafea324 --- /dev/null +++ b/tests/SnapshotTests/BugFixes/snapshots/snap-v9.0/Bug914Tests.NullableValueType_underlying_generates_null_safe_GetHashCode_dbd487d12d15aac0.verified.txt @@ -0,0 +1,389 @@ +[ +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace generator +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + public class VogenTypesFactory : global::System.Text.Json.Serialization.JsonConverterFactory + { + public VogenTypesFactory() + { + } + + private static readonly global::System.Collections.Generic.Dictionary> _lookup = new global::System.Collections.Generic.Dictionary> + { + { + typeof(global::ConsumerTests.Speed), + new global::System.Lazy(() => new global::ConsumerTests.Speed.SpeedSystemTextJsonConverter()) + } + }; + public override bool CanConvert(global::System.Type typeToConvert) => _lookup.ContainsKey(typeToConvert); + public override global::System.Text.Json.Serialization.JsonConverter CreateConverter(global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) => _lookup[typeToConvert].Value; + } +} + +// ------------------------------------------------------------------------------ +// +// This code was generated by a source generator named Vogen (https://github.com/SteveDunn/Vogen) +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +// ------------------------------------------------------------------------------ +// Suppress warnings about [Obsolete] member usage in generated code. +#pragma warning disable CS0618 +// Suppress warnings for 'Override methods on comparable types'. +#pragma warning disable CA1036 +// Suppress Error MA0097 : A class that implements IComparable or IComparable should override comparison operators +#pragma warning disable MA0097 +// Suppress warning for 'The annotation for nullable reference types should only be used in code within a '#nullable' annotations context. Auto-generated code requires an explicit '#nullable' directive in source.' +// The generator copies signatures from the BCL, e.g. for `TryParse`, and some of those have nullable annotations. +#pragma warning disable CS8669, CS8632 +#pragma warning disable CS8604 // Possible null reference argument. + +// Suppress warnings about CS1591: Missing XML comment for publicly visible type or member 'Type_or_Member' +#pragma warning disable CS1591 +// Suppress warnings about CS8767 : Nullability of reference types in type of parameter doesn't match implicitly implemented member (possibly because of nullability attributes). +#pragma warning disable CS8767 +namespace ConsumerTests +{ + [global::System.Diagnostics.CodeAnalysis.ExcludeFromCodeCoverage] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Vogen", "1.0.0.0")] + [global::System.Text.Json.Serialization.JsonConverter(typeof(SpeedSystemTextJsonConverter))] + [global::System.ComponentModel.TypeConverter(typeof(SpeedTypeConverter))] + [global::System.Diagnostics.DebuggerTypeProxyAttribute(typeof(SpeedDebugView))] + [global::System.Diagnostics.DebuggerDisplayAttribute("Underlying type: global::System.Nullable, Value = { _value }")] + // ReSharper disable once UnusedType.Global + internal readonly partial struct Speed : global::System.IEquatable, global::System.IEquatable> + { +#if DEBUG +private readonly global::System.Diagnostics.StackTrace _stackTrace = null!; +#endif +#if !VOGEN_NO_VALIDATION + private readonly global::System.Boolean _isInitialized; +#endif + private readonly global::System.Nullable _value; + /// + /// Gets the underlying value if set, otherwise a is thrown. + /// + public readonly global::System.Nullable Value + { + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get + { + EnsureInitialized(); + return _value; + } + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + public Speed() + { +#if DEBUG + _stackTrace = new global::System.Diagnostics.StackTrace(); +#endif +#if !VOGEN_NO_VALIDATION + _isInitialized = false; +#endif + _value = default; + } + + [global::System.Diagnostics.DebuggerStepThroughAttribute] + private Speed(global::System.Nullable value) + { + _value = value; +#if !VOGEN_NO_VALIDATION + _isInitialized = true; +#endif +#if DEBUG + _stackTrace = null; +#endif + } + + /// + /// Builds an instance from the provided underlying type. + /// + /// The underlying type. + /// An instance of this type. + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public static Speed From(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + /// + /// Tries to build an instance from the provided underlying type. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, false will be returned. + /// + /// The underlying type. + /// An instance of the value object. + /// True if the value object can be built, otherwise false. + public static bool TryFrom( +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.NotNullWhen(true)] +#endif + global::System.Nullable value, +#if NETCOREAPP3_0_OR_GREATER +[global::System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] +#endif + out Speed vo) + { + value = Speed.NormalizeInput(value); + vo = new Speed(value); + return true; + } + + /// + /// Tries to build an instance from the provided underlying value. + /// If a normalization method is provided, it will be called. + /// If validation is provided, and it fails, an error will be returned. + /// + /// The primitive value. + /// A containing either the value object, or an error. + public static global::Vogen.ValueObjectOrError TryFrom(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new global::Vogen.ValueObjectOrError(new Speed(value)); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +#if VOGEN_NO_VALIDATION +#pragma warning disable CS8775 + public readonly bool IsInitialized() => true; +#pragma warning restore CS8775 +#else + public readonly bool IsInitialized() => _isInitialized; +#endif + public static explicit operator Speed(global::System.Nullable value) => From(value); + public static explicit operator global::System.Nullable(Speed value) => value.Value; + // only called internally when something has been deserialized into + // its primitive type. + private static Speed __Deserialize(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + + public readonly global::System.Boolean Equals(Speed other) + { + // It's possible to create uninitialized instances via converters such as EfCore (HasDefaultValue), which call Equals. + // We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type. + if (!IsInitialized() || !other.IsInitialized()) + return false; + return global::System.Collections.Generic.EqualityComparer>.Default.Equals(Value, other.Value); + } + + public global::System.Boolean Equals(Speed other, global::System.Collections.Generic.IEqualityComparer comparer) + { + return comparer.Equals(this, other); + } + + public readonly global::System.Boolean Equals(global::System.Nullable primitive) + { + return Value.Equals(primitive); + } + + public readonly override global::System.Boolean Equals(global::System.Object obj) + { + return obj is Speed && Equals((Speed)obj); + } + + public static global::System.Boolean operator ==(Speed left, Speed right) => left.Equals(right); + public static global::System.Boolean operator !=(Speed left, Speed right) => !(left == right); + public static global::System.Boolean operator ==(Speed left, global::System.Nullable right) => left.Value.Equals(right); + public static global::System.Boolean operator ==(global::System.Nullable left, Speed right) => right.Value.Equals(left); + public static global::System.Boolean operator !=(global::System.Nullable left, Speed right) => !(left == right); + public static global::System.Boolean operator !=(Speed left, global::System.Nullable right) => !(left == right); +#nullable disable +#nullable restore + public readonly override global::System.Int32 GetHashCode() + { + return Value is null ? 0 : Value.GetHashCode(); + } + + /// + public override global::System.String ToString() => IsInitialized() ? Value.ToString() ?? "" : "[UNINITIALIZED]"; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + private readonly void EnsureInitialized() + { + if (!IsInitialized()) + { +#if DEBUG + ThrowHelper.ThrowWhenNotInitialized(_stackTrace); +#else + ThrowHelper.ThrowWhenNotInitialized(); +#endif + } + } + +#nullable disable + /// + /// Converts a Speed to or from JSON. + /// + public partial class SpeedSystemTextJsonConverter : global::System.Text.Json.Serialization.JsonConverter + { + public override Speed Read(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void Write(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + global::System.Text.Json.JsonSerializer.Serialize(writer, value.Value, options); + } + +#if NET6_0_OR_GREATER + public override Speed ReadAsPropertyName(ref global::System.Text.Json.Utf8JsonReader reader, global::System.Type typeToConvert, global::System.Text.Json.JsonSerializerOptions options) + { + var primitive = global::System.Text.Json.JsonSerializer.Deserialize>(ref reader, options); + return DeserializeJson(primitive); + } + + public override void WriteAsPropertyName(global::System.Text.Json.Utf8JsonWriter writer, Speed value, global::System.Text.Json.JsonSerializerOptions options) + { + writer.WritePropertyName(global::System.Text.Json.JsonSerializer.Serialize(value.Value)); + } +#endif +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + private static void ThrowJsonExceptionWhenValidationFails(global::Vogen.Validation validation) + { + var e = ThrowHelper.CreateValidationException(validation); + throw new global::System.Text.Json.JsonException(null, e); + } + + private static Speed DeserializeJson(global::System.Nullable value) + { + value = Speed.NormalizeInput(value); + return new Speed(value); + } + } + +#nullable restore +#nullable disable + class SpeedTypeConverter : global::System.ComponentModel.TypeConverter + { + public override global::System.Boolean CanConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override global::System.Object ConvertFrom(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value) + { + global::System.Nullable ut = (global::System.Nullable)value; + return Speed.__Deserialize(ut); + } + + public override bool CanConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Type sourceType) + { + return sourceType == typeof(global::System.Nullable); + } + + public override object ConvertTo(global::System.ComponentModel.ITypeDescriptorContext context, global::System.Globalization.CultureInfo culture, global::System.Object value, global::System.Type destinationType) + { + if (value is Speed idValue) + { + return idValue.Value; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + } + +#nullable restore +#nullable disable + internal sealed class SpeedDebugView + { + private readonly Speed _t; + SpeedDebugView(Speed t) + { + _t = t; + } + + public global::System.Boolean IsInitialized => _t.IsInitialized(); + public global::System.String UnderlyingType => "System.Nullable"; + public global::System.String Value => _t.IsInitialized() ? _t._value.ToString() : "[not initialized]"; +#if DEBUG + public global::System.String CreatedWith => _t._stackTrace.ToString() ?? "the From method"; +#endif + public global::System.String Conversions => @"Default"; + } + +#nullable restore + static class ThrowHelper + { +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowInvalidOperationException(string message) => throw new global::System.InvalidOperationException(message); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowArgumentException(string message, string arg) => throw new global::System.ArgumentException(message, arg); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenCreatedWithNull() => throw CreateCannotBeNullException(); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized() => throw CreateValidationException("Use of uninitialized Value Object."); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenNotInitialized(global::System.Diagnostics.StackTrace stackTrace) => throw CreateValidationException("Use of uninitialized Value Object at: " + stackTrace ?? ""); +#if NETCOREAPP3_0_OR_GREATER + [global::System.Diagnostics.CodeAnalysis.DoesNotReturnAttribute] +#endif + internal static void ThrowWhenValidationFails(global::Vogen.Validation validation) + { + throw CreateValidationException(validation); + } + + internal static global::System.Exception CreateValidationException(string message) => new global::Vogen.ValueObjectValidationException(message); + internal static global::System.Exception CreateCannotBeNullException() => new global::Vogen.ValueObjectValidationException("Cannot create a value object with null."); + internal static global::System.Exception CreateValidationException(global::Vogen.Validation validation) + { + var ex = CreateValidationException(validation.ErrorMessage); + if (validation.Data != null) + { + foreach (var kvp in validation.Data) + { + ex.Data[kvp.Key] = kvp.Value; + } + } + + return ex; + } + } + } +} +] \ No newline at end of file