Skip to content

Commit

Permalink
Add support for static fields (#46)
Browse files Browse the repository at this point in the history
  • Loading branch information
viceroypenguin authored May 30, 2024
1 parent 999bec9 commit 615c52a
Show file tree
Hide file tree
Showing 6 changed files with 280 additions and 23 deletions.
51 changes: 40 additions & 11 deletions src/Immediate.Validations.Analyzers/ValidateClassAnalyzer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ private static void AnalyzeSymbol(SyntaxNodeAnalysisContext context)
var members = symbol
.GetAllMembers()
.Where(m =>
m is IPropertySymbol
m is IPropertySymbol or IFieldSymbol
or IMethodSymbol
{
Parameters: [],
Expand Down Expand Up @@ -410,13 +410,6 @@ List<ISymbol> members
return;

var validateParameter = validateParameterSymbols.First(p => p.Name == parameter.Name);
var targetType = validateParameter.Type;

if (validateParameter.IsParams && targetType is IArrayTypeSymbol { ElementType: { } et })
targetType = et;

if (targetType is ITypeParameterSymbol)
targetType = typeArgumentType;

if (syntax.Expression.IsNameOfExpression(out var propertyName))
{
Expand All @@ -439,12 +432,13 @@ List<ISymbol> members

var memberType = member switch
{
IPropertySymbol { Type: { } t } => t,
IMethodSymbol { ReturnType: { } t } => t,
IPropertySymbol { Type: { } t } => t,
IFieldSymbol { Type: { } t } => t,
_ => throw new InvalidOperationException(),
};

if (!context.Compilation.ClassifyConversion(memberType, targetType).IsValidConversion())
if (!ValidateArgumentType(context.Compilation, validateParameter, memberType, typeArgumentType, out var targetType))
{
context.ReportDiagnostic(
Diagnostic.Create(
Expand All @@ -462,7 +456,7 @@ List<ISymbol> members
if (context.SemanticModel.GetOperation(syntax.Expression)?.Type is not ITypeSymbol argumentType)
return;

if (!context.Compilation.ClassifyConversion(argumentType, targetType).IsValidConversion())
if (!ValidateArgumentType(context.Compilation, validateParameter, argumentType, typeArgumentType, out var targetType))
{
context.ReportDiagnostic(
Diagnostic.Create(
Expand All @@ -475,6 +469,41 @@ List<ISymbol> members
}
}
}

private static bool ValidateArgumentType(
Compilation compilation,
IParameterSymbol parameter,
ITypeSymbol argumentType,
ITypeSymbol typeArgumentType,
out ITypeSymbol targetType
)
{
targetType = parameter.Type;
var buildArrayTypeSymbol = false;

if (parameter.IsParams
&& targetType is IArrayTypeSymbol { ElementType: { } paramElementType })
{
targetType = paramElementType;

if (argumentType is IArrayTypeSymbol { ElementType: { } argumentElementType })
{
argumentType = argumentElementType;
buildArrayTypeSymbol = true;
}
}

if (targetType is ITypeParameterSymbol)
targetType = typeArgumentType;

if (compilation.ClassifyConversion(argumentType, targetType).IsValidConversion())
return true;

if (buildArrayTypeSymbol)
targetType = compilation.CreateArrayTypeSymbol(targetType);

return false;
}
}

file static class Extensions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ CancellationToken token
var members = symbol
.GetAllMembers()
.Where(m =>
m is IPropertySymbol
m is IPropertySymbol or IFieldSymbol
or IMethodSymbol
{
Parameters: [],
Expand Down Expand Up @@ -537,9 +537,8 @@ AttributeArgumentSyntax attributeArgumentSyntax
{
IMethodSymbol { IsStatic: true } => $"{name}()",
IMethodSymbol => $"instance.{name}()",
IPropertySymbol { IsStatic: true } => $"{name}",
IPropertySymbol => $"instance.{name}",
_ => "",
{ IsStatic: true } => $"{name}",
_ => $"instance.{name}",
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,7 @@ params string[] first
? default
: (true, $"Value '{target}' is not equal to '{first[0]}'");
}
[Validate]
public sealed partial record Target : IValidationTarget<Target>
{
Expand Down Expand Up @@ -567,7 +567,7 @@ params string[] first
? default
: (true, $"Value '{target}' is not equal to '{first[0]}'");
}
[Validate]
public sealed partial record Target : IValidationTarget<Target>
{
Expand Down Expand Up @@ -599,7 +599,7 @@ params T[] first
? default
: (true, $"Value '{target}' is not equal to '{first[0]}'");
}
[Validate]
public sealed partial record Target : IValidationTarget<Target>
{
Expand Down Expand Up @@ -631,7 +631,7 @@ params T[] first
? default
: (true, $"Value '{target}' is not equal to '{first[0]}'");
}
[Validate]
public sealed partial record Target : IValidationTarget<Target>
{
Expand Down Expand Up @@ -663,7 +663,7 @@ params string[] first
? default
: (true, $"Value '{target}' is not equal to '{first[0]}'");
}
[Validate]
public sealed partial record Target : IValidationTarget<Target>
{
Expand Down Expand Up @@ -695,7 +695,7 @@ params string[] first
? default
: (true, $"Value '{target}' is not equal to '{first[0]}'");
}
[Validate]
public sealed partial record Target : IValidationTarget<Target>
{
Expand Down Expand Up @@ -727,7 +727,7 @@ params T[] first
? default
: (true, $"Value '{target}' is not equal to '{first[0]}'");
}
[Validate]
public sealed partial record Target : IValidationTarget<Target>
{
Expand Down Expand Up @@ -759,7 +759,7 @@ params T[] first
? default
: (true, $"Value '{target}' is not equal to '{first[0]}'");
}
[Validate]
public sealed partial record Target : IValidationTarget<Target>
{
Expand All @@ -772,6 +772,46 @@ public sealed partial record Target : IValidationTarget<Target>
"""
).RunAsync();

[Fact]
public async Task ValidValidatorTypeShouldNotWarn16() =>
await AnalyzerTestHelpers.CreateAnalyzerTest<ValidateClassAnalyzer>(
"""
using System.Collections.Generic;
using Immediate.Validations.Shared;
[Validate]
public sealed partial record Target : IValidationTarget<Target>
{
[OneOf(nameof(Values))]
public required string Id { get; init; }
private static readonly string[] Values = ["123", "456", "789"];
public static List<ValidationError> Validate(Target target) => [];
}
"""
).RunAsync();

[Fact]
public async Task InvalidValidatorTypeShouldWarn16() =>
await AnalyzerTestHelpers.CreateAnalyzerTest<ValidateClassAnalyzer>(
"""
using System.Collections.Generic;
using Immediate.Validations.Shared;
[Validate]
public sealed partial record Target : IValidationTarget<Target>
{
[OneOf({|IV0016:nameof(Values)|})]
public required int Id { get; init; }
private static readonly string[] Values = ["123", "456", "789"];
public static List<ValidationError> Validate(Target target) => [];
}
"""
).RunAsync();

[Fact]
public async Task InvalidNameofShouldWarn() =>
await AnalyzerTestHelpers.CreateAnalyzerTest<ValidateClassAnalyzer>(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//HintName: IV...ValidateClass.g.cs
using System.Collections.Generic;
using Immediate.Validations.Shared;

#nullable enable
#pragma warning disable CS1591


partial class ValidateClass
{
static List<ValidationError> IValidationTarget<ValidateClass>.Validate(ValidateClass? target) =>
Validate(target);

public static List<ValidationError> Validate(ValidateClass? target)
{
if (target is not { } t)
{
return
[
new()
{
PropertyName = ".self",
ErrorMessage = "`target` must not be `null`.",
},
];
}

var errors = new List<ValidationError>();


__ValidateStringProperty(errors, t, t.StringProperty);


return errors;
}



private static void __ValidateStringProperty(
List<ValidationError> errors, ValidateClass instance, string target
)
{

if (target is not { } t)
{
errors.Add(new()
{
PropertyName = $"StringProperty",
ErrorMessage = "Property must not be `null`.",
});

return;
}



errors.Add(
global::Immediate.Validations.Shared.NotEqualAttribute.ValidateProperty(
t
, operand: instance._argumentValue
),
$"StringProperty",
null
);
}

}

Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//HintName: IV...ValidateClass.g.cs
using System.Collections.Generic;
using Immediate.Validations.Shared;

#nullable enable
#pragma warning disable CS1591


partial class ValidateClass
{
static List<ValidationError> IValidationTarget<ValidateClass>.Validate(ValidateClass? target) =>
Validate(target);

public static List<ValidationError> Validate(ValidateClass? target)
{
if (target is not { } t)
{
return
[
new()
{
PropertyName = ".self",
ErrorMessage = "`target` must not be `null`.",
},
];
}

var errors = new List<ValidationError>();


__ValidateStringProperty(errors, t, t.StringProperty);


return errors;
}



private static void __ValidateStringProperty(
List<ValidationError> errors, ValidateClass instance, string target
)
{

if (target is not { } t)
{
errors.Add(new()
{
PropertyName = $"StringProperty",
ErrorMessage = "Property must not be `null`.",
});

return;
}



errors.Add(
global::Immediate.Validations.Shared.NotEqualAttribute.ValidateProperty(
t
, operand: _argumentValue
),
$"StringProperty",
null
);
}

}

Loading

0 comments on commit 615c52a

Please sign in to comment.