Skip to content

Commit 4442047

Browse files
Add support for [element: ] attributes on record parameters
1 parent 6d2916a commit 4442047

File tree

5 files changed

+146
-22
lines changed

5 files changed

+146
-22
lines changed

src/Common/ITypeSymbolExtensions.cs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -329,15 +329,12 @@ SemanticModel semanticModel
329329

330330
foreach (var attributeList in parameterSyntax.AttributeLists)
331331
{
332-
var target = attributeList.Target?.Identifier.ValueText;
333-
334-
if (target is not "property")
335-
continue;
332+
var target = attributeList.Target?.Identifier.ValueText.NullIf("property");
336333

337334
foreach (var attribute in attributeList.Attributes)
338335
{
339336
if (semanticModel.GetOperation(attribute) is IAttributeOperation { Operation: IObjectCreationOperation operation })
340-
list.Add((null, operation));
337+
list.Add((target, operation));
341338
}
342339
}
343340

src/Immediate.Validations.Analyzers/InvalidAttributeTargetSuppressor.cs

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,23 +29,31 @@ public override void ReportSuppressions(SuppressionAnalysisContext context)
2929

3030
if (syntaxTree
3131
?.GetRoot(token)
32-
.FindNode(diagnostic.Location.SourceSpan) is
33-
not AttributeTargetSpecifierSyntax
32+
.FindNode(diagnostic.Location.SourceSpan) is not AttributeTargetSpecifierSyntax
3433
{
3534
Identifier.ValueText: "element",
36-
Parent.Parent: PropertyDeclarationSyntax propertyDeclarationSyntax
35+
Parent.Parent: SyntaxNode declarationSyntax
3736
})
3837
{
3938
continue;
4039
}
4140

42-
if (context.GetSemanticModel(syntaxTree)
43-
.GetDeclaredSymbol(propertyDeclarationSyntax, token) is
44-
not IPropertySymbol
45-
{
46-
ContainingType: INamedTypeSymbol containerSymbol,
47-
Type: ITypeSymbol propertyTypeSymbol
48-
})
41+
if (context.GetSemanticModel(syntaxTree).GetDeclaredSymbol(declarationSyntax, token) switch
42+
{
43+
IPropertySymbol
44+
{
45+
ContainingType: INamedTypeSymbol ct1,
46+
Type: ITypeSymbol pt1
47+
} => (true, ct1, pt1),
48+
49+
IParameterSymbol
50+
{
51+
ContainingType: INamedTypeSymbol ct1,
52+
Type: ITypeSymbol pt1
53+
} => (true, ct1, pt1),
54+
55+
_ => (false, null, null),
56+
} is not (true, { } containerSymbol, { } propertyTypeSymbol))
4957
{
5058
continue;
5159
}
@@ -79,6 +87,8 @@ not IPropertySymbol
7987
diagnostic
8088
)
8189
);
90+
91+
break;
8292
}
8393
}
8494
}

tests/Immediate.Validations.Tests/AnalyzerTests/InvalidAttributeTargetSuppressorTests.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,36 @@ public sealed class InvalidAttributeTargetSuppressorTests
88
public static readonly DiagnosticResult CS0658 =
99
DiagnosticResult.CompilerWarning("CS0658");
1010

11+
[Fact]
12+
public async Task ElementAttributeInValidatorListForParameterIsSuppressed() =>
13+
await AnalyzerTestHelpers
14+
.CreateSuppressorTest<InvalidAttributeTargetSuppressor>(
15+
"""
16+
#nullable enable
17+
18+
using System.Collections.Generic;
19+
using Immediate.Validations.Shared;
20+
21+
[Validate]
22+
public record Target(
23+
[{|#0:element|}: MaxLength(3)]
24+
List<string> Strings
25+
): IValidationTarget<Target>
26+
{
27+
28+
public ValidationResult Validate() => [];
29+
public ValidationResult Validate(ValidationResult errors) => [];
30+
public static ValidationResult Validate(Target target) => [];
31+
public static ValidationResult Validate(Target target, ValidationResult errors) => [];
32+
}
33+
"""
34+
)
35+
.WithSpecificDiagnostics([CS0658])
36+
.WithExpectedDiagnosticsResults([
37+
CS0658.WithLocation(0).WithIsSuppressed(true),
38+
])
39+
.RunAsync(TestContext.Current.CancellationToken);
40+
1141
[Fact]
1242
public async Task ElementAttributeInValidatorListIsSuppressed() =>
1343
await AnalyzerTestHelpers

tests/Immediate.Validations.Tests/GeneratorTests/MiscellaneousTests.PropertyValidationOnRecord#IV...ValidateClass.g.verified.cs

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,27 +37,28 @@ public static ValidationResult Validate(ValidateClass? target, ValidationResult
3737
return errors;
3838

3939

40-
__ValidateTesting(errors, t, t.Testing);
40+
__ValidateTesting1(errors, t, t.Testing1);
41+
__Validatedata(errors, t, t.data);
4142

4243

4344
return errors;
4445
}
4546

4647

4748

48-
private static void __ValidateTesting(
49+
private static void __ValidateTesting1(
4950
ValidationResult errors, ValidateClass instance, string target
5051
)
5152
{
5253

5354
if (target is not { } t)
5455
{
5556
errors.Add(
56-
$"Testing",
57+
$"Testing1",
5758
global::Immediate.Validations.Shared.NotNullAttribute.DefaultMessage,
5859
new()
5960
{
60-
["PropertyName"] = $"Testing",
61+
["PropertyName"] = $"Testing1",
6162
["PropertyValue"] = null,
6263
}
6364
);
@@ -75,11 +76,11 @@ private static void __ValidateTesting(
7576
)
7677
{
7778
errors.Add(
78-
$"Testing",
79+
$"Testing1",
7980
global::Immediate.Validations.Shared.MaxLengthAttribute.DefaultMessage,
8081
new()
8182
{
82-
["PropertyName"] = $"Testing",
83+
["PropertyName"] = $"Testing1",
8384
["PropertyValue"] = t,
8485
["MaxLengthName"] = "",
8586
["MaxLengthValue"] = 3,
@@ -89,5 +90,85 @@ private static void __ValidateTesting(
8990
}
9091
}
9192

93+
private static void __Validatedata0(
94+
ValidationResult errors, ValidateClass instance, int target, int counter0
95+
)
96+
{
97+
98+
var t = target;
99+
100+
101+
102+
{
103+
if (!global::Immediate.Validations.Shared.GreaterThanAttribute.ValidateProperty(
104+
t
105+
, comparison: 0
106+
)
107+
)
108+
{
109+
errors.Add(
110+
$"data[{counter0}]",
111+
global::Immediate.Validations.Shared.GreaterThanAttribute.DefaultMessage,
112+
new()
113+
{
114+
["PropertyName"] = $"data[{counter0}]",
115+
["PropertyValue"] = t,
116+
["ComparisonName"] = "",
117+
["ComparisonValue"] = 0,
118+
}
119+
);
120+
}
121+
}
122+
}
123+
124+
private static void __Validatedata(
125+
ValidationResult errors, ValidateClass instance, global::System.Collections.Generic.List<int> target
126+
)
127+
{
128+
129+
if (target is not { } t)
130+
{
131+
errors.Add(
132+
$"data",
133+
global::Immediate.Validations.Shared.NotNullAttribute.DefaultMessage,
134+
new()
135+
{
136+
["PropertyName"] = $"data",
137+
["PropertyValue"] = null,
138+
}
139+
);
140+
141+
return;
142+
}
143+
144+
145+
var counter0 = 0;
146+
foreach (var item0 in t)
147+
{
148+
__Validatedata0(
149+
errors, instance, item0, counter0
150+
);
151+
counter0++;
152+
}
153+
154+
{
155+
if (!global::Immediate.Validations.Shared.NotEmptyAttribute.ValidateProperty(
156+
t
157+
)
158+
)
159+
{
160+
errors.Add(
161+
$"data",
162+
global::Immediate.Validations.Shared.NotEmptyAttribute.DefaultMessage,
163+
new()
164+
{
165+
["PropertyName"] = $"data",
166+
["PropertyValue"] = t,
167+
}
168+
);
169+
}
170+
}
171+
}
172+
92173
}
93174

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,17 @@ public async Task PropertyValidationOnRecord()
88
var result = GeneratorTestHelper.RunGenerator(
99
"""
1010
using System.ComponentModel;
11+
using System.Collections.Generic;
1112
using Immediate.Validations.Shared;
1213
1314
[Validate]
1415
public sealed partial record ValidateClass(
15-
[property: MaxLength(3)] string Testing
16+
[property: MaxLength(3)]
17+
string Testing1,
18+
19+
[property: NotEmpty]
20+
[element: GreaterThan(0)]
21+
List<int> data
1622
): IValidationTarget<ValidateClass>;
1723
"""
1824
);

0 commit comments

Comments
 (0)