Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Options Source Gen with Length attributes applied on properties of Interface type #93426

Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,26 @@ internal static bool TypeHasProperty(ITypeSymbol typeSymbol, string propertyName

if (type.GetMembers(propertyName).OfType<IPropertySymbol>().Any(property =>
property.Type.SpecialType == returnType && property.DeclaredAccessibility == Accessibility.Public &&
!property.IsStatic && property.GetMethod != null && property.Parameters.IsEmpty))
property.Kind == SymbolKind.Property && !property.IsStatic && property.GetMethod != null && property.Parameters.IsEmpty))
{
return true;
}

type = type.BaseType;
} while (type is not null && type.SpecialType != SpecialType.System_Object);

// When we have an interface type, we need to check all the interfaces that it extends.
// Like IList<T> extends ICollection<T> where the property we're looking for is defined.
foreach (var interfaceType in typeSymbol.AllInterfaces)
{
if (interfaceType.GetMembers(propertyName).OfType<IPropertySymbol>().Any(property =>
property.Type.SpecialType == returnType && property.Kind == SymbolKind.Property &&
!property.IsStatic && property.GetMethod != null && property.Parameters.IsEmpty))
{
return true;
}
}

return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1813,4 +1813,44 @@ public sealed partial class OptionsUsingGeneratedAttributesValidator : IValidate
Assert.True(emitResult.Success);
// Console.WriteLine(emittedSource);
}

[ConditionalFact(typeof(PlatformDetection), nameof(PlatformDetection.IsNotBrowser))]
public async Task UsingInterfaceAsPropertyTypeForLengthAttributesTests()
{
var (diagnostics, generatedSources) = await RunGenerator(@"""
using System.Collections.Generic;

public class MyOptions
{
[Length(10, 20)]
public IList<string> P1 { get; set; }

[MinLength(4)]
public IList<string> P2 { get; set; }

[MaxLength(5)]
public IList<string> P3 { get; set; }

[Length(10, 20)]
public ICollection<string> P4 { get; set; }

[MinLength(4)]
public ICollection<string> P5 { get; set; }

[MaxLength(5)]
public ICollection<string> P6 { get; set; }
}

[OptionsValidator]
public partial class MyOptionsValidator : IValidateOptions<MyOptions>
{
}
""");

Assert.Empty(diagnostics);
Assert.Single(generatedSources);

// string generatedSource = File.ReadAllText(@"Baselines/DataAnnotationAttributesWithParams.g.cs");
// Assert.Equal(generatedSource.Replace("\r\n", "\n"), generatedSources[0].SourceText.ToString().Replace("\r\n", "\n"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,12 @@ public void TestCustomGeneratedAttributes()
P23 = new List<string>() { "1", "2", "3", "4" },
P24 = new FakeCount(4),
P25 = new FakeCountChild(4),
P27 = new List<string> { "1", "2" },
P28 = new HashSet<string> { "1", "2" },
P29 = new List<string> { "1", "2", "3" },
P30 = new HashSet<string> { "1", "2", "3" },
P31 = new List<int> { 1, 2, 3, 4 },
P32 = new HashSet<int> { 1, 2, 3, 4 },
#endif // NET8_0_OR_GREATER
P1 = 2,
P2 = "12345",
Expand Down Expand Up @@ -325,6 +331,12 @@ public void TestCustomGeneratedAttributes()
P23 = new List<string>() { "1", "2", "3", "4", "5" },
P24 = new FakeCount(5),
P25 = new FakeCountChild(5),
P27 = new List<string> { "1" },
P28 = new HashSet<string> { "1" },
P29 = new List<string> { "1", "2" },
P30 = new HashSet<string> { "1", "2" },
P31 = new List<int> { 1, 2, 3, 4, 5 },
P32 = new HashSet<int> { 1, 2, 3, 4, 5 },
#endif // NET8_0_OR_GREATER
P1 = 4,
P2 = "1234",
Expand Down Expand Up @@ -362,6 +374,12 @@ public void TestCustomGeneratedAttributes()
"P23: The field OptionsUsingGeneratedAttributes.P23 must be a string or array type with a maximum length of '4'.",
"P24: The field OptionsUsingGeneratedAttributes.P24 must be a string or array type with a maximum length of '4'.",
"P25: The field OptionsUsingGeneratedAttributes.P25 must be a string or array type with a maximum length of '4'.",
"P27: The field OptionsUsingGeneratedAttributes.P27 must be a string or collection type with a minimum length of '2' and maximum length of '10'.",
"P28: The field OptionsUsingGeneratedAttributes.P28 must be a string or collection type with a minimum length of '2' and maximum length of '10'.",
"P29: The field OptionsUsingGeneratedAttributes.P29 must be a string or array type with a minimum length of '3'.",
"P30: The field OptionsUsingGeneratedAttributes.P30 must be a string or array type with a minimum length of '3'.",
"P31: The field OptionsUsingGeneratedAttributes.P31 must be a string or array type with a maximum length of '4'.",
"P32: The field OptionsUsingGeneratedAttributes.P32 must be a string or array type with a maximum length of '4'.",
#endif // NET8_0_OR_GREATER
"P1: The field OptionsUsingGeneratedAttributes.P1 must be between 1 and 3.",
"P2: The field OptionsUsingGeneratedAttributes.P2 must be a string or array type with a minimum length of '5'.",
Expand Down Expand Up @@ -412,6 +430,9 @@ public class OptionsUsingGeneratedAttributes
[LengthAttribute(2, 10)]
public int[] P17 { get; set; }

// Although MinLength and MaxLength attributes defined in NETFX but the implementation there has a bug which can produce exception like the following when using types like List<string>:
// System.InvalidCastException : Unable to cast object of type 'System.Collections.Generic.List`1[System.String]' to type 'System.Array'.

[MinLengthAttribute(3)]
public List<string> P18 { get; set; }

Expand All @@ -429,6 +450,24 @@ public class OptionsUsingGeneratedAttributes

[MaxLengthAttribute(4)]
public FakeCountChild P25 { get; set; }

[LengthAttribute(2, 10)]
public IList<string> P27 { get; set; }

[LengthAttribute(2, 10)]
public ICollection<string> P28 { get; set; }

[MinLengthAttribute(3)]
public IList<string> P29 { get; set; }

[MinLengthAttribute(3)]
public ICollection<string> P30 { get; set; }

[MaxLengthAttribute(4)]
public IList<int> P31 { get; set; }

[MaxLengthAttribute(4)]
public ICollection<int> P32 { get; set; }
#endif // NET8_0_OR_GREATER

[RangeAttribute(1, 3)]
Expand Down
Loading