Skip to content

Commit

Permalink
Merge pull request #7 from altasoft/bugfix/RefTypeImplicitOperators
Browse files Browse the repository at this point in the history
Fixed a bug in implicit operator generation
  • Loading branch information
temonk authored Mar 11, 2024
2 parents 1d3ee0e + b32d5ff commit 14de232
Show file tree
Hide file tree
Showing 39 changed files with 1,657 additions and 22 deletions.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
<Product>Domain Primitives</Product>
<Company>ALTA Software llc.</Company>
<Copyright>Copyright © 2024 ALTA Software llc.</Copyright>
<Version>2.0.1</Version>
<Version>2.0.2</Version>
</PropertyGroup>

<PropertyGroup>
Expand Down
60 changes: 41 additions & 19 deletions src/AltaSoft.DomainPrimitives.Generator/Executor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -662,51 +662,73 @@ static StringBuilder AppendInterface(StringBuilder sb, string interfaceName)
private static void GenerateImplicitOperators(GeneratorData data, SourceCodeBuilder builder)
{
var friendlyName = data.PrimitiveTypeFriendlyName;
var type = data.PrimitiveTypeSymbol;
var className = data.ClassName;

// From Underlying to our type
if (data.TypeSymbol.IsValueType)
{
builder.AppendSummary($"Implicit conversion from <see cref = \"{friendlyName}\"/> to <see cref = \"{data.ClassName}\"/>")
builder.AppendSummary($"Implicit conversion from <see cref = \"{friendlyName}\"/> to <see cref = \"{className}\"/>")
.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]")
.Append($"public static implicit operator {data.ClassName}({friendlyName} value)")
.AppendLine(" => new(value);")
.Append($"public static implicit operator {className}({friendlyName} value)").AppendLine(" => new(value);")
.NewLine();
}

var type = data.PrimitiveTypeSymbol;

builder.AppendSummary($"Implicit conversion from <see cref = \"{friendlyName}\"/> (nullable) to <see cref = \"{data.ClassName}\"/> (nullable)")
builder.AppendSummary($"Implicit conversion from <see cref = \"{friendlyName}\"/> (nullable) to <see cref = \"{className}\"/> (nullable)")
.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]")
.AppendLine("[return: NotNullIfNotNull(nameof(value))]")
.Append($"public static implicit operator {data.ClassName}?({friendlyName}? value)")
.Append($"public static implicit operator {className}?({friendlyName}? value)")
.AppendLine($" => value is null ? null : new(value{(type.IsValueType ? ".Value" : "")});")
.NewLine();

// From our type to underlying type
if (data.TypeSymbol.IsValueType)
{
builder.AppendSummary($"Implicit conversion from <see cref = \"{className}\"/> to <see cref = \"{friendlyName}\"/>")
.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]")
.Append($"public static implicit operator {friendlyName}({className} value)").AppendLine($" => ({friendlyName})value.{data.FieldName};")
.NewLine();
}

builder.AppendSummary($"Implicit conversion from <see cref = \"{className}\"/> (nullable) to <see cref = \"{friendlyName}\"/> (nullable)")
.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]")
.AppendLine("[return: NotNullIfNotNull(nameof(value))]")
.Append($"public static implicit operator {friendlyName}?({className}? value)")
.AppendLine($" => value is null ? null : ({friendlyName}?)value{(type.IsValueType ? ".Value" : "")}.{data.FieldName};")
.NewLine();

if (data.ParentSymbols.Count != 0)
{
builder.AppendSummary($"Implicit conversion from <see cref = \"{data.ParentSymbols[0].Name}\"/> to <see cref = \"{data.ClassName}\"/>")
var parentClassName = data.ParentSymbols[0].Name;

if (data.TypeSymbol.IsValueType)
{
builder.AppendSummary($"Implicit conversion from <see cref = \"{parentClassName}\"/> to <see cref = \"{className}\"/>")
.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]")
.Append($"public static implicit operator {data.ClassName}({data.ParentSymbols[0].Name} value)")
.Append($"public static implicit operator {className}({parentClassName} value)")
.AppendLine(" => new(value);")
.NewLine();
}
}

builder.AppendSummary($"Implicit conversion from <see cref = \"{data.ClassName}\"/> to <see cref = \"{friendlyName}\"/>")
.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]")
.Append($"public static implicit operator {friendlyName}({data.ClassName} value)")
.AppendLine($" => ({friendlyName})value.{data.FieldName};")
.NewLine();
builder.AppendSummary($"Implicit conversion from <see cref = \"{parentClassName}\"/> (nullable) to <see cref = \"{className}\"/> (nullable)")
.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]")
.AppendLine("[return: NotNullIfNotNull(nameof(value))]")
.Append($"public static implicit operator {className}?({parentClassName}? value)")
.AppendLine($" => value is null ? null : ({className}?)value{(type.IsValueType ? ".Value" : "")}.{data.FieldName};")
.NewLine();
}

if (data.UnderlyingType is DomainPrimitiveUnderlyingType.DateOnly or DomainPrimitiveUnderlyingType.TimeOnly)
{
builder.AppendSummary($"Implicit conversion from <see cref = \"{data.ClassName}\"/> to <see cref = \"DateTime\"/>")
builder.AppendSummary($"Implicit conversion from <see cref = \"{className}\"/> to <see cref = \"DateTime\"/>")
.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]")
.Append($"public static implicit operator DateTime({data.ClassName} value)")
.Append($"public static implicit operator DateTime({className} value)")
.AppendLine($" => (({friendlyName})value.{data.FieldName}).ToDateTime();")
.NewLine();

builder.AppendSummary($"Implicit conversion from <see cref = \"DateTime\"/> to <see cref = \"{data.ClassName}\"/>")
builder.AppendSummary($"Implicit conversion from <see cref = \"DateTime\"/> to <see cref = \"{className}\"/>")
.AppendLine("[MethodImpl(MethodImplOptions.AggressiveInlining)]")
.Append($"public static implicit operator {data.ClassName}(DateTime value)")
.Append($"public static implicit operator {className}(DateTime value)")
.AppendLine($" => {data.UnderlyingType}.FromDateTime(value);")
.NewLine();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,9 @@ public SourceCodeBuilder(int startingIndent = 0)
/// Returns the length of the new line character(s) used in the current environment.
/// </summary>
/// <returns>The length of the new line character(s).</returns>
#pragma warning disable CA1822
public int GetNewLineLength() => s_newLineLength;
#pragma warning restore CA1822

/// <summary>
/// Returns a string that represents the specified number of indentation chars.
Expand Down
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -623,6 +623,95 @@ public static void Validate(char value)
return TestHelper.Verify(source, (_, x, _) => Assert.Equal(4, x.Count));
}

[Fact]
public Task StringOfStringValue_GeneratesAllInterfacesAndConverters()
{
const string source = """
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AltaSoft.DomainPrimitives.Abstractions;
namespace AltaSoft.DomainPrimitives;
/// <inheritdoc/>
public partial class StringValue : IDomainValue<string>
{
/// <inheritdoc/>
public static void Validate(string value)
{
if (value=="Test")
throw new InvalidDomainValueException("Invalid Value");
}
/// <inheritdoc/>
public static string Default => default;
}
/// <inheritdoc/>
public partial class StringOfStringValue : IDomainValue<StringValue>
{
/// <inheritdoc/>
public static void Validate(StringValue value)
{
if (value=="Test")
throw new InvalidDomainValueException("Invalid Value");
}
/// <inheritdoc/>
public static StringValue Default => default;
}
""";

return TestHelper.Verify(source, (_, x, _) => Assert.Equal(7, x.Count));
}

[Fact]
public Task IntOfIntValue_GeneratesAllInterfacesAndConverters()
{
const string source = """
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using AltaSoft.DomainPrimitives.Abstractions;
namespace AltaSoft.DomainPrimitives;
/// <inheritdoc/>
public readonly partial struct IntValue : IDomainValue<int>
{
/// <inheritdoc/>
public static void Validate(int value)
{
if (value < 10 || value > 20)
throw new InvalidDomainValueException("Invalid Value");
}
/// <inheritdoc/>
public static int Default => default;
}
/// <inheritdoc/>
public readonly partial struct IntOfIntValue : IDomainValue<IntValue>
{
/// <inheritdoc/>
public static void Validate(IntValue value)
{
if (value < 10 || value > 20)
throw new InvalidDomainValueException("Invalid Value");
}
/// <inheritdoc/>
public static IntValue Default => default;
}
""";

return TestHelper.Verify(source, (_, x, _) => Assert.Equal(7, x.Count));
}
public static class TestHelper
{
internal static Task Verify(string source, Action<ImmutableArray<Diagnostic>, List<string>, GeneratorDriver>? additionalChecks = null, DomainPrimitiveGlobalOptions? options = null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,13 @@ public int CompareTo(object? obj)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator bool(BoolValue value) => (bool)value._value;

/// <summary>
/// Implicit conversion from <see cref = "BoolValue"/> (nullable) to <see cref = "bool"/> (nullable)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[return: NotNullIfNotNull(nameof(value))]
public static implicit operator bool?(BoolValue? value) => value is null ? null : (bool?)value.Value._value;

/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static BoolValue Parse(string s, IFormatProvider? provider) => bool.Parse(s);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ public int CompareTo(object? obj)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator byte(ByteValue value) => (byte)value._value;

/// <summary>
/// Implicit conversion from <see cref = "ByteValue"/> (nullable) to <see cref = "byte"/> (nullable)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[return: NotNullIfNotNull(nameof(value))]
public static implicit operator byte?(ByteValue? value) => value is null ? null : (byte?)value.Value._value;

/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator <(ByteValue left, ByteValue right) => left._value < right._value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ public int CompareTo(object? obj)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator char(CharValue value) => (char)value._value;

/// <summary>
/// Implicit conversion from <see cref = "CharValue"/> (nullable) to <see cref = "char"/> (nullable)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[return: NotNullIfNotNull(nameof(value))]
public static implicit operator char?(CharValue? value) => value is null ? null : (char?)value.Value._value;

/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator <(CharValue left, CharValue right) => left._value < right._value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,13 @@ public int CompareTo(object? obj)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator DateOnly(DateOnlyValue value) => (DateOnly)value._value;

/// <summary>
/// Implicit conversion from <see cref = "DateOnlyValue"/> (nullable) to <see cref = "DateOnly"/> (nullable)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[return: NotNullIfNotNull(nameof(value))]
public static implicit operator DateOnly?(DateOnlyValue? value) => value is null ? null : (DateOnly?)value.Value._value;

/// <summary>
/// Implicit conversion from <see cref = "DateOnlyValue"/> to <see cref = "DateTime"/>
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ public int CompareTo(object? obj)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator DateTimeOffset(DateTimeOffsetValue value) => (DateTimeOffset)value._value;

/// <summary>
/// Implicit conversion from <see cref = "DateTimeOffsetValue"/> (nullable) to <see cref = "DateTimeOffset"/> (nullable)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[return: NotNullIfNotNull(nameof(value))]
public static implicit operator DateTimeOffset?(DateTimeOffsetValue? value) => value is null ? null : (DateTimeOffset?)value.Value._value;

/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator <(DateTimeOffsetValue left, DateTimeOffsetValue right) => left._value < right._value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,13 @@ public int CompareTo(object? obj)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator DateTime(DateTimeValue value) => (DateTime)value._value;

/// <summary>
/// Implicit conversion from <see cref = "DateTimeValue"/> (nullable) to <see cref = "DateTime"/> (nullable)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[return: NotNullIfNotNull(nameof(value))]
public static implicit operator DateTime?(DateTimeValue? value) => value is null ? null : (DateTime?)value.Value._value;

/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator <(DateTimeValue left, DateTimeValue right) => left._value < right._value;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ public int CompareTo(object? obj)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator decimal(DecimalValue value) => (decimal)value._value;

/// <summary>
/// Implicit conversion from <see cref = "DecimalValue"/> (nullable) to <see cref = "decimal"/> (nullable)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[return: NotNullIfNotNull(nameof(value))]
public static implicit operator decimal?(DecimalValue? value) => value is null ? null : (decimal?)value.Value._value;

/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static DecimalValue operator +(DecimalValue left, DecimalValue right) => new(left._value + right._value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ public int CompareTo(object? obj)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator double(DoubleValue value) => (double)value._value;

/// <summary>
/// Implicit conversion from <see cref = "DoubleValue"/> (nullable) to <see cref = "double"/> (nullable)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[return: NotNullIfNotNull(nameof(value))]
public static implicit operator double?(DoubleValue? value) => value is null ? null : (double?)value.Value._value;

/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static DoubleValue operator +(DoubleValue left, DoubleValue right) => new(left._value + right._value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,13 @@ public int CompareTo(object? obj)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator float(FloatValue value) => (float)value._value;

/// <summary>
/// Implicit conversion from <see cref = "FloatValue"/> (nullable) to <see cref = "float"/> (nullable)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[return: NotNullIfNotNull(nameof(value))]
public static implicit operator float?(FloatValue? value) => value is null ? null : (float?)value.Value._value;

/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static FloatValue operator +(FloatValue left, FloatValue right) => new(left._value + right._value);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,13 @@ public int CompareTo(object? obj)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator Guid(GuidValue value) => (Guid)value._value;

/// <summary>
/// Implicit conversion from <see cref = "GuidValue"/> (nullable) to <see cref = "Guid"/> (nullable)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[return: NotNullIfNotNull(nameof(value))]
public static implicit operator Guid?(GuidValue? value) => value is null ? null : (Guid?)value.Value._value;

/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static GuidValue Parse(string s, IFormatProvider? provider) => Guid.Parse(s, provider);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ public int CompareTo(object? obj)
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static implicit operator short(ShortValue value) => (short)value._value;

/// <summary>
/// Implicit conversion from <see cref = "ShortValue"/> (nullable) to <see cref = "short"/> (nullable)
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
[return: NotNullIfNotNull(nameof(value))]
public static implicit operator short?(ShortValue? value) => value is null ? null : (short?)value.Value._value;

/// <inheritdoc/>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static bool operator <(ShortValue left, ShortValue right) => left._value < right._value;
Expand Down
Loading

0 comments on commit 14de232

Please sign in to comment.