Skip to content

Commit 4fb5a14

Browse files
authored
Descriptive Error for Improper Use of [AsParameters] (#58218)
* Instead of throwing there, we emit diagnostics when the type is invalid. * symbol-based comparison
1 parent b8809ad commit 4fb5a14

File tree

5 files changed

+32
-2
lines changed

5 files changed

+32
-2
lines changed

src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/DiagnosticDescriptors.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,13 @@ internal static class DiagnosticDescriptors
128128
DiagnosticSeverity.Warning,
129129
isEnabledByDefault: true,
130130
helpLinkUri: GetHelpLinkUrl("RDG013"));
131+
132+
public static DiagnosticDescriptor InvalidAsParametersEnumerableType { get; } = new(
133+
"RDG014",
134+
new LocalizableResourceString(nameof(Resources.InvalidAsParametersEnumerableType_Title), Resources.ResourceManager, typeof(Resources)),
135+
new LocalizableResourceString(nameof(Resources.InvalidAsParametersEnumerableType_Message), Resources.ResourceManager, typeof(Resources)),
136+
"Usage",
137+
DiagnosticSeverity.Warning,
138+
isEnabledByDefault: true,
139+
helpLinkUri: GetHelpLinkUrl("RDG014"));
131140
}

src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/Resources.resx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,4 +195,10 @@
195195
<data name="KeyedAndNotKeyedServiceAttributesNotSupported_Message" xml:space="preserve">
196196
<value>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</value>
197197
</data>
198+
<data name="InvalidAsParametersEnumerableType_Title" xml:space="preserve">
199+
<value>Invalid enumerable type</value>
200+
</data>
201+
<data name="InvalidAsParametersEnumerableType_Message" xml:space="preserve">
202+
<value>The enumerable type '{0}' is not supported. For more information, please see https://aka.ms/aspnet/rdg-known-issues</value>
203+
</data>
198204
</root>

src/Http/Http.Extensions/gen/Microsoft.AspNetCore.Http.RequestDelegateGenerator/StaticRouteHandlerModel/EndpointParameter.cs

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// The .NET Foundation licenses this file to you under the MIT license.
33

44
using System;
5+
using System.Collections;
56
using System.Collections.Generic;
67
using System.Collections.Immutable;
78
using System.Diagnostics.CodeAnalysis;
@@ -168,7 +169,7 @@ private void ProcessEndpointParameterSource(Endpoint endpoint, ISymbol symbol, I
168169
}
169170
if (symbol is IPropertySymbol ||
170171
Type is not INamedTypeSymbol namedTypeSymbol ||
171-
!TryGetAsParametersConstructor(endpoint, namedTypeSymbol, out var isDefaultConstructor, out var matchedProperties))
172+
!TryGetAsParametersConstructor(endpoint, namedTypeSymbol, wellKnownTypes, out var isDefaultConstructor, out var matchedProperties))
172173
{
173174
if (symbol is IPropertySymbol)
174175
{
@@ -454,7 +455,7 @@ private static string GetEscapedParameterName(AttributeData attribute, string pa
454455
}
455456
}
456457

457-
private static bool TryGetAsParametersConstructor(Endpoint endpoint, INamedTypeSymbol type, out bool? isDefaultConstructor, [NotNullWhen(true)] out IEnumerable<ConstructorParameter>? matchedProperties)
458+
private static bool TryGetAsParametersConstructor(Endpoint endpoint, INamedTypeSymbol type, WellKnownTypes wellKnownTypes, out bool? isDefaultConstructor, [NotNullWhen(true)] out IEnumerable<ConstructorParameter>? matchedProperties)
458459
{
459460
isDefaultConstructor = null;
460461
matchedProperties = null;
@@ -466,6 +467,12 @@ private static bool TryGetAsParametersConstructor(Endpoint endpoint, INamedTypeS
466467
return false;
467468
}
468469

470+
if (type.Implements(wellKnownTypes.Get(WellKnownType.System_Collections_IEnumerable)))
471+
{
472+
endpoint.Diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.InvalidAsParametersEnumerableType, location, parameterTypeString));
473+
return false;
474+
}
475+
469476
var constructors = type.Constructors.Where(constructor => constructor.DeclaredAccessibility == Accessibility.Public && !constructor.IsStatic);
470477
var numOfConstructors = constructors.Count();
471478
// When leveraging parameterless constructors, we want to ensure we only emit for writable

src/Http/Http.Extensions/test/RequestDelegateGenerator/CompileTimeCreationTests.AsParameters.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,13 +50,19 @@ static string GetNoContructorsError(Type type)
5050
static string GetInvalidConstructorError(Type type)
5151
=> $"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";
5252

53+
static string GetEnumerableTypeError(Type type)
54+
=> $"The enumerable type '{TypeNameHelper.GetTypeDisplayName(type, fullName: false)}' is not supported. For more information, please see https://aka.ms/aspnet/rdg-known-issues";
55+
5356
return new []
5457
{
5558
new object[] { "BadArgumentListRecord", DiagnosticDescriptors.InvalidAsParametersSingleConstructorOnly.Id, GetMultipleContructorsError(typeof(BadArgumentListRecord)) },
5659
new object[] { "BadArgumentListClass", DiagnosticDescriptors.InvalidAsParametersSignature.Id, GetInvalidConstructorError(typeof(BadArgumentListClass)) },
5760
new object[] { "BadArgumentListClassMultipleCtors", DiagnosticDescriptors.InvalidAsParametersSingleConstructorOnly.Id, GetMultipleContructorsError(typeof(BadArgumentListClassMultipleCtors)) },
5861
new object[] { "BadAbstractArgumentListClass", DiagnosticDescriptors.InvalidAsParametersAbstractType.Id, GetAbstractTypeError(typeof(BadAbstractArgumentListClass)) },
5962
new object[] { "BadNoPublicConstructorArgumentListClass", DiagnosticDescriptors.InvalidAsParametersNoConstructorFound.Id, GetNoContructorsError(typeof(BadNoPublicConstructorArgumentListClass)) },
63+
new object[] { "List<string>", DiagnosticDescriptors.InvalidAsParametersEnumerableType.Id, GetEnumerableTypeError(typeof(List<string>)) },
64+
new object[] { "List<IFormFile>", DiagnosticDescriptors.InvalidAsParametersEnumerableType.Id, GetEnumerableTypeError(typeof(List<IFormFile>)) },
65+
new object[] { "Dictionary<string, string>", DiagnosticDescriptors.InvalidAsParametersEnumerableType.Id, GetEnumerableTypeError(typeof(Dictionary<string, string>)) }
6066
};
6167
}
6268
}

src/Shared/RoslynUtils/WellKnownTypeData.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public enum WellKnownType
3232
Microsoft_AspNetCore_Http_IFormCollection,
3333
Microsoft_AspNetCore_Http_IFormFileCollection,
3434
Microsoft_AspNetCore_Http_IFormFile,
35+
System_Collections_IEnumerable,
3536
System_DateOnly,
3637
System_DateTimeOffset,
3738
System_IO_Stream,
@@ -146,6 +147,7 @@ public enum WellKnownType
146147
"Microsoft.AspNetCore.Http.IFormCollection",
147148
"Microsoft.AspNetCore.Http.IFormFileCollection",
148149
"Microsoft.AspNetCore.Http.IFormFile",
150+
"System.Collections.IEnumerable",
149151
"System.DateOnly",
150152
"System.DateTimeOffset",
151153
"System.IO.Stream",

0 commit comments

Comments
 (0)