-
Notifications
You must be signed in to change notification settings - Fork 5.1k
Use Roslyn interceptors feature in binder gen #90340
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,6 +2,7 @@ | |
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Collections.Immutable; | ||
using System.Diagnostics; | ||
using System.Text.RegularExpressions; | ||
using Microsoft.CodeAnalysis; | ||
|
@@ -17,7 +18,6 @@ private sealed partial class Emitter | |
private readonly SourceGenerationSpec _sourceGenSpec; | ||
|
||
private bool _emitBlankLineBeforeNextStatement; | ||
private bool _useFullyQualifiedNames; | ||
private int _valueSuffixIndex; | ||
|
||
private static readonly Regex s_arrayBracketsRegex = new(Regex.Escape("[]")); | ||
|
@@ -32,7 +32,7 @@ public Emitter(SourceProductionContext context, SourceGenerationSpec sourceGenSp | |
|
||
public void Emit() | ||
{ | ||
if (!ShouldEmitBinders()) | ||
if (!ShouldEmitBindingExtensions()) | ||
{ | ||
return; | ||
} | ||
|
@@ -42,17 +42,26 @@ public void Emit() | |
#nullable enable | ||
#pragma warning disable CS0612, CS0618 // Suppress warnings about [Obsolete] member usage in generated code. | ||
"""); | ||
_writer.WriteLine(); | ||
|
||
_useFullyQualifiedNames = true; | ||
EmitBinder_Extensions_IConfiguration(); | ||
EmitBinder_Extensions_OptionsBuilder(); | ||
EmitBinder_Extensions_IServiceCollection(); | ||
EmitInterceptsLocationAttrDecl(); | ||
|
||
EmitStartBlock($"namespace {ProjectName}"); | ||
EmitUsingStatements(); | ||
|
||
_writer.WriteLine(); | ||
EmitStartBlock($$""" | ||
{{Expression.GeneratedCodeAnnotation}} | ||
file static class {{Identifier.BindingExtensions}} | ||
"""); | ||
EmitBindingExtensions_IConfiguration(); | ||
EmitBindingExtensions_OptionsBuilder(); | ||
EmitBindingExtensions_IServiceCollection(); | ||
EmitCoreBindingHelpers(); | ||
EmitEndBlock(); // BindingExtensions class | ||
|
||
_useFullyQualifiedNames = false; | ||
Emit_CoreBindingHelper(); | ||
EmitEndBlock(); // Binding namespace. | ||
|
||
_context.AddSource($"{Identifier.GeneratedConfigurationBinder}.g.cs", _writer.ToSourceText()); | ||
_context.AddSource($"{Identifier.BindingExtensions}.g.cs", _writer.ToSourceText()); | ||
} | ||
|
||
private void EmitBindCoreCall( | ||
|
@@ -74,7 +83,7 @@ private void EmitBindCoreCall( | |
if (initKind is InitializationKind.AssignmentWithNullCheck) | ||
{ | ||
Debug.Assert(!type.IsValueType); | ||
_writer.WriteLine($"{type.MinimalDisplayString}? {tempIdentifier} = {memberAccessExpr};"); | ||
_writer.WriteLine($"{type.DisplayString}? {tempIdentifier} = {memberAccessExpr};"); | ||
EmitBindCoreCall(tempIdentifier, InitializationKind.AssignmentWithNullCheck); | ||
} | ||
else if (initKind is InitializationKind.None && type.IsValueType) | ||
|
@@ -89,9 +98,7 @@ private void EmitBindCoreCall( | |
|
||
void EmitBindCoreCall(string objExpression, InitializationKind initKind) | ||
{ | ||
string methodDisplayString = GetHelperMethodDisplayString(nameof(MethodsToGen_CoreBindingHelper.BindCore)); | ||
string bindCoreCall = $@"{methodDisplayString}({configArgExpr}, ref {objExpression}, {Identifier.binderOptions});"; | ||
|
||
string bindCoreCall = $@"{nameof(MethodsToGen_CoreBindingHelper.BindCore)}({configArgExpr}, ref {objExpression}, {Identifier.binderOptions});"; | ||
EmitObjectInit(objExpression, initKind); | ||
_writer.WriteLine(bindCoreCall); | ||
writeOnSuccess?.Invoke(objExpression); | ||
|
@@ -127,12 +134,11 @@ private void EmitBindLogicFromString( | |
} | ||
else if (typeKind is StringParsableTypeKind.Enum) | ||
{ | ||
parsedValueExpr = $"ParseEnum<{type.MinimalDisplayString}>({stringValueToParse_Expr}, () => {sectionPathExpr})"; | ||
parsedValueExpr = $"ParseEnum<{type.DisplayString}>({stringValueToParse_Expr}, () => {sectionPathExpr})"; | ||
} | ||
else | ||
{ | ||
string helperMethodDisplayString = GetHelperMethodDisplayString(type.ParseMethodName); | ||
parsedValueExpr = $"{helperMethodDisplayString}({stringValueToParse_Expr}, () => {sectionPathExpr})"; | ||
parsedValueExpr = $"{type.ParseMethodName}({stringValueToParse_Expr}, () => {sectionPathExpr})"; | ||
} | ||
|
||
if (!checkForNullSectionValue) | ||
|
@@ -156,7 +162,7 @@ private bool EmitObjectInit(TypeSpec type, string memberAccessExpr, Initializati | |
string initExpr; | ||
CollectionSpec? collectionType = type as CollectionSpec; | ||
|
||
string effectiveDisplayString = GetTypeDisplayString(type); | ||
string effectiveDisplayString = type.DisplayString; | ||
if (collectionType is not null) | ||
{ | ||
if (collectionType is EnumerableSpec { InitializationStrategy: InitializationStrategy.Array }) | ||
|
@@ -165,7 +171,7 @@ private bool EmitObjectInit(TypeSpec type, string memberAccessExpr, Initializati | |
} | ||
else | ||
{ | ||
effectiveDisplayString = GetTypeDisplayString(collectionType.ConcreteType ?? collectionType); | ||
effectiveDisplayString = (collectionType.ConcreteType ?? collectionType).DisplayString; | ||
initExpr = $"new {effectiveDisplayString}()"; | ||
} | ||
} | ||
|
@@ -215,36 +221,41 @@ private bool EmitObjectInit(TypeSpec type, string memberAccessExpr, Initializati | |
return true; | ||
} | ||
|
||
private void EmitCastToIConfigurationSection() | ||
private void EmitInterceptsLocationAttrDecl() | ||
{ | ||
string sectionTypeDisplayString; | ||
string exceptionTypeDisplayString; | ||
if (_useFullyQualifiedNames) | ||
{ | ||
sectionTypeDisplayString = "global::Microsoft.Extensions.Configuration.IConfigurationSection"; | ||
exceptionTypeDisplayString = FullyQualifiedDisplayString.InvalidOperationException; | ||
} | ||
else | ||
{ | ||
sectionTypeDisplayString = Identifier.IConfigurationSection; | ||
exceptionTypeDisplayString = nameof(InvalidOperationException); | ||
} | ||
|
||
_writer.WriteLine(); | ||
_writer.WriteLine($$""" | ||
if ({{Identifier.configuration}} is not {{sectionTypeDisplayString}} {{Identifier.section}}) | ||
namespace System.Runtime.CompilerServices | ||
{ | ||
throw new {{exceptionTypeDisplayString}}(); | ||
using System; | ||
using System.CodeDom.Compiler; | ||
|
||
{{Expression.GeneratedCodeAnnotation}} | ||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] | ||
file sealed class InterceptsLocationAttribute : Attribute | ||
{ | ||
public InterceptsLocationAttribute(string filePath, int line, int column) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Did we want to add something like https://github.com/dotnet/sdk/blob/4249701de6f005c670c7f371141e772d02f89156/src/WebSdk/ProjectSystem/Targets/Microsoft.NET.Sdk.Web.ProjectSystem.targets#L64 to our NuGet package targets, or will we ask users to set this? @eerhardt @tarekgh @captainsafia There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we decided to mimic whatever the aspnet is doing in the web project. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ASP.NET doesn't ship a nuget package so we're special here. There's an ongoing discussion around this to decide what we want to do. |
||
{ | ||
} | ||
} | ||
} | ||
"""); | ||
_writer.WriteLine(); | ||
} | ||
|
||
private void EmitUsingStatements() | ||
{ | ||
foreach (string @namespace in _sourceGenSpec.Namespaces.ToImmutableSortedSet()) | ||
{ | ||
_writer.WriteLine($"using {@namespace};"); | ||
} | ||
} | ||
|
||
private void EmitIConfigurationHasValueOrChildrenCheck(bool voidReturn) | ||
{ | ||
string returnPostfix = voidReturn ? string.Empty : " null"; | ||
string methodDisplayString = GetHelperMethodDisplayString(Identifier.HasValueOrChildren); | ||
|
||
_writer.WriteLine($$""" | ||
if (!{{methodDisplayString}}({{Identifier.configuration}})) | ||
if (!{{Identifier.HasValueOrChildren}}({{Identifier.configuration}})) | ||
{ | ||
return{{returnPostfix}}; | ||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.