Skip to content

Commit 0b3cd8c

Browse files
authored
Fix handling of nullable types in validations generator (#61766)
* Fix handling of nullable types in validations generator * Fix property type in parsable test
1 parent 0862db9 commit 0b3cd8c

File tree

5 files changed

+12
-9
lines changed

5 files changed

+12
-9
lines changed

src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.ValidationsGenerator/Extensions/ITypeSymbolExtensions.cs

+3-2
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,9 @@ public static ITypeSymbol UnwrapType(this ITypeSymbol type, INamedTypeSymbol enu
4646

4747
if (type.NullableAnnotation == NullableAnnotation.Annotated)
4848
{
49-
// Extract the underlying type from a reference type
50-
type = type.OriginalDefinition;
49+
// Remove the nullable annotation but keep any generic arguments, e.g. List<int>? → List<int>
50+
// so we can retain them in future steps.
51+
type = type.WithNullableAnnotation(NullableAnnotation.NotAnnotated);
5152
}
5253

5354
if (type is INamedTypeSymbol namedType && namedType.IsEnumerable(enumerable) && namedType.TypeArguments.Length == 1)

src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.ValidationsGenerator/Parsers/ValidationsGenerator.TypesParser.cs

+5-4
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,14 @@ internal ImmutableArray<ValidatableType> ExtractValidatableTypes(IInvocationOper
2929
List<ITypeSymbol> visitedTypes = [];
3030
foreach (var parameter in parameters)
3131
{
32-
_ = TryExtractValidatableType(parameter.Type.UnwrapType(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Collections_IEnumerable)), wellKnownTypes, ref validatableTypes, ref visitedTypes);
32+
_ = TryExtractValidatableType(parameter.Type, wellKnownTypes, ref validatableTypes, ref visitedTypes);
3333
}
3434
return [.. validatableTypes];
3535
}
3636

37-
internal bool TryExtractValidatableType(ITypeSymbol typeSymbol, WellKnownTypes wellKnownTypes, ref HashSet<ValidatableType> validatableTypes, ref List<ITypeSymbol> visitedTypes)
37+
internal bool TryExtractValidatableType(ITypeSymbol incomingTypeSymbol, WellKnownTypes wellKnownTypes, ref HashSet<ValidatableType> validatableTypes, ref List<ITypeSymbol> visitedTypes)
3838
{
39+
var typeSymbol = incomingTypeSymbol.UnwrapType(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Collections_IEnumerable));
3940
if (typeSymbol.SpecialType != SpecialType.None)
4041
{
4142
return false;
@@ -126,7 +127,7 @@ internal ImmutableArray<ValidatableProperty> ExtractValidatableMembers(ITypeSymb
126127
// Check if the property's type is validatable, this resolves
127128
// validatable types in the inheritance hierarchy
128129
var hasValidatableType = TryExtractValidatableType(
129-
correspondingProperty.Type.UnwrapType(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Collections_IEnumerable)),
130+
correspondingProperty.Type,
130131
wellKnownTypes,
131132
ref validatableTypes,
132133
ref visitedTypes);
@@ -153,7 +154,7 @@ internal ImmutableArray<ValidatableProperty> ExtractValidatableMembers(ITypeSymb
153154
continue;
154155
}
155156

156-
var hasValidatableType = TryExtractValidatableType(member.Type.UnwrapType(wellKnownTypes.Get(WellKnownTypeData.WellKnownType.System_Collections_IEnumerable)), wellKnownTypes, ref validatableTypes, ref visitedTypes);
157+
var hasValidatableType = TryExtractValidatableType(member.Type, wellKnownTypes, ref validatableTypes, ref visitedTypes);
157158
var attributes = ExtractValidationAttributes(member, wellKnownTypes, out var isRequired);
158159

159160
// If the member has no validation attributes or validatable types and is not required, skip it.

src/Http/Http.Extensions/test/ValidationsGenerator/ValidationsGenerator.ComplexType.cs

+2-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ public class ComplexType
4545
4646
public SubTypeWithInheritance PropertyWithInheritance { get; set; } = new SubTypeWithInheritance("some-value", default);
4747
48-
public List<SubType> ListOfSubTypes { get; set; } = [];
48+
// Nullable to validate https://github.com/dotnet/aspnetcore/issues/61737
49+
public List<SubType>? ListOfSubTypes { get; set; } = [];
4950
5051
[DerivedValidation(ErrorMessage = "Value must be an even number")]
5152
public int IntegerWithDerivedValidationAttribute { get; set; }

src/Http/Http.Extensions/test/ValidationsGenerator/ValidationsGenerator.Parsable.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public class ComplexTypeWithParsableProperties
3939
public TimeOnly? TimeOnlyWithRequiredValue { get; set; } = TimeOnly.FromDateTime(DateTime.UtcNow);
4040
4141
[Url(ErrorMessage = "The field Url must be a valid URL.")]
42-
public Uri? Url { get; set; } = new Uri("https://example.com");
42+
public string? Url { get; set; } = "https://example.com";
4343
4444
[Required]
4545
[Range(typeof(DateOnly), "2023-01-01", "2025-12-31", ErrorMessage = "Date must be between 2023-01-01 and 2025-12-31")]

src/Http/Http.Extensions/test/ValidationsGenerator/snapshots/ValidationsGeneratorTests.CanValidateTypeWithParsableProperties#ValidatableInfoResolver.g.verified.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ private ValidatableTypeInfo CreateComplexTypeWithParsableProperties()
9595
),
9696
new GeneratedValidatablePropertyInfo(
9797
containingType: typeof(global::ComplexTypeWithParsableProperties),
98-
propertyType: typeof(global::System.Uri),
98+
propertyType: typeof(string),
9999
name: "Url",
100100
displayName: "Url"
101101
),

0 commit comments

Comments
 (0)