Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/Vogen.SharedTypes/Conversions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Diagnostics;

namespace Vogen;

Expand All @@ -8,6 +9,11 @@ namespace Vogen;
[Flags]
public enum Conversions
{

[DebuggerBrowsable(DebuggerBrowsableState.Never)]
Unspecified = -1,


// Used with HasFlag, so needs to be 1, 2, 4 etc

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions src/Vogen.SharedTypes/ValueObjectAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public class ValueObjectAttribute<T> : ValueObjectAttribute
/// </example>
/// </param>
public ValueObjectAttribute(
Conversions conversions = Conversions.Default,
Conversions conversions = Conversions.Unspecified,
Type? throws = null!,
Customizations customizations = Customizations.None,
DeserializationStrictness deserializationStrictness = DeserializationStrictness.AllowValidAndKnownInstances,
Expand Down Expand Up @@ -131,7 +131,7 @@ public class ValueObjectAttribute : Attribute
/// </param>
public ValueObjectAttribute(
Type? underlyingType = null!,
Conversions conversions = Conversions.Default,
Conversions conversions = Conversions.Unspecified,
Type? throws = null!,
Customizations customizations = Customizations.None,
DeserializationStrictness deserializationStrictness = DeserializationStrictness.AllowValidAndKnownInstances,
Expand Down
2 changes: 1 addition & 1 deletion src/Vogen.SharedTypes/VogenDefaultsAttribute.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public class VogenDefaultsAttribute : Attribute
/// <param name="explicitlySpecifyTypeInValueObject">Every ValueObject attribute must explicitly specify the type of the primitive.</param>
public VogenDefaultsAttribute(
Type? underlyingType = null,
Conversions conversions = Conversions.Default,
Conversions conversions = Conversions.Unspecified,
Type? throws = null,
Customizations customizations = Customizations.None,
DeserializationStrictness deserializationStrictness = DeserializationStrictness.AllowValidAndKnownInstances,
Expand Down
26 changes: 15 additions & 11 deletions src/Vogen/BuildConfigurationFromAttributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Linq;
using Microsoft.CodeAnalysis;
using Vogen.Diagnostics;
using Vogen.Extensions;

// ReSharper disable NullableWarningSuppressionIsUsed

Expand All @@ -27,9 +28,9 @@ internal class BuildConfigurationFromAttributes
private CastOperator _fromPrimitiveCasting;
private CastOperator _toPrimitiveCasting;
private bool _disableStackTraceGenerationInDebug;
private ParsableForStrings _parsableForStrings;
private ParsableForPrimitives _parsableForPrimitives;
private TryFromGeneration _tryFromGeneration;
private ParsableForStrings _parsableForStrings;
private ParsableForPrimitives _parsableForPrimitives;
private TryFromGeneration _tryFromGeneration;
private IsInitializedMethodGeneration _isInitializedMethodGeneration;
private SystemTextJsonConverterFactoryGeneration _systemTextJsonConverterFactoryGeneration;
private StaticAbstractsGeneration _staticAbstractsGeneration;
Expand All @@ -42,7 +43,7 @@ private BuildConfigurationFromAttributes(AttributeData att)
_matchingAttribute = att;
_invalidExceptionType = null;
_underlyingType = null;
_conversions = Conversions.Default;
_conversions = Conversions.Unspecified;
_customizations = Customizations.None;
_deserializationStrictness = DeserializationStrictness.Default;
_debuggerAttributes = DebuggerAttributeGeneration.Default;
Expand All @@ -61,9 +62,9 @@ private BuildConfigurationFromAttributes(AttributeData att)
_openApiSchemaCustomizations = OpenApiSchemaCustomizations.Unspecified;
_primitiveTypeMustBeExplicit = false;
_primitiveEqualityGeneration = PrimitiveEqualityGeneration.Unspecified;

_diagnostics = new List<Diagnostic>();

ImmutableArray<TypedConstant> args = _matchingAttribute.ConstructorArguments;

_hasErroredAttributes = args.Any(a => a.Kind == TypedConstantKind.Error);
Expand All @@ -72,7 +73,7 @@ private BuildConfigurationFromAttributes(AttributeData att)
public static VogenConfigurationBuildResult TryBuildFromValueObjectAttribute(AttributeData matchingAttribute) =>
new BuildConfigurationFromAttributes(matchingAttribute).Build(argsAreFromVogenDefaultAttribute: false);

public static VogenConfigurationBuildResult TryBuildFromVogenDefaultsAttribute(AttributeData matchingAttribute) =>
public static VogenConfigurationBuildResult TryBuildFromVogenDefaultsAttribute(AttributeData matchingAttribute) =>
new BuildConfigurationFromAttributes(matchingAttribute).Build(argsAreFromVogenDefaultAttribute: true);

private VogenConfigurationBuildResult Build(bool argsAreFromVogenDefaultAttribute)
Expand Down Expand Up @@ -172,10 +173,13 @@ private void PopulateDiagnosticsWithAnyValidationIssues()
}

var syntaxLocation = syntax.GetLocation();

if (!_conversions.IsValidFlags())
{
_diagnostics.Add(DiagnosticsCatalogue.InvalidConversions(syntaxLocation));
if (_matchingAttribute.GetExplicitlySpecifiedAttributeConstructorParameters().Contains("conversions"))
{
_diagnostics.Add(DiagnosticsCatalogue.InvalidConversions(syntaxLocation));
}
}

if (!_customizations.IsValidFlags())
Expand Down Expand Up @@ -213,7 +217,7 @@ private void PopulateFromVogenDefaultsAttributeArgs(ImmutableArray<TypedConstant
{
_primitiveEqualityGeneration = (PrimitiveEqualityGeneration) v;
}

if (i == 15)
{
_primitiveTypeMustBeExplicit = (bool) v;
Expand Down Expand Up @@ -344,7 +348,7 @@ private void PopulateFromValueObjectAttributeArgs(ImmutableArray<TypedConstant>
{
_fromPrimitiveCasting = (CastOperator) v;
}

if (i == 7)
{
_toPrimitiveCasting = (CastOperator) v;
Expand Down
6 changes: 3 additions & 3 deletions src/Vogen/CombineConfigurations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,9 @@ public static VogenConfiguration CombineAndResolveAnythingUnspecified(
{
var conversions = (localValues.Conversions, globalValues?.Conversions) switch
{
(Conversions.Default, null) => VogenConfiguration.DefaultInstance.Conversions,
(Conversions.Default, Conversions.Default) => VogenConfiguration.DefaultInstance.Conversions,
(Conversions.Default, var globalDefault) => globalDefault.Value,
(Conversions.Unspecified, null) => VogenConfiguration.DefaultInstance.Conversions,
(Conversions.Unspecified, Conversions.Unspecified) => VogenConfiguration.DefaultInstance.Conversions,
(Conversions.Unspecified, var globalDefault) => globalDefault.Value,
(var specificValue, _) => specificValue
};

Expand Down
2 changes: 1 addition & 1 deletion src/Vogen/EnumExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ internal static class EnumExtensions
private static readonly int _maxCustomization = Enum.GetValues(typeof(Customizations)).Cast<int>().Max() * 2;
private static readonly int _maxDeserializationStrictness = Enum.GetValues(typeof(DeserializationStrictness)).Cast<int>().Max() * 2;

public static bool IsValidFlags(this Conversions value) => (int) value >= 0 && (int) value < _maxConversion;

public static bool IsValidFlags(this Conversions value) => (int) value >= 0 && (int) value < _maxConversion;
public static bool IsValidFlags(this Customizations value) => (int) value >= 0 && (int) value < _maxCustomization;

public static bool IsValidFlags(this DeserializationStrictness value) => (int) value >= 0 && (int) value < _maxDeserializationStrictness;
Expand Down
72 changes: 72 additions & 0 deletions src/Vogen/Extensions/AttributeDataExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace Vogen.Extensions;

public static class AttributeDataExtensions
{
public static IEnumerable<string> GetExplicitlySpecifiedAttributeConstructorParameters(this AttributeData attributeData)
{
var ctor = attributeData.AttributeConstructor;
if (ctor is null)
{
yield break;
}

var parameters = ctor.Parameters;

// Need the syntax of the specific attribute application.
var attrSyntax = attributeData.ApplicationSyntaxReference?.GetSyntax() as AttributeSyntax;
var argList = attrSyntax?.ArgumentList;

if (argList is null || argList.Arguments.Count == 0)
{
// No arguments written → any optional params are defaults (implicit)
yield break;
}

// First pass: positional constructor arguments (no NameEquals and no NameColon)
int index = 0; // walks constructor parameters in order
foreach (var arg in argList.Arguments)
{
if (arg.NameEquals is not null)
{
// Property/field initializer: `Name = value` → not a ctor parameter
continue;
}

IParameterSymbol parameterSymbol = parameters[index];

if (arg.NameColon is null)
{
// Positional ctor argument → binds to the next parameter position
if (index < parameters.Length)
{
yield return parameterSymbol.Name;
index++;
}

// If more positional args than parameters exist, the compiler will error;
// analyzer can ignore or record as needed.
continue;
}

// NameColon means a named constructor argument: `paramName: expr`
var name = arg.NameColon.Name.Identifier.ValueText;

if (Exists(parameters, name))
{
yield return name;
}
}

yield break;

static bool Exists(ImmutableArray<IParameterSymbol> ps, string name) =>
Enumerable.Any(ps, t => string.Equals(t.Name, name, StringComparison.Ordinal));
}
}
17 changes: 9 additions & 8 deletions src/Vogen/VoFilter.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
Expand All @@ -22,8 +23,8 @@ public static IEnumerable<AttributeData> TryGetValueObjectAttributes(INamedTypeS

return attrs.Where(
a => a.AttributeClass?.EscapedFullName() == "Vogen.ValueObjectAttribute"
|| a.AttributeClass?.BaseType?.EscapedFullName() == "Vogen.ValueObjectAttribute"
|| a.AttributeClass?.BaseType?.BaseType?.EscapedFullName() == "Vogen.ValueObjectAttribute");
|| a.AttributeClass?.BaseType?.EscapedFullName() == "Vogen.ValueObjectAttribute"
|| a.AttributeClass?.BaseType?.BaseType?.EscapedFullName() == "Vogen.ValueObjectAttribute");
}

/// <summary>
Expand All @@ -40,7 +41,7 @@ public static IEnumerable<AttributeData> TryGetValueObjectAttributes(INamedTypeS
var semanticModel = context.SemanticModel;

ISymbol declaredSymbol = semanticModel.GetDeclaredSymbol(context.Node)!;

var voSymbolInformation = (INamedTypeSymbol) declaredSymbol;

var attributeData = TryGetValueObjectAttributes(voSymbolInformation).ToImmutableArray();
Expand All @@ -60,9 +61,9 @@ public static IEnumerable<AttributeData> TryGetValueObjectAttributes(INamedTypeS
return null;
}

public static bool IsTarget(SyntaxNode syntaxNode) =>
public static bool IsTarget(SyntaxNode syntaxNode) =>
syntaxNode is TypeDeclarationSyntax { AttributeLists.Count: > 0 };

public static bool IsTarget(INamedTypeSymbol? voClass) =>
voClass is not null && TryGetValueObjectAttributes(voClass).Any();
}
public static bool IsTarget(INamedTypeSymbol? voClass) =>
voClass is not null && TryGetValueObjectAttributes(voClass).Any();
}
Loading
Loading