Skip to content

Commit

Permalink
Refactor
Browse files Browse the repository at this point in the history
  • Loading branch information
SteveDunn committed Oct 10, 2023
1 parent be2a7a1 commit eb2fe1d
Show file tree
Hide file tree
Showing 43,395 changed files with 656,460 additions and 488,237 deletions.
The diff you're trying to view is too large. We only load the first 3000 changed files.
Binary file modified .editorconfig
Binary file not shown.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -750,7 +750,7 @@ For other types, a generic type conversion and serializer is applied. If you are
public partial struct SpecialMeasurement { }
```

### I've done a change that means the 'Snapshot' tests are expectedly failing in the build - what do I do?
### I've made a change that means the 'Snapshot' tests are expectedly failing in the build - what do I do?

Vogen uses a combination of unit tests, in-memory compilation tests, and snapshot tests. The snapshot tests are used
to compare the output of the source generators to the expected output stored on disk.
Expand Down
12 changes: 12 additions & 0 deletions src/Vogen.SharedTypes/StringComparersGeneration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Diagnostics;
// ReSharper disable UnusedMember.Global

namespace Vogen;

public enum StringComparersGeneration
{
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
Unspecified = -1,
Omit = 0,
Generate = 1,
}
16 changes: 0 additions & 16 deletions src/Vogen.SharedTypes/StringComparisonGeneration.cs

This file was deleted.

6 changes: 3 additions & 3 deletions src/Vogen.SharedTypes/ValueObjectAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ public ValueObjectAttribute(
DeserializationStrictness deserializationStrictness = DeserializationStrictness.AllowValidAndKnownInstances,
DebuggerAttributeGeneration debuggerAttributes = DebuggerAttributeGeneration.Default,
ComparisonGeneration comparison = ComparisonGeneration.Default,
StringComparisonGeneration stringComparison = StringComparisonGeneration.Unspecified)
: base(typeof(T), conversions, throws, customizations, deserializationStrictness, debuggerAttributes, comparison, stringComparison)
StringComparersGeneration stringComparers = StringComparersGeneration.Unspecified)
: base(typeof(T), conversions, throws, customizations, deserializationStrictness, debuggerAttributes, comparison, stringComparers)
{
}
}
Expand All @@ -49,7 +49,7 @@ public ValueObjectAttribute(
DeserializationStrictness deserializationStrictness = DeserializationStrictness.AllowValidAndKnownInstances,
DebuggerAttributeGeneration debuggerAttributes = DebuggerAttributeGeneration.Default,
ComparisonGeneration comparison = ComparisonGeneration.Default,
StringComparisonGeneration stringComparison = StringComparisonGeneration.Unspecified)
StringComparersGeneration stringComparers = StringComparersGeneration.Unspecified)
{
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/Vogen/BuildConfigurationFromAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ internal class BuildConfigurationFromAttributes
private DeserializationStrictness _deserializationStrictness;
private DebuggerAttributeGeneration _debuggerAttributes;
private ComparisonGeneration _comparisonGeneration;
private StringComparisonGeneration _stringComparison;
private StringComparersGeneration _stringComparers;

private BuildConfigurationFromAttributes(AttributeData att)
{
Expand All @@ -36,7 +36,7 @@ private BuildConfigurationFromAttributes(AttributeData att)
_deserializationStrictness = DeserializationStrictness.Default;
_debuggerAttributes = DebuggerAttributeGeneration.Default;
_comparisonGeneration = ComparisonGeneration.Default;
_stringComparison = StringComparisonGeneration.Unspecified;
_stringComparers = StringComparersGeneration.Unspecified;
_hasErroredAttributes = false;

_diagnostics = new List<Diagnostic>();
Expand Down Expand Up @@ -88,7 +88,7 @@ private VogenConfigurationBuildResult Build(bool argsAreFromVogenDefaultAttribut
_deserializationStrictness,
_debuggerAttributes,
_comparisonGeneration,
_stringComparison),
_stringComparers),
diagnostics: _diagnostics);
}

Expand Down Expand Up @@ -222,7 +222,7 @@ private void PopulateFromValueObjectAttributeArgs(ImmutableArray<TypedConstant>

if (i == 6)
{
_stringComparison = (StringComparisonGeneration) v;
_stringComparers = (StringComparersGeneration) v;
}

if (i == 5)
Expand Down
2 changes: 1 addition & 1 deletion src/Vogen/BuildWorkItems.cs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ internal static class BuildWorkItems
Customizations = config.Customizations,
TypeForValidationExceptions = config.ValidationExceptionType,
ComparisonGeneration = config.Comparison,
StringComparisonGeneration = config.StringComparison,
StringComparersGeneration = config.StringComparers,
ValidateMethod = validateMethod,
NormalizeInputMethod = normalizeInputMethod,
FullNamespace = voSymbolInformation.FullNamespace()
Expand Down
147 changes: 96 additions & 51 deletions src/Vogen/GenerateEqualsAndHashCodes.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using System;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Vogen;
Expand Down Expand Up @@ -27,10 +26,15 @@ public static string GenerateEqualsForAClass(VoWorkItem item, TypeDeclarationSyn
return true;
}
return GetType() == other.GetType() && {{GenerateCallToDefaultComparerOrCustomStringComparer(item)}};
return GetType() == other.GetType() && {{$"global::System.Collections.Generic.EqualityComparer<{item.UnderlyingTypeFullName}>.Default.Equals(Value, other.Value)"}};
}
public global::System.Boolean Equals({{className}} other, global::System.Collections.Generic.IEqualityComparer<{{className}}> comparer)
{
return comparer.Equals(this, other);
}
{{GenerateEqualsForUnderlyingMethod(item)}}
{{GenerateEqualsForUnderlyingMethod(item, isReadOnly: false)}}
public override global::System.Boolean Equals(global::System.Object obj)
{
Expand Down Expand Up @@ -66,82 +70,123 @@ public static string GenerateEqualsForAStruct(VoWorkItem item, TypeDeclarationSy
// We treat anything uninitialized as not equal to anything, even other uninitialized instances of this type.
if(!_isInitialized || !other._isInitialized) return false;
return {{GenerateCallToDefaultComparerOrCustomStringComparer(item)}};
return {{$"global::System.Collections.Generic.EqualityComparer<{item.UnderlyingTypeFullName}>.Default.Equals(Value, other.Value)"}};
}
{{GenerateEqualsForUnderlyingMethod(item)}}
public global::System.Boolean Equals({{structName}} other, global::System.Collections.Generic.IEqualityComparer<{{structName}}> comparer)
{
return comparer.Equals(this, other);
}
{{GenerateEqualsForUnderlyingMethod(item, isReadOnly: true)}}
public readonly override global::System.Boolean Equals(global::System.Object obj)
{
return obj is {{structName}} && Equals(({{structName}}) obj);
}
""";
}
public static string GenerateGetHashCode(VoWorkItem item)

public static string GenerateGetHashCodeForAClass(VoWorkItem item)
{
string itemUnderlyingType = item.UnderlyingTypeFullName;

bool isString = typeof(string).IsAssignableFrom(itemUnderlyingType.GetType());

var stringHashCode = isString && item.StringComparisonGeneration is not StringComparisonGeneration.Unspecified
? $"global::System.StringComparer.{GetComparerLiteral(item.StringComparisonGeneration)}.GetHashCode(Value)"
: "Value.GetHashCode()";
return $$"""
public override global::System.Int32 GetHashCode()
public override global::System.Int32 GetHashCode()
{
unchecked // Overflow is fine, just wrap
{
unchecked // Overflow is fine, just wrap
{
global::System.Int32 hash = (global::System.Int32) 2166136261;
hash = (hash * 16777619) ^ {{stringHashCode}};
hash = (hash * 16777619) ^ GetType().GetHashCode();
hash = (hash * 16777619) ^ global::System.Collections.Generic.EqualityComparer<{{itemUnderlyingType}}>.Default.GetHashCode();
return hash;
}
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);
return hash;
}
}
""";
}

private static string GenerateEqualsForUnderlyingMethod(VoWorkItem item)
public static string GenerateGetHashCodeForAStruct(VoWorkItem item)
{
string itemUnderlyingType = item.UnderlyingTypeFullName;

bool isString = typeof(string).IsAssignableFrom(itemUnderlyingType.GetType());

return isString && item.StringComparisonGeneration is not StringComparisonGeneration.Unspecified
? GenerateEqualsForString(item, itemUnderlyingType)
: $"public global::System.Boolean Equals({itemUnderlyingType} primitive) {{ return Value.Equals(primitive); }}";
return $$"""
public readonly override global::System.Int32 GetHashCode()
{
return global::System.Collections.Generic.EqualityComparer<{{itemUnderlyingType}}>.Default.GetHashCode(Value);
}
""";
}

private static string GenerateEqualsForString(VoWorkItem item, string itemUnderlyingType)
private static string GenerateEqualsForUnderlyingMethod(VoWorkItem item, bool isReadOnly)
{
string comparerLiteral = GetComparerLiteral(item.StringComparisonGeneration);
string itemUnderlyingType = item.UnderlyingTypeFullName;

return
$"public global::System.Boolean Equals({itemUnderlyingType} primitive) {{ return global::System.StringComparer.{comparerLiteral}.Equals(Value, primitive); }}";
}
bool isString = item.IsUnderlyingAString;

string readonlyOrEmpty = isReadOnly ? " readonly" : string.Empty;

private static string GetComparerLiteral(StringComparisonGeneration e) =>
e switch
string output = $$"""
public{{readonlyOrEmpty}} global::System.Boolean Equals({{itemUnderlyingType}} primitive) {
return Value.Equals(primitive);
}
""";

if(isString)
{
StringComparisonGeneration.CurrentCulture => "CurrentCulture",
StringComparisonGeneration.CurrentCultureIgnoreCase => "CurrentCultureIgnoreCase",
StringComparisonGeneration.InvariantCulture => "InvariantCulture",
StringComparisonGeneration.InvariantCultureIgnoreCase => "InvariantCultureIgnoreCase",
StringComparisonGeneration.Ordinal => "Ordinal",
StringComparisonGeneration.OrdinalIgnoreCase => "OrdinalIgnoreCase",
StringComparisonGeneration.Unspecified => throw new InvalidOperationException("Don't know how to get the StringComparer as it was unspecified"),
_ => throw new InvalidOperationException("Don't know how to get the StringComparer for " + nameof(e))
};

private static string GenerateCallToDefaultComparerOrCustomStringComparer(VoWorkItem item)
output += $$"""
public{{readonlyOrEmpty}} global::System.Boolean Equals({{itemUnderlyingType}} primitive, global::System.StringComparer comparer) {
return comparer.Equals(Value, primitive);
}
""";
}

return output;
}

public static string GenerateStringComparersIfNeeded(VoWorkItem item, TypeDeclarationSyntax tds)
{
string itemUnderlyingType = item.UnderlyingTypeFullName;
if (!item.IsUnderlyingAString) return string.Empty;

if(item.StringComparersGeneration != StringComparersGeneration.Generate) return string.Empty;

bool isString = typeof(string).IsAssignableFrom(itemUnderlyingType.GetType());
return $$"""
public static class Comparers
{
private class __StringEqualityComparer : global::System.Collections.Generic.IEqualityComparer<{{tds.Identifier}}>
{
readonly global::System.StringComparer _comparer;
public __StringEqualityComparer(global::System.StringComparer comparer) {
_comparer = comparer;
}
public bool Equals({{tds.Identifier}} x, {{tds.Identifier}} y) {
return _comparer.Equals(x._value, y._value);
}
public int GetHashCode({{tds.Identifier}} obj) {
return _comparer.GetHashCode();
}
}
public static readonly global::System.Collections.Generic.IEqualityComparer<{{tds.Identifier}}> Ordinal =
new __StringEqualityComparer(global::System.StringComparer.Ordinal);
public static readonly global::System.Collections.Generic.IEqualityComparer<{{tds.Identifier}}> OrdinalIgnoreCase =
new __StringEqualityComparer(global::System.StringComparer.OrdinalIgnoreCase);
public static readonly global::System.Collections.Generic.IEqualityComparer<{{tds.Identifier}}> CurrentCulture =
new __StringEqualityComparer(global::System.StringComparer.CurrentCulture);
return isString && item.StringComparisonGeneration is not StringComparisonGeneration.Unspecified
? $"Equals(other.Value)"
: $"global::System.Collections.Generic.EqualityComparer<{itemUnderlyingType}>.Default.Equals(Value, other.Value)";
public static readonly global::System.Collections.Generic.IEqualityComparer<{{tds.Identifier}}> CurrentCultureIgnoreCase =
new __StringEqualityComparer(global::System.StringComparer.CurrentCultureIgnoreCase);
public static readonly global::System.Collections.Generic.IEqualityComparer<{{tds.Identifier}}> InvariantCulture =
new __StringEqualityComparer(global::System.StringComparer.InvariantCulture);
public static readonly global::System.Collections.Generic.IEqualityComparer<{{tds.Identifier}}> InvariantCultureIgnoreCase =
new __StringEqualityComparer(global::System.StringComparer.InvariantCultureIgnoreCase);
}
""";
}
}
3 changes: 2 additions & 1 deletion src/Vogen/Generators/ClassGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public string BuildClass(VoWorkItem item, TypeDeclarationSyntax tds)
return instance;
}}
{GenerateEqualsAndHashCodes.GenerateStringComparersIfNeeded(item, tds)}
// only called internally when something has been deserialized into
// its primitive type.
Expand Down Expand Up @@ -102,7 +103,7 @@ public string BuildClass(VoWorkItem item, TypeDeclarationSyntax tds)
public static explicit operator {itemUnderlyingType}({className} value) => value.Value;
{GenerateComparableCode.GenerateIComparableImplementationIfNeeded(item, tds)}
{GenerateEqualsAndHashCodes.GenerateGetHashCode(item)}
{GenerateEqualsAndHashCodes.GenerateGetHashCodeForAClass(item)}
private void EnsureInitialized()
{{
Expand Down
3 changes: 2 additions & 1 deletion src/Vogen/Generators/StructGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public readonly {itemUnderlyingType} Value
return instance;
}}
{GenerateEqualsAndHashCodes.GenerateStringComparersIfNeeded(item, tds)}
public static explicit operator {structName}({itemUnderlyingType} value) => From(value);
public static explicit operator {itemUnderlyingType}({structName} value) => value.Value;
Expand Down Expand Up @@ -103,7 +104,7 @@ public readonly {itemUnderlyingType} Value
{GenerateComparableCode.GenerateIComparableImplementationIfNeeded(item, tds)}
{TryParseGeneration.GenerateTryParseIfNeeded(item)}
{GenerateEqualsAndHashCodes.GenerateGetHashCode(item)}
{GenerateEqualsAndHashCodes.GenerateGetHashCodeForAStruct(item)}
{Util.GenerateToStringReadOnly(item)}
Expand Down
5 changes: 4 additions & 1 deletion src/Vogen/VoWorkItem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,12 @@ public INamedTypeSymbol UnderlyingType
_underlyingType = value;
_underlyingTypeFullName = value.FullName() ?? value?.Name ?? throw new InvalidOperationException(
"No underlying type specified - please file a bug at https://github.com/SteveDunn/Vogen/issues/new?assignees=&labels=bug&template=BUG_REPORT.yml");
IsUnderlyingAString = typeof(string).IsAssignableFrom(Type.GetType(_underlyingTypeFullName));
}
}

public bool IsUnderlyingAString { get; private set; }

/// <summary>
/// The syntax information for the type to augment.
/// </summary>
Expand Down Expand Up @@ -55,5 +58,5 @@ public INamedTypeSymbol UnderlyingType

public ComparisonGeneration ComparisonGeneration { get; set; }

public StringComparisonGeneration StringComparisonGeneration { get; set; }
public StringComparersGeneration StringComparersGeneration { get; set; }
}
Loading

0 comments on commit eb2fe1d

Please sign in to comment.