Skip to content

Commit

Permalink
Merge pull request #186 from SteveDunn/allow-override-tostring
Browse files Browse the repository at this point in the history
Allow user to supply their own ToString
  • Loading branch information
SteveDunn authored Jul 21, 2022
2 parents 83d476b + 9c1749a commit 0dfa066
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 18 deletions.
47 changes: 47 additions & 0 deletions src/Vogen/BuildWorkItems.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ internal static class BuildWorkItems
ReportErrorIfNestedType(target, context, voSymbolInformation);

var instanceProperties = TryBuildInstanceProperties(attributes, voSymbolInformation, context);

var hasToString = HasToStringOverload(voSymbolInformation);

MethodDeclarationSyntax? validateMethod = null;
MethodDeclarationSyntax? normalizeInputMethod = null;
Expand Down Expand Up @@ -89,6 +91,7 @@ internal static class BuildWorkItems
InstanceProperties = instanceProperties.ToList(),
TypeToAugment = voTypeSyntax,
IsValueType = isValueType,
HasToString = hasToString,
UnderlyingType = config.UnderlyingType,
Conversions = config.Conversions,
Customizations = config.Customizations,
Expand All @@ -99,6 +102,50 @@ internal static class BuildWorkItems
};
}

private static bool HasToStringOverload(ITypeSymbol typeSymbol)
{
while (true)
{
var toStringMethods = typeSymbol.GetMembers("ToString").OfType<IMethodSymbol>();

foreach (IMethodSymbol eachMethod in toStringMethods)
{
// we could have "public virtual new string ToString() => "xxx"
if (!eachMethod.IsOverride && !eachMethod.IsVirtual)
{
continue;
}

// can't change access rights
if (eachMethod.DeclaredAccessibility != Accessibility.Public && eachMethod.DeclaredAccessibility != Accessibility.Protected)
{
continue;
}

if (eachMethod.Parameters.Length != 0)
{
continue;
}

return true;
}

INamedTypeSymbol? baseType = typeSymbol.BaseType;

if (baseType is null)
{
return false;
}

if (baseType.SpecialType == SpecialType.System_Object)
{
return false;
}

typeSymbol = baseType;
}
}

private static bool IsUnderlyingAValueType(VogenConfiguration config)
{
bool isValueType = true;
Expand Down
2 changes: 1 addition & 1 deletion src/Vogen/Generators/ClassGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ private void EnsureInitialized()
{Util.GenerateAnyInstances(tds, item)}
public override global::System.String ToString() => Value.ToString();
{Util.GenerateToString(item)}
{Util.GenerateAnyConversionBodies(tds, item)}
Expand Down
3 changes: 3 additions & 0 deletions src/Vogen/Util.cs
Original file line number Diff line number Diff line change
Expand Up @@ -233,4 +233,7 @@ public static string GenerateDebuggerProxyForClasses(TypeDeclarationSyntax tds,

public static string GenerateYourAssemblyName() => typeof(Util).Assembly.GetName().Name;
public static string GenerateYourAssemblyVersion() => typeof(Util).Assembly.GetName().Version.ToString();

public static string GenerateToString(VoWorkItem item) =>
item.HasToString ? string.Empty : "public override global::System.String ToString() => Value.ToString();";
}
2 changes: 2 additions & 0 deletions src/Vogen/VoWorkItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,6 @@ public class VoWorkItem
public string VoTypeName => TypeToAugment.Identifier.ToString();

public string UnderlyingTypeFullName => UnderlyingType.FullName() ?? UnderlyingType?.Name ?? "global::System.Int32";

public bool HasToString { get; set; } = false;
}
48 changes: 45 additions & 3 deletions tests/SmallTests/ToStringTests.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,44 @@
using FluentAssertions;
using Vogen;
using Vogen.Tests.Types;
using Xunit;

namespace Vogen.Tests;
namespace SmallTests;

public class ToString_Derived1
{
public override string ToString() => "derived1!";
}

public class ToString_Derived2 : ToString_Derived1
{
public new virtual string ToString() => "derived2!";
}

public class ToString_Derived3 : ToString_Derived2
{
}

[ValueObject]
public partial class ToString_Vo
{
public override string ToString() => "ToString_Vo!";
}

[ValueObject]
public partial class ToString_Vo_Derived1 : ToString_Derived1
{
}

[ValueObject]
public partial class ToString_Vo_Derived2 : ToString_Derived3
{
}

public class ToStringTests
{

[Fact]
public void To_string()
public void ToString_uses_generated_method()
{
Age.From(18).ToString().Should().Be("18");
Age.From(100).ToString().Should().Be("100");
Expand All @@ -18,4 +48,16 @@ public void To_string()
Name.From("barney").ToString().Should().Be("barney");
Name.From("wilma").ToString().Should().Be("wilma");
}

[Fact]
public void ToString_uses_users_method() =>
ToString_Vo.From(18).ToString().Should().Be("ToString_Vo!");

[Fact]
public void ToString_uses_derived_method() =>
ToString_Vo_Derived1.From(18).ToString().Should().Be("derived1!");

[Fact]
public void ToString_uses_least_derived_method() =>
ToString_Vo_Derived2.From(18).ToString().Should().Be("derived2!");
}
18 changes: 4 additions & 14 deletions tests/Testbench/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,12 @@ public class Program
{
public static void Main()
{
GeneratedValueObject c = Create(new Object[]
{
new()
});
}

static GeneratedValueObject Create(Object[] normalObject)
{
return null!;//GeneratedValueObject.From(10);
// To debug the source generator or analyzer, set the active project to Vogen,
// and then select Roslyn as the debug target. This requires the Roslyn SDK
// to be installed in the list of Visual Studio components.
}
}

[ValueObject]
partial class GeneratedValueObject { }

public abstract partial record class x(string X){}
}



0 comments on commit 0dfa066

Please sign in to comment.