Skip to content

Commit 6d2916a

Browse files
Restore support for validation of record parameters
1 parent 7142ac8 commit 6d2916a

File tree

3 files changed

+156
-10
lines changed

3 files changed

+156
-10
lines changed

src/Common/ITypeSymbolExtensions.cs

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -303,22 +303,50 @@ typeSymbol is INamedTypeSymbol
303303
SemanticModel semanticModel
304304
)
305305
{
306-
var propertySyntax = (PropertyDeclarationSyntax)propertySymbol.DeclaringSyntaxReferences.First().GetSyntax();
306+
switch (propertySymbol.DeclaringSyntaxReferences.First().GetSyntax())
307+
{
308+
case PropertyDeclarationSyntax propertySyntax:
309+
{
310+
var list = new List<(string? Target, IObjectCreationOperation AttributeOperation)>();
307311

308-
var list = new List<(string? Target, IObjectCreationOperation AttributeOperation)>();
312+
foreach (var attributeList in propertySyntax.AttributeLists)
313+
{
314+
var target = attributeList.Target?.Identifier.ValueText;
309315

310-
foreach (var attributeList in propertySyntax.AttributeLists)
311-
{
312-
var target = attributeList.Target?.Identifier.ValueText;
316+
foreach (var attribute in attributeList.Attributes)
317+
{
318+
if (semanticModel.GetOperation(attribute) is IAttributeOperation { Operation: IObjectCreationOperation operation })
319+
list.Add((target, operation));
320+
}
321+
}
313322

314-
foreach (var attribute in attributeList.Attributes)
323+
return list;
324+
}
325+
326+
case ParameterSyntax parameterSyntax:
315327
{
316-
if (semanticModel.GetOperation(attribute) is IAttributeOperation { Operation: IObjectCreationOperation operation })
317-
list.Add((target, operation));
328+
var list = new List<(string? Target, IObjectCreationOperation AttributeOperation)>();
329+
330+
foreach (var attributeList in parameterSyntax.AttributeLists)
331+
{
332+
var target = attributeList.Target?.Identifier.ValueText;
333+
334+
if (target is not "property")
335+
continue;
336+
337+
foreach (var attribute in attributeList.Attributes)
338+
{
339+
if (semanticModel.GetOperation(attribute) is IAttributeOperation { Operation: IObjectCreationOperation operation })
340+
list.Add((null, operation));
341+
}
342+
}
343+
344+
return list;
318345
}
319-
}
320346

321-
return list;
347+
case var syntax:
348+
throw new InvalidOperationException($"Property declared using a `{syntax.GetType().FullName}`.");
349+
}
322350
}
323351

324352
public static bool IsTargetTypeSymbol(this ISymbol symbol) =>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
//HintName: IV...ValidateClass.g.cs
2+
using System.Collections.Generic;
3+
using Immediate.Validations.Shared;
4+
5+
#nullable enable
6+
#pragma warning disable CS1591
7+
8+
9+
partial record ValidateClass : IValidationTarget
10+
{
11+
ValidationResult IValidationTarget.Validate() =>
12+
Validate(this, []);
13+
14+
ValidationResult IValidationTarget.Validate(ValidationResult errors) =>
15+
Validate(this, errors);
16+
17+
static ValidationResult IValidationTarget<ValidateClass>.Validate(ValidateClass? target) =>
18+
Validate(target, []);
19+
20+
static ValidationResult IValidationTarget<ValidateClass>.Validate(ValidateClass? target, ValidationResult errors) =>
21+
Validate(target, errors);
22+
23+
public static ValidationResult Validate(ValidateClass? target) =>
24+
Validate(target, []);
25+
26+
public static ValidationResult Validate(ValidateClass? target, ValidationResult errors)
27+
{
28+
if (target is not { } t)
29+
{
30+
return new()
31+
{
32+
{ ".self", "`target` must not be `null`." },
33+
};
34+
}
35+
36+
if (!errors.VisitType(typeof(ValidateClass)))
37+
return errors;
38+
39+
40+
__ValidateTesting(errors, t, t.Testing);
41+
42+
43+
return errors;
44+
}
45+
46+
47+
48+
private static void __ValidateTesting(
49+
ValidationResult errors, ValidateClass instance, string target
50+
)
51+
{
52+
53+
if (target is not { } t)
54+
{
55+
errors.Add(
56+
$"Testing",
57+
global::Immediate.Validations.Shared.NotNullAttribute.DefaultMessage,
58+
new()
59+
{
60+
["PropertyName"] = $"Testing",
61+
["PropertyValue"] = null,
62+
}
63+
);
64+
65+
return;
66+
}
67+
68+
69+
70+
{
71+
if (!global::Immediate.Validations.Shared.MaxLengthAttribute.ValidateProperty(
72+
t
73+
, maxLength: 3
74+
)
75+
)
76+
{
77+
errors.Add(
78+
$"Testing",
79+
global::Immediate.Validations.Shared.MaxLengthAttribute.DefaultMessage,
80+
new()
81+
{
82+
["PropertyName"] = $"Testing",
83+
["PropertyValue"] = t,
84+
["MaxLengthName"] = "",
85+
["MaxLengthValue"] = 3,
86+
}
87+
);
88+
}
89+
}
90+
}
91+
92+
}
93+

tests/Immediate.Validations.Tests/GeneratorTests/MiscellaneousTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,31 @@ namespace Immediate.Validations.Tests.GeneratorTests;
22

33
public sealed class MiscellaneousTests
44
{
5+
[Fact]
6+
public async Task PropertyValidationOnRecord()
7+
{
8+
var result = GeneratorTestHelper.RunGenerator(
9+
"""
10+
using System.ComponentModel;
11+
using Immediate.Validations.Shared;
12+
13+
[Validate]
14+
public sealed partial record ValidateClass(
15+
[property: MaxLength(3)] string Testing
16+
): IValidationTarget<ValidateClass>;
17+
"""
18+
);
19+
20+
Assert.Equal(
21+
[
22+
@"Immediate.Validations.Generators/Immediate.Validations.Generators.ImmediateValidationsGenerator/IV...ValidateClass.g.cs",
23+
],
24+
result.GeneratedTrees.Select(t => t.FilePath.Replace('\\', '/'))
25+
);
26+
27+
_ = await Verify(result);
28+
}
29+
530
[Fact]
631
public async Task FilledDescriptionChangesName()
732
{

0 commit comments

Comments
 (0)