diff --git a/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/DiagnosticDescriptors.cs b/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/DiagnosticDescriptors.cs index 53842cfcd9ce..298ac3eb4e90 100644 --- a/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/DiagnosticDescriptors.cs +++ b/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/DiagnosticDescriptors.cs @@ -125,4 +125,13 @@ internal static class DiagnosticDescriptors DiagnosticSeverity.Warning, isEnabledByDefault: true, helpLinkUri: GetHelpLinkUrl("RDG013")); + + public static DiagnosticDescriptor InvalidAsParametersEnumerableType { get; } = new( + "RDG014", + new LocalizableResourceString(nameof(Resources.InvalidAsParametersEnumerableType_Title), Resources.ResourceManager, typeof(Resources)), + new LocalizableResourceString(nameof(Resources.InvalidAsParametersEnumerableType_Message), Resources.ResourceManager, typeof(Resources)), + "Usage", + DiagnosticSeverity.Warning, + isEnabledByDefault: true, + helpLinkUri: GetHelpLinkUrl("RDG014")); } diff --git a/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/Resources.resx b/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/Resources.resx index ba39a95278ac..a83fbe4a38df 100644 --- a/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/Resources.resx +++ b/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/Resources.resx @@ -195,4 +195,10 @@ The [FromKeyedServices] attribute is not supported on parameters that are also annotated with IFromServiceMetadata. For more information, please see https://aka.ms/aspnet/rdg-known-issues + + Invalid enumerable type + + + The enumerable type '{0}' is not supported. For more information, please see https://aka.ms/aspnet/rdg-known-issues + diff --git a/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/StaticRouteHandlerModel/EndpointParameter.cs b/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/StaticRouteHandlerModel/EndpointParameter.cs index 8bea8558fea4..301e1d524d71 100644 --- a/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/StaticRouteHandlerModel/EndpointParameter.cs +++ b/src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/StaticRouteHandlerModel/EndpointParameter.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; @@ -168,7 +169,7 @@ private void ProcessEndpointParameterSource(Endpoint endpoint, ISymbol symbol, I } if (symbol is IPropertySymbol || Type is not INamedTypeSymbol namedTypeSymbol || - !TryGetAsParametersConstructor(endpoint, namedTypeSymbol, out var isDefaultConstructor, out var matchedProperties)) + !TryGetAsParametersConstructor(endpoint, namedTypeSymbol, wellKnownTypes, out var isDefaultConstructor, out var matchedProperties)) { if (symbol is IPropertySymbol) { @@ -454,7 +455,7 @@ private static string GetEscapedParameterName(AttributeData attribute, string pa } } - private static bool TryGetAsParametersConstructor(Endpoint endpoint, INamedTypeSymbol type, out bool? isDefaultConstructor, [NotNullWhen(true)] out IEnumerable? matchedProperties) + private static bool TryGetAsParametersConstructor(Endpoint endpoint, INamedTypeSymbol type, WellKnownTypes wellKnownTypes, out bool? isDefaultConstructor, [NotNullWhen(true)] out IEnumerable? matchedProperties) { isDefaultConstructor = null; matchedProperties = null; @@ -466,6 +467,12 @@ private static bool TryGetAsParametersConstructor(Endpoint endpoint, INamedTypeS return false; } + if (type.Implements(wellKnownTypes.Get(WellKnownType.System_Collections_IEnumerable))) + { + endpoint.Diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.InvalidAsParametersEnumerableType, location, parameterTypeString)); + return false; + } + var constructors = type.Constructors.Where(constructor => constructor.DeclaredAccessibility == Accessibility.Public && !constructor.IsStatic); var numOfConstructors = constructors.Count(); // When leveraging parameterless constructors, we want to ensure we only emit for writable diff --git a/src/Http/Http.Extensions/test/RequestDelegateGenerator/CompileTimeCreationTests.AsParameters.cs b/src/Http/Http.Extensions/test/RequestDelegateGenerator/CompileTimeCreationTests.AsParameters.cs index ced9bb67a029..a42149b8286a 100644 --- a/src/Http/Http.Extensions/test/RequestDelegateGenerator/CompileTimeCreationTests.AsParameters.cs +++ b/src/Http/Http.Extensions/test/RequestDelegateGenerator/CompileTimeCreationTests.AsParameters.cs @@ -50,6 +50,9 @@ static string GetNoContructorsError(Type type) static string GetInvalidConstructorError(Type type) => $"The public parameterized constructor must contain only parameters that match the declared public properties for type '{TypeNameHelper.GetTypeDisplayName(type, fullName: false)}'. For more information, please see https://aka.ms/aspnet/rdg-known-issues"; + static string GetEnumerableTypeError(Type type) + => $"The enumerable type '{TypeNameHelper.GetTypeDisplayName(type, fullName: false)}' is not supported. For more information, please see https://aka.ms/aspnet/rdg-known-issues"; + return new [] { new object[] { "BadArgumentListRecord", DiagnosticDescriptors.InvalidAsParametersSingleConstructorOnly.Id, GetMultipleContructorsError(typeof(BadArgumentListRecord)) }, @@ -57,6 +60,9 @@ static string GetInvalidConstructorError(Type type) new object[] { "BadArgumentListClassMultipleCtors", DiagnosticDescriptors.InvalidAsParametersSingleConstructorOnly.Id, GetMultipleContructorsError(typeof(BadArgumentListClassMultipleCtors)) }, new object[] { "BadAbstractArgumentListClass", DiagnosticDescriptors.InvalidAsParametersAbstractType.Id, GetAbstractTypeError(typeof(BadAbstractArgumentListClass)) }, new object[] { "BadNoPublicConstructorArgumentListClass", DiagnosticDescriptors.InvalidAsParametersNoConstructorFound.Id, GetNoContructorsError(typeof(BadNoPublicConstructorArgumentListClass)) }, + new object[] { "List", DiagnosticDescriptors.InvalidAsParametersEnumerableType.Id, GetEnumerableTypeError(typeof(List)) }, + new object[] { "List", DiagnosticDescriptors.InvalidAsParametersEnumerableType.Id, GetEnumerableTypeError(typeof(List)) }, + new object[] { "Dictionary", DiagnosticDescriptors.InvalidAsParametersEnumerableType.Id, GetEnumerableTypeError(typeof(Dictionary)) } }; } } diff --git a/src/Shared/RoslynUtils/WellKnownTypeData.cs b/src/Shared/RoslynUtils/WellKnownTypeData.cs index 7a4fa428c105..494357b17a3a 100644 --- a/src/Shared/RoslynUtils/WellKnownTypeData.cs +++ b/src/Shared/RoslynUtils/WellKnownTypeData.cs @@ -32,6 +32,7 @@ public enum WellKnownType Microsoft_AspNetCore_Http_IFormCollection, Microsoft_AspNetCore_Http_IFormFileCollection, Microsoft_AspNetCore_Http_IFormFile, + System_Collections_IEnumerable, System_DateOnly, System_DateTimeOffset, System_IO_Stream, @@ -146,6 +147,7 @@ public enum WellKnownType "Microsoft.AspNetCore.Http.IFormCollection", "Microsoft.AspNetCore.Http.IFormFileCollection", "Microsoft.AspNetCore.Http.IFormFile", + "System.Collections.IEnumerable", "System.DateOnly", "System.DateTimeOffset", "System.IO.Stream",