Skip to content

Commit 1b16209

Browse files
authored
Emit diagnostics for unsupported RDG scenarios (#49417)
* Emit diagnostics for unsupported RDG scenarios * Add test for invalid/valid endpoints
1 parent c69cb31 commit 1b16209

File tree

9 files changed

+632
-11
lines changed

9 files changed

+632
-11
lines changed

src/Http/Http.Extensions/gen/DiagnosticDescriptors.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,4 +90,20 @@ internal static class DiagnosticDescriptors
9090
"Usage",
9191
DiagnosticSeverity.Error,
9292
isEnabledByDefault: true);
93+
94+
public static DiagnosticDescriptor TypeParametersNotSupported { get; } = new(
95+
"RDG011",
96+
new LocalizableResourceString(nameof(Resources.TypeParametersNotSupported_Title), Resources.ResourceManager, typeof(Resources)),
97+
new LocalizableResourceString(nameof(Resources.TypeParametersNotSupported_Message), Resources.ResourceManager, typeof(Resources)),
98+
"Usage",
99+
DiagnosticSeverity.Error,
100+
isEnabledByDefault: true);
101+
102+
public static DiagnosticDescriptor InaccessibleTypesNotSupported { get; } = new(
103+
"RDG012",
104+
new LocalizableResourceString(nameof(Resources.InaccessibleTypesNotSupported_Title), Resources.ResourceManager, typeof(Resources)),
105+
new LocalizableResourceString(nameof(Resources.InaccessibleTypesNotSupported_Message), Resources.ResourceManager, typeof(Resources)),
106+
"Usage",
107+
DiagnosticSeverity.Error,
108+
isEnabledByDefault: true);
93109
}

src/Http/Http.Extensions/gen/Resources.resx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,16 @@
177177
<data name="InvalidAsParametersNullable_Message" xml:space="preserve">
178178
<value>The nullable type '{0}' is not supported. Mark the parameter as non-nullable.</value>
179179
</data>
180+
<data name="TypeParametersNotSupported_Title" xml:space="preserve">
181+
<value>Type parameters not supported</value>
182+
</data>
183+
<data name="TypeParametersNotSupported_Message" xml:space="preserve">
184+
<value>Endpoints with generic type parameters are not supported. Compile-time endpoint generation will skip this endpoint.</value>
185+
</data>
186+
<data name="InaccessibleTypesNotSupported_Title" xml:space="preserve">
187+
<value>Unable to resolve inaccessible type</value>
188+
</data>
189+
<data name="InaccessibleTypesNotSupported_Message" xml:space="preserve">
190+
<value>Encountered inaccessible type '{0}' while processing endpoint. Compile-time endpoint generation will skip this endpoint.</value>
191+
</data>
180192
</root>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using Microsoft.CodeAnalysis;
7+
8+
namespace Microsoft.AspNetCore.Http.RequestDelegateGenerator.StaticRouteHandlerModel;
9+
10+
internal static class DiagnosticEmitter
11+
{
12+
public static void EmitRequiredDiagnostics(this EndpointResponse response, List<Diagnostic> diagnostics, Location location)
13+
{
14+
if (response.ResponseType is ITypeParameterSymbol)
15+
{
16+
diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.TypeParametersNotSupported, location));
17+
}
18+
19+
if (response.ResponseType?.DeclaredAccessibility is Accessibility.Private or Accessibility.Protected)
20+
{
21+
diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.InaccessibleTypesNotSupported, location));
22+
}
23+
24+
if (response.ResponseType?.IsAnonymousType == true)
25+
{
26+
diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.UnableToResolveAnonymousReturnType, location));
27+
}
28+
}
29+
30+
public static void EmitRequiredDiagnostics(this IParameterSymbol parameterSymbol, List<Diagnostic> diagnostics, Location location)
31+
{
32+
var typeSymbol = parameterSymbol.Type;
33+
if (typeSymbol is ITypeParameterSymbol ||
34+
typeSymbol is INamedTypeSymbol &&
35+
((INamedTypeSymbol)typeSymbol).TypeArguments.Any(a => a is ITypeParameterSymbol))
36+
{
37+
diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.TypeParametersNotSupported, location));
38+
}
39+
40+
if (typeSymbol.DeclaredAccessibility is Accessibility.Private or Accessibility.Protected ||
41+
typeSymbol is INamedTypeSymbol &&
42+
((INamedTypeSymbol)typeSymbol).TypeArguments.Any(typeArg =>
43+
typeArg.DeclaredAccessibility is Accessibility.Private or Accessibility.Protected))
44+
{
45+
diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.InaccessibleTypesNotSupported, location, typeSymbol.Name));
46+
}
47+
}
48+
}

src/Http/Http.Extensions/gen/StaticRouteHandlerModel/Endpoint.cs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,7 @@ public Endpoint(IInvocationOperation operation, WellKnownTypes wellKnownTypes, S
2929
}
3030

3131
Response = new EndpointResponse(method, wellKnownTypes);
32-
if (Response.IsAnonymousType)
33-
{
34-
Diagnostics.Add(Diagnostic.Create(DiagnosticDescriptors.UnableToResolveAnonymousReturnType, Operation.Syntax.GetLocation()));
35-
return;
36-
}
37-
32+
Response.EmitRequiredDiagnostics(Diagnostics, Operation.Syntax.GetLocation());
3833
IsAwaitable = Response?.IsAwaitable == true;
3934

4035
EmitterContext.HasResponseMetadata = Response is { } response && !(response.IsIResult || response.HasNoResponse);
@@ -55,7 +50,13 @@ public Endpoint(IInvocationOperation operation, WellKnownTypes wellKnownTypes, S
5550

5651
for (var i = 0; i < method.Parameters.Length; i++)
5752
{
58-
var parameter = new EndpointParameter(this, method.Parameters[i], wellKnownTypes);
53+
var parameterSymbol = method.Parameters[i];
54+
parameterSymbol.EmitRequiredDiagnostics(Diagnostics, Operation.Syntax.GetLocation());
55+
if (Diagnostics.Count > 0)
56+
{
57+
continue;
58+
}
59+
var parameter = new EndpointParameter(this, parameterSymbol, wellKnownTypes);
5960

6061
switch (parameter.Source)
6162
{
@@ -82,6 +83,7 @@ public Endpoint(IInvocationOperation operation, WellKnownTypes wellKnownTypes, S
8283
Parameters = parameters;
8384

8485
EmitterContext.RequiresLoggingHelper = !Parameters.All(parameter =>
86+
parameter is not null &&
8587
parameter.Source == EndpointParameterSource.SpecialType ||
8688
parameter is { IsArray: true, ElementType.SpecialType: SpecialType.System_String, Source: EndpointParameterSource.Query });
8789
}

src/Http/Http.Extensions/gen/StaticRouteHandlerModel/EndpointResponse.cs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ internal class EndpointResponse
2020
public bool HasNoResponse { get; set; }
2121
public bool IsIResult { get; set; }
2222
public bool IsSerializable { get; set; }
23-
public bool IsAnonymousType { get; set; }
2423
public bool IsEndpointMetadataProvider { get; set; }
2524
private WellKnownTypes WellKnownTypes { get; init; }
2625

@@ -34,7 +33,6 @@ internal EndpointResponse(IMethodSymbol method, WellKnownTypes wellKnownTypes)
3433
IsIResult = GetIsIResult();
3534
IsSerializable = GetIsSerializable();
3635
ContentType = GetContentType();
37-
IsAnonymousType = method.ReturnType.IsAnonymousType;
3836
IsEndpointMetadataProvider = ImplementsIEndpointMetadataProvider(ResponseType, wellKnownTypes);
3937
}
4038

0 commit comments

Comments
 (0)