Skip to content

Commit 182f02d

Browse files
committed
Add infrastructure for RequestDelegateGenerator
1 parent 63cd49a commit 182f02d

14 files changed

+1178
-2
lines changed

AspNetCore.sln

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1762,6 +1762,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server
17621762
EndProject
17631763
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Server.Kestrel.Transport.NamedPipes.Tests", "src\Servers\Kestrel\Transport.NamedPipes\test\Microsoft.AspNetCore.Server.Kestrel.Transport.NamedPipes.Tests.csproj", "{97C7D2A4-87E5-4A4A-A170-D736427D5C21}"
17641764
EndProject
1765+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNetCore.Http.SourceGeneration", "src\Http\Http.Extensions\gen\Microsoft.AspNetCore.Http.SourceGeneration.csproj", "{4730F56D-24EF-4BB2-AA75-862E31205F3A}"
1766+
EndProject
17651767
Global
17661768
GlobalSection(SolutionConfigurationPlatforms) = preSolution
17671769
Debug|Any CPU = Debug|Any CPU
@@ -10571,6 +10573,22 @@ Global
1057110573
{97C7D2A4-87E5-4A4A-A170-D736427D5C21}.Release|x64.Build.0 = Release|Any CPU
1057210574
{97C7D2A4-87E5-4A4A-A170-D736427D5C21}.Release|x86.ActiveCfg = Release|Any CPU
1057310575
{97C7D2A4-87E5-4A4A-A170-D736427D5C21}.Release|x86.Build.0 = Release|Any CPU
10576+
{4730F56D-24EF-4BB2-AA75-862E31205F3A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
10577+
{4730F56D-24EF-4BB2-AA75-862E31205F3A}.Debug|Any CPU.Build.0 = Debug|Any CPU
10578+
{4730F56D-24EF-4BB2-AA75-862E31205F3A}.Debug|arm64.ActiveCfg = Debug|Any CPU
10579+
{4730F56D-24EF-4BB2-AA75-862E31205F3A}.Debug|arm64.Build.0 = Debug|Any CPU
10580+
{4730F56D-24EF-4BB2-AA75-862E31205F3A}.Debug|x64.ActiveCfg = Debug|Any CPU
10581+
{4730F56D-24EF-4BB2-AA75-862E31205F3A}.Debug|x64.Build.0 = Debug|Any CPU
10582+
{4730F56D-24EF-4BB2-AA75-862E31205F3A}.Debug|x86.ActiveCfg = Debug|Any CPU
10583+
{4730F56D-24EF-4BB2-AA75-862E31205F3A}.Debug|x86.Build.0 = Debug|Any CPU
10584+
{4730F56D-24EF-4BB2-AA75-862E31205F3A}.Release|Any CPU.ActiveCfg = Release|Any CPU
10585+
{4730F56D-24EF-4BB2-AA75-862E31205F3A}.Release|Any CPU.Build.0 = Release|Any CPU
10586+
{4730F56D-24EF-4BB2-AA75-862E31205F3A}.Release|arm64.ActiveCfg = Release|Any CPU
10587+
{4730F56D-24EF-4BB2-AA75-862E31205F3A}.Release|arm64.Build.0 = Release|Any CPU
10588+
{4730F56D-24EF-4BB2-AA75-862E31205F3A}.Release|x64.ActiveCfg = Release|Any CPU
10589+
{4730F56D-24EF-4BB2-AA75-862E31205F3A}.Release|x64.Build.0 = Release|Any CPU
10590+
{4730F56D-24EF-4BB2-AA75-862E31205F3A}.Release|x86.ActiveCfg = Release|Any CPU
10591+
{4730F56D-24EF-4BB2-AA75-862E31205F3A}.Release|x86.Build.0 = Release|Any CPU
1057410592
EndGlobalSection
1057510593
GlobalSection(SolutionProperties) = preSolution
1057610594
HideSolutionNode = FALSE
@@ -11441,6 +11459,7 @@ Global
1144111459
{F057512B-55BF-4A8B-A027-A0505F8BA10C} = {4FDDC525-4E60-4CAF-83A3-261C5B43721F}
1144211460
{10173568-A65E-44E5-8C6F-4AA49D0577A1} = {F057512B-55BF-4A8B-A027-A0505F8BA10C}
1144311461
{97C7D2A4-87E5-4A4A-A170-D736427D5C21} = {F057512B-55BF-4A8B-A027-A0505F8BA10C}
11462+
{4730F56D-24EF-4BB2-AA75-862E31205F3A} = {225AEDCF-7162-4A86-AC74-06B84660B379}
1144411463
EndGlobalSection
1144511464
GlobalSection(ExtensibilityGlobals) = postSolution
1144611465
SolutionGuid = {3E8720B3-DBDD-498C-B383-2CC32A054E8F}
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<Suppressions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
2+
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
33
<Suppression>
44
<DiagnosticId>PKV004</DiagnosticId>
55
<Target>net7.0</Target>
66
</Suppression>
7+
<Suppression>
8+
<DiagnosticId>PKV004</DiagnosticId>
9+
<Target>net8.0</Target>
10+
</Suppression>
711
</Suppressions>

src/Framework/App.Ref/src/Microsoft.AspNetCore.App.Ref.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ This package is an internal implementation of the .NET Core SDK and is not meant
7777
Private="false"
7878
ReferenceOutputAssembly="false" />
7979

80+
<ProjectReference Include="$(RepoRoot)src\Http\Http.Extensions\gen\Microsoft.AspNetCore.Http.SourceGeneration.csproj"
81+
Private="false"
82+
ReferenceOutputAssembly="false" />
83+
8084
<!-- Enforce build order. Targeting pack needs to bundle analyzers and information about the runtime. -->
8185
<ProjectReference Include="..\..\App.Runtime\src\Microsoft.AspNetCore.App.Runtime.csproj"
8286
Private="false"
@@ -176,6 +180,7 @@ This package is an internal implementation of the .NET Core SDK and is not meant
176180
<_InitialRefPackContent Include="$(ArtifactsDir)bin\Microsoft.AspNetCore.App.Analyzers\$(Configuration)\netstandard2.0\Microsoft.AspNetCore.App.Analyzers.dll" PackagePath="$(AnalyzersPackagePath)dotnet/cs/" />
177181
<_InitialRefPackContent Include="$(ArtifactsDir)bin\Microsoft.AspNetCore.Components.Analyzers\$(Configuration)\netstandard2.0\Microsoft.AspNetCore.Components.Analyzers.dll" PackagePath="$(AnalyzersPackagePath)dotnet/cs/" />
178182
<_InitialRefPackContent Include="$(ArtifactsDir)bin\Microsoft.AspNetCore.App.CodeFixes\$(Configuration)\netstandard2.0\Microsoft.AspNetCore.App.CodeFixes.dll" PackagePath="$(AnalyzersPackagePath)dotnet/cs/" />
183+
<_InitialRefPackContent Include="$(ArtifactsDir)bin\Microsoft.AspNetCore.Http.SourceGeneration\$(Configuration)\netstandard2.0\Microsoft.AspNetCore.Http.SourceGeneration.dll" PackagePath="$(AnalyzersPackagePath)dotnet/cs/" />
179184

180185
<_InitialRefPackContent Include="@(AspNetCoreReferenceAssemblyPath)" PackagePath="$(RefAssemblyPackagePath)" />
181186
<_InitialRefPackContent Include="@(AspNetCoreReferenceDocXml)" PackagePath="$(RefAssemblyPackagePath)" />
Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
<?xml version="1.0" encoding="utf-8"?>
2-
<Suppressions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
2+
<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
33
<Suppression>
44
<DiagnosticId>PKV0001</DiagnosticId>
55
<Target>net7.0</Target>
66
</Suppression>
7+
<Suppression>
8+
<DiagnosticId>PKV0001</DiagnosticId>
9+
<Target>net8.0</Target>
10+
</Suppression>
711
</Suppressions>
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>netstandard2.0</TargetFramework>
5+
<IncludeBuildOutput>false</IncludeBuildOutput>
6+
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
7+
<IsPackable>false</IsPackable>
8+
<IsAnalyzersProject>true</IsAnalyzersProject>
9+
<AddPublicApiAnalyzers>false</AddPublicApiAnalyzers>
10+
</PropertyGroup>
11+
12+
<ItemGroup>
13+
<Reference Include="Microsoft.CodeAnalysis.CSharp" PrivateAssets="all" />
14+
<Reference Include="Microsoft.CodeAnalysis.Common" PrivateAssets="all" />
15+
</ItemGroup>
16+
17+
<ItemGroup>
18+
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
19+
</ItemGroup>
20+
21+
<ItemGroup>
22+
<InternalsVisibleTo Include="Microsoft.AspNetCore.Http.Extensions.Tests" />
23+
</ItemGroup>
24+
25+
</Project>
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
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.Linq;
5+
using System.Text;
6+
using Microsoft.CodeAnalysis;
7+
using Microsoft.CodeAnalysis.CSharp.Syntax;
8+
using Microsoft.CodeAnalysis.Operations;
9+
using Microsoft.AspNetCore.Http.SourceGeneration.StaticRouteHandlerModel;
10+
11+
namespace Microsoft.AspNetCore.Http.SourceGeneration;
12+
13+
[Generator]
14+
public class RequestDelegateGenerator : IIncrementalGenerator
15+
{
16+
private static readonly string[] _knownMethods =
17+
{
18+
"MapGet",
19+
"MapPost",
20+
"MapPut",
21+
"MapDelete",
22+
"MapPatch",
23+
"Map",
24+
};
25+
26+
public void Initialize(IncrementalGeneratorInitializationContext context)
27+
{
28+
var isGeneratorEnabled = context.AnalyzerConfigOptionsProvider.Select((provider, _) =>
29+
provider.GlobalOptions.TryGetValue("build_property.EnableRequestDelegateGenerator", out var enableRequestDelegateGenerator)
30+
&& enableRequestDelegateGenerator == "true");
31+
32+
var mapActionOperations = context.SyntaxProvider.CreateSyntaxProvider(
33+
predicate: (node, _) => node is InvocationExpressionSyntax
34+
{
35+
Expression: MemberAccessExpressionSyntax
36+
{
37+
Name: IdentifierNameSyntax
38+
{
39+
Identifier: { ValueText: var method }
40+
}
41+
},
42+
ArgumentList: { Arguments: { Count: 2 } args }
43+
} && _knownMethods.Contains(method),
44+
transform: (context, token) => context.SemanticModel.GetOperation(context.Node, token) as IInvocationOperation);
45+
46+
// Filter out any map actions if the generator is not enabled
47+
// via config
48+
var conditionalMapActionOperations = mapActionOperations.Combine(isGeneratorEnabled)
49+
.Where(pair => pair.Right)
50+
.Select((pair, _) => pair.Left);
51+
52+
var endpoints = conditionalMapActionOperations
53+
.Select((operation, _) => StaticRouteHandlerModelParser.GetEndpointFromOperation(operation))
54+
.WithTrackingName("EndpointModel");
55+
56+
var genericThunks = endpoints.Select((endpoint, token) =>
57+
{
58+
var code = RequestDelegateGeneratorSources.GetGenericThunks(string.Empty);
59+
return code;
60+
});
61+
62+
var thunks = endpoints.Select((endpoint, _) => $@" [{StaticRouteHandlerModelEmitter.EmitSourceKey(endpoint)}] = (
63+
(del, builder) =>
64+
{{
65+
builder.Metadata.Add(new SourceKey{StaticRouteHandlerModelEmitter.EmitSourceKey(endpoint)});
66+
}},
67+
(del, builder) =>
68+
{{
69+
var handler = ({StaticRouteHandlerModelEmitter.EmitHandlerDelegateType(endpoint)})del;
70+
EndpointFilterDelegate? filteredInvocation = null;
71+
72+
if (builder.FilterFactories.Count > 0)
73+
{{
74+
filteredInvocation = BuildFilterDelegate(ic =>
75+
{{
76+
if (ic.HttpContext.Response.StatusCode == 400)
77+
{{
78+
return System.Threading.Tasks.ValueTask.FromResult<object?>(Results.Empty);
79+
}}
80+
{StaticRouteHandlerModelEmitter.EmitFilteredInvocation()}
81+
}},
82+
builder,
83+
handler.Method);
84+
}}
85+
86+
{StaticRouteHandlerModelEmitter.EmitRequestHandler()}
87+
{StaticRouteHandlerModelEmitter.EmitFilteredRequestHandler()}
88+
89+
return filteredInvocation is null ? RequestHandler : RequestHandlerFiltered;
90+
}}),
91+
");
92+
93+
var stronglyTypedEndpointDefinitions = endpoints.Select((endpoint, _) =>
94+
{
95+
var code = new StringBuilder();
96+
code.AppendLine($"internal static Microsoft.AspNetCore.Builder.RouteHandlerBuilder {endpoint.HttpMethod}");
97+
code.Append("(this Microsoft.AspNetCore.Routing.IEndpointRouteBuilder endpoints, string pattern, ");
98+
code.Append(StaticRouteHandlerModelEmitter.EmitHandlerDelegateType(endpoint));
99+
code.Append(@" handler, [System.Runtime.CompilerServices.CallerFilePath] string filePath = """", [System.Runtime.CompilerServices.CallerLineNumber]int lineNumber = 0)");
100+
code.AppendLine("{");
101+
code.AppendLine("return MapCore(endpoints, pattern, handler, GetVerb, filePath, lineNumber);");
102+
code.AppendLine("}");
103+
return code.ToString();
104+
});
105+
106+
context.RegisterSourceOutput(genericThunks.Collect(), (context, sources) =>
107+
{
108+
var code = new StringBuilder();
109+
foreach (var source in sources)
110+
{
111+
code.AppendLine(source);
112+
}
113+
context.AddSource("GeneratedRouteBuilderExtensions.GenericThunks.g.cs", code.ToString());
114+
});
115+
116+
context.RegisterSourceOutput(thunks.Collect(), (context, sources) =>
117+
{
118+
var thunks = new StringBuilder();
119+
foreach (var source in sources)
120+
{
121+
thunks.AppendLine(source);
122+
}
123+
var code = RequestDelegateGeneratorSources.GetThunks(thunks.ToString());
124+
context.AddSource("GeneratedRouteBuilderExtensions.Thunks.g.cs", code);
125+
});
126+
127+
context.RegisterSourceOutput(stronglyTypedEndpointDefinitions.Collect(), (context, sources) =>
128+
{
129+
var endpoints = new StringBuilder();
130+
foreach (var source in sources)
131+
{
132+
endpoints.AppendLine(source);
133+
}
134+
var code = RequestDelegateGeneratorSources.GetEndpoints(endpoints.ToString());
135+
context.AddSource("GeneratedRouteBuilderExtensions.Endpoints.g.cs", code);
136+
});
137+
138+
context.RegisterSourceOutput(isGeneratorEnabled, (context, isGeneratorEnabled) =>
139+
{
140+
if (isGeneratorEnabled)
141+
{
142+
context.AddSource("GeneratedRouteBuilderExtensions.Helpers.g.cs", RequestDelegateGeneratorSources.GeneratedRouteBuilderExtensionsSource);
143+
}
144+
});
145+
}
146+
}

0 commit comments

Comments
 (0)