From c937f49a46fc2326b6c7fda3e8368e209003c0c9 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 10:31:26 +0000 Subject: [PATCH 1/7] Initial plan From 8c90944c2139b830a334af9353788ccdb108140e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 10:44:05 +0000 Subject: [PATCH 2/7] Support generic logging methods; rename LoggingMethodIsGeneric to LoggingMethodHasAllowsRefStructConstraint Co-authored-by: svick <287848+svick@users.noreply.github.com> --- global.json.bak | 26 +++++++ .../Emission/Emitter.Method.cs | 8 ++- .../Emission/Emitter.Utils.cs | 37 ++++++++++ .../Model/LoggingMethod.cs | 1 + .../Model/LoggingMethodTypeParameter.cs | 13 ++++ .../Parsing/DiagDescriptors.cs | 6 +- .../Microsoft.Gen.Logging/Parsing/Parser.cs | 67 +++++++++++++++++-- .../Parsing/Resources.Designer.cs | 12 ++-- .../Parsing/Resources.resx | 8 +-- .../TestClasses/GenericTestExtensions.cs | 31 +++++++++ .../Unit/ParserTests.LogMethod.cs | 37 +++++++++- 11 files changed, 224 insertions(+), 22 deletions(-) create mode 100644 global.json.bak create mode 100644 src/Generators/Microsoft.Gen.Logging/Model/LoggingMethodTypeParameter.cs create mode 100644 test/Generators/Microsoft.Gen.Logging/TestClasses/GenericTestExtensions.cs diff --git a/global.json.bak b/global.json.bak new file mode 100644 index 00000000000..857f942434e --- /dev/null +++ b/global.json.bak @@ -0,0 +1,26 @@ +{ + "sdk": { + "version": "10.0.103" + }, + "tools": { + "dotnet": "10.0.103", + "runtimes": { + "dotnet": [ + "8.0.0", + "9.0.0", + "10.0.0" + ], + "aspnetcore": [ + "8.0.0", + "9.0.0", + "10.0.0" + ] + } + }, + "msbuild-sdks": { + "Microsoft.Build.NoTargets": "3.7.0", + "Microsoft.Build.Traversal": "3.2.0", + "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.26110.2", + "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.26110.2" + } +} diff --git a/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Method.cs b/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Method.cs index de0252657b7..08b61af515b 100644 --- a/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Method.cs +++ b/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Method.cs @@ -49,9 +49,13 @@ private void GenLogMethod(LoggingMethod lm) OutGeneratedCodeAttribute(); OutIndent(); - Out($"{lm.Modifiers} void {lm.Name}({extension}"); + Out($"{lm.Modifiers} void {lm.Name}"); + GenTypeParameterList(lm); + Out($"({extension}"); GenParameters(lm); - Out(")\n"); + Out(")"); + GenTypeConstraints(lm); + Out("\n"); OutOpenBrace(); diff --git a/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Utils.cs b/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Utils.cs index c550aded815..69a965ce7fb 100644 --- a/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Utils.cs +++ b/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Utils.cs @@ -131,4 +131,41 @@ internal static string PickUniqueName(string baseName, IEnumerable poten #pragma warning restore S1643 // Strings should not be concatenated using '+' in a loop } } + + private void GenTypeParameterList(LoggingMethod lm) + { + if (lm.TypeParameters.Count == 0) + { + return; + } + + bool firstItem = true; + Out("<"); + foreach (var tp in lm.TypeParameters) + { + if (firstItem) + { + firstItem = false; + } + else + { + Out(", "); + } + + Out(tp.Name); + } + + Out(">"); + } + + private void GenTypeConstraints(LoggingMethod lm) + { + foreach (var tp in lm.TypeParameters) + { + if (tp.Constraints is not null) + { + Out($"\n where {tp.Name} : {tp.Constraints}"); + } + } + } } diff --git a/src/Generators/Microsoft.Gen.Logging/Model/LoggingMethod.cs b/src/Generators/Microsoft.Gen.Logging/Model/LoggingMethod.cs index f22f1524f50..076bdb9673d 100644 --- a/src/Generators/Microsoft.Gen.Logging/Model/LoggingMethod.cs +++ b/src/Generators/Microsoft.Gen.Logging/Model/LoggingMethod.cs @@ -16,6 +16,7 @@ internal sealed class LoggingMethod { public readonly List Parameters = []; public readonly List Templates = []; + public readonly List TypeParameters = []; public string Name = string.Empty; public string Message = string.Empty; public int? Level; diff --git a/src/Generators/Microsoft.Gen.Logging/Model/LoggingMethodTypeParameter.cs b/src/Generators/Microsoft.Gen.Logging/Model/LoggingMethodTypeParameter.cs new file mode 100644 index 00000000000..0705baa69a2 --- /dev/null +++ b/src/Generators/Microsoft.Gen.Logging/Model/LoggingMethodTypeParameter.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +namespace Microsoft.Gen.Logging.Model; + +/// +/// A type parameter of a generic logging method. +/// +internal sealed class LoggingMethodTypeParameter +{ + public string Name = string.Empty; + public string? Constraints; +} diff --git a/src/Generators/Microsoft.Gen.Logging/Parsing/DiagDescriptors.cs b/src/Generators/Microsoft.Gen.Logging/Parsing/DiagDescriptors.cs index 1763581307b..b04611c7262 100644 --- a/src/Generators/Microsoft.Gen.Logging/Parsing/DiagDescriptors.cs +++ b/src/Generators/Microsoft.Gen.Logging/Parsing/DiagDescriptors.cs @@ -56,10 +56,10 @@ internal sealed class DiagDescriptors : DiagDescriptorsBase messageFormat: Resources.LoggingMethodMustBePartialMessage, category: Category); - public static DiagnosticDescriptor LoggingMethodIsGeneric { get; } = Make( + public static DiagnosticDescriptor LoggingMethodHasAllowsRefStructConstraint { get; } = Make( id: DiagnosticIds.LoggerMessage.LOGGEN007, - title: Resources.LoggingMethodIsGenericTitle, - messageFormat: Resources.LoggingMethodIsGenericMessage, + title: Resources.LoggingMethodHasAllowsRefStructConstraintTitle, + messageFormat: Resources.LoggingMethodHasAllowsRefStructConstraintMessage, category: Category); public static DiagnosticDescriptor RedundantQualifierInMessage { get; } = Make( diff --git a/src/Generators/Microsoft.Gen.Logging/Parsing/Parser.cs b/src/Generators/Microsoft.Gen.Logging/Parsing/Parser.cs index 94996b7b1b3..18064fef996 100644 --- a/src/Generators/Microsoft.Gen.Logging/Parsing/Parser.cs +++ b/src/Generators/Microsoft.Gen.Logging/Parsing/Parser.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Reflection; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -16,6 +17,14 @@ namespace Microsoft.Gen.Logging.Parsing; internal sealed partial class Parser { + // ITypeParameterSymbol.AllowsRefLikeType was added in Roslyn 4.9 (C# 13). Access via a compiled + // delegate so the same source file compiles against all supported Roslyn versions, while + // avoiding the per-call overhead of PropertyInfo.GetValue boxing. + private static readonly Func? _getAllowsRefLikeType = + (Func?)typeof(ITypeParameterSymbol) + .GetProperty("AllowsRefLikeType")?.GetGetMethod()! + .CreateDelegate(typeof(Func)); + private readonly CancellationToken _cancellationToken; private readonly Compilation _compilation; private readonly Action _reportDiagnostic; @@ -398,11 +407,22 @@ static bool IsAllowedKind(SyntaxKind kind) => keepMethod = false; } - if (method.Arity > 0) + foreach (var tp in methodSymbol.TypeParameters) { - // we don't currently support generic methods - Diag(DiagDescriptors.LoggingMethodIsGeneric, method.TypeParameterList!.GetLocation()); - keepMethod = false; + if (_getAllowsRefLikeType?.Invoke(tp) == true) + { + // 'allows ref struct' anti-constraint is not supported because the generated code stores + // parameters in fields and cannot hold ref struct type arguments. + Diag(DiagDescriptors.LoggingMethodHasAllowsRefStructConstraint, method.Identifier.GetLocation()); + keepMethod = false; + break; + } + + lm.TypeParameters.Add(new LoggingMethodTypeParameter + { + Name = tp.Name, + Constraints = GetTypeParameterConstraints(tp), + }); } bool isPartial = methodSymbol.IsPartialDefinition; @@ -466,6 +486,45 @@ private static bool HasXmlDocumentation(MethodDeclarationSyntax method) return false; } + private static string? GetTypeParameterConstraints(ITypeParameterSymbol typeParameter) + { + var constraints = new List(); + + if (typeParameter.HasReferenceTypeConstraint) + { + string classConstraint = typeParameter.ReferenceTypeConstraintNullableAnnotation == NullableAnnotation.Annotated ? "class?" : "class"; + constraints.Add(classConstraint); + } + else if (typeParameter.HasValueTypeConstraint) + { + // HasUnmanagedTypeConstraint also implies HasValueTypeConstraint + constraints.Add(typeParameter.HasUnmanagedTypeConstraint ? "unmanaged" : "struct"); + } + else if (typeParameter.HasNotNullConstraint) + { + constraints.Add("notnull"); + } + + foreach (var constraintType in typeParameter.ConstraintTypes) + { + if (constraintType is IErrorTypeSymbol) + { + continue; + } + + constraints.Add(constraintType.ToDisplayString( + SymbolDisplayFormat.FullyQualifiedFormat.WithMiscellaneousOptions( + SymbolDisplayFormat.FullyQualifiedFormat.MiscellaneousOptions | SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier))); + } + + if (typeParameter.HasConstructorConstraint) + { + constraints.Add("new()"); + } + + return constraints.Count > 0 ? string.Join(", ", constraints) : null; + } + // Returns all the classification attributes attached to a symbol. private static List GetDataClassificationAttributes(ISymbol symbol, SymbolHolder symbols) => symbol diff --git a/src/Generators/Microsoft.Gen.Logging/Parsing/Resources.Designer.cs b/src/Generators/Microsoft.Gen.Logging/Parsing/Resources.Designer.cs index 34d4e341491..4a06180f0df 100644 --- a/src/Generators/Microsoft.Gen.Logging/Parsing/Resources.Designer.cs +++ b/src/Generators/Microsoft.Gen.Logging/Parsing/Resources.Designer.cs @@ -187,20 +187,20 @@ internal static string LoggingMethodHasBodyTitle { } /// - /// Looks up a localized string similar to Logging methods can't be generic. + /// Looks up a localized string similar to Logging methods can't use the 'allows ref struct' constraint. /// - internal static string LoggingMethodIsGenericMessage { + internal static string LoggingMethodHasAllowsRefStructConstraintMessage { get { - return ResourceManager.GetString("LoggingMethodIsGenericMessage", resourceCulture); + return ResourceManager.GetString("LoggingMethodHasAllowsRefStructConstraintMessage", resourceCulture); } } /// - /// Looks up a localized string similar to Logging methods can't be generic. + /// Looks up a localized string similar to Logging methods can't use the 'allows ref struct' constraint. /// - internal static string LoggingMethodIsGenericTitle { + internal static string LoggingMethodHasAllowsRefStructConstraintTitle { get { - return ResourceManager.GetString("LoggingMethodIsGenericTitle", resourceCulture); + return ResourceManager.GetString("LoggingMethodHasAllowsRefStructConstraintTitle", resourceCulture); } } diff --git a/src/Generators/Microsoft.Gen.Logging/Parsing/Resources.resx b/src/Generators/Microsoft.Gen.Logging/Parsing/Resources.resx index 2ce7a16851a..104c6860f95 100644 --- a/src/Generators/Microsoft.Gen.Logging/Parsing/Resources.resx +++ b/src/Generators/Microsoft.Gen.Logging/Parsing/Resources.resx @@ -153,11 +153,11 @@ Logging methods must be partial - - Logging methods can't be generic + + Logging methods can't use the 'allows ref struct' constraint - - Logging methods can't be generic + + Logging methods can't use the 'allows ref struct' constraint Don't include a template for parameter "{0}" in the logging message, exceptions are automatically delivered without being listed in the logging message diff --git a/test/Generators/Microsoft.Gen.Logging/TestClasses/GenericTestExtensions.cs b/test/Generators/Microsoft.Gen.Logging/TestClasses/GenericTestExtensions.cs new file mode 100644 index 00000000000..f1a30a73ad0 --- /dev/null +++ b/test/Generators/Microsoft.Gen.Logging/TestClasses/GenericTestExtensions.cs @@ -0,0 +1,31 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using Microsoft.Extensions.Logging; + +namespace TestClasses +{ + internal static partial class GenericTestExtensions + { + // generic method with single type parameter + [LoggerMessage(0, LogLevel.Debug, "M1 {value}")] + internal static partial void M1(ILogger logger, T value); + + // generic method with struct+Enum constraint + [LoggerMessage(1, LogLevel.Debug, "M2 {code}")] + internal static partial void M2(ILogger logger, TCode code) + where TCode : struct, Enum; + + // generic method with multiple type parameters + [LoggerMessage(2, LogLevel.Debug, "M3 {p1} {p2}")] + internal static partial void M3(ILogger logger, T1 p1, T2 p2) + where T1 : class + where T2 : notnull; + + // generic method with new() constraint + [LoggerMessage(3, LogLevel.Debug, "M4 {value}")] + internal static partial void M4(ILogger logger, T value) + where T : new(); + } +} diff --git a/test/Generators/Microsoft.Gen.Logging/Unit/ParserTests.LogMethod.cs b/test/Generators/Microsoft.Gen.Logging/Unit/ParserTests.LogMethod.cs index 9cd3c8fd252..2e38c49dce1 100644 --- a/test/Generators/Microsoft.Gen.Logging/Unit/ParserTests.LogMethod.cs +++ b/test/Generators/Microsoft.Gen.Logging/Unit/ParserTests.LogMethod.cs @@ -276,12 +276,43 @@ public async Task MethodGeneric() const string Source = @" partial class C { - [LoggerMessage(0, LogLevel.Debug, ""M1"")] - static partial void M1/*0+*//*-0*/(ILogger logger); + [LoggerMessage(0, LogLevel.Debug, ""M1 {value}"")] + static partial void M1(ILogger logger, T value); + } + "; + + await RunGenerator(Source); + } + + [Fact] + public async Task MethodGenericWithConstraints() + { + const string Source = @" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1 {code}"")] + static partial void M1(ILogger logger, TCode code) + where TCode : struct, System.Enum; } "; - await RunGenerator(Source, DiagDescriptors.LoggingMethodIsGeneric); + await RunGenerator(Source); + } + + [Fact] + public async Task MethodGenericMultipleTypeParams() + { + const string Source = @" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1 {p1} {p2}"")] + static partial void M1(ILogger logger, T1 p1, T2 p2) + where T1 : class + where T2 : notnull; + } + "; + + await RunGenerator(Source); } [Theory] From 4dd832a8db503f06b214605261b0fb8ad656d632 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 10:44:15 +0000 Subject: [PATCH 3/7] Remove accidentally committed global.json.bak Co-authored-by: svick <287848+svick@users.noreply.github.com> --- global.json.bak | 26 -------------------------- 1 file changed, 26 deletions(-) delete mode 100644 global.json.bak diff --git a/global.json.bak b/global.json.bak deleted file mode 100644 index 857f942434e..00000000000 --- a/global.json.bak +++ /dev/null @@ -1,26 +0,0 @@ -{ - "sdk": { - "version": "10.0.103" - }, - "tools": { - "dotnet": "10.0.103", - "runtimes": { - "dotnet": [ - "8.0.0", - "9.0.0", - "10.0.0" - ], - "aspnetcore": [ - "8.0.0", - "9.0.0", - "10.0.0" - ] - } - }, - "msbuild-sdks": { - "Microsoft.Build.NoTargets": "3.7.0", - "Microsoft.Build.Traversal": "3.2.0", - "Microsoft.DotNet.Arcade.Sdk": "9.0.0-beta.26110.2", - "Microsoft.DotNet.Helix.Sdk": "9.0.0-beta.26110.2" - } -} From 6789fe5ec15dd00e06bde437424d23e7549a70dc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 12:52:06 +0000 Subject: [PATCH 4/7] Use char overload and OutLn in new GenTypeParameterList/GenTypeConstraints code Co-authored-by: svick <287848+svick@users.noreply.github.com> --- .../Microsoft.Gen.Logging/Emission/Emitter.Method.cs | 4 ++-- .../Microsoft.Gen.Logging/Emission/Emitter.Utils.cs | 7 ++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Method.cs b/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Method.cs index 08b61af515b..6686888f86e 100644 --- a/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Method.cs +++ b/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Method.cs @@ -53,9 +53,9 @@ private void GenLogMethod(LoggingMethod lm) GenTypeParameterList(lm); Out($"({extension}"); GenParameters(lm); - Out(")"); + Out(')'); GenTypeConstraints(lm); - Out("\n"); + OutLn(); OutOpenBrace(); diff --git a/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Utils.cs b/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Utils.cs index 69a965ce7fb..6bd3d5d48f7 100644 --- a/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Utils.cs +++ b/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Utils.cs @@ -140,7 +140,7 @@ private void GenTypeParameterList(LoggingMethod lm) } bool firstItem = true; - Out("<"); + Out('<'); foreach (var tp in lm.TypeParameters) { if (firstItem) @@ -155,7 +155,7 @@ private void GenTypeParameterList(LoggingMethod lm) Out(tp.Name); } - Out(">"); + Out('>'); } private void GenTypeConstraints(LoggingMethod lm) @@ -164,7 +164,8 @@ private void GenTypeConstraints(LoggingMethod lm) { if (tp.Constraints is not null) { - Out($"\n where {tp.Name} : {tp.Constraints}"); + OutLn(); + Out($" where {tp.Name} : {tp.Constraints}"); } } } From 31501cb693e33e7035a309a6ef93e7be75fd3c8b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 12:58:02 +0000 Subject: [PATCH 5/7] Add test for allows ref struct constraint (MethodGenericWithAllowsRefStructConstraint) Co-authored-by: svick <287848+svick@users.noreply.github.com> --- .../Unit/ParserTests.LogMethod.cs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/test/Generators/Microsoft.Gen.Logging/Unit/ParserTests.LogMethod.cs b/test/Generators/Microsoft.Gen.Logging/Unit/ParserTests.LogMethod.cs index 2e38c49dce1..820ffe3a9d8 100644 --- a/test/Generators/Microsoft.Gen.Logging/Unit/ParserTests.LogMethod.cs +++ b/test/Generators/Microsoft.Gen.Logging/Unit/ParserTests.LogMethod.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Threading.Tasks; +using Microsoft.CodeAnalysis; using Microsoft.Gen.Logging.Parsing; using Xunit; @@ -315,6 +316,27 @@ static partial void M1(ILogger logger, T1 p1, T2 p2) await RunGenerator(Source); } + [Fact] + public async Task MethodGenericWithAllowsRefStructConstraint() + { + // The 'allows ref struct' detection requires Roslyn 4.9+ (ITypeParameterSymbol.AllowsRefLikeType). + // Skip gracefully on older Roslyn versions where the property and syntax are unavailable. + if (typeof(ITypeParameterSymbol).GetProperty("AllowsRefLikeType") is null) + { + return; + } + + const string Source = @" + partial class C + { + [LoggerMessage(0, LogLevel.Debug, ""M1"")] + static partial void /*0+*/M1/*-0*/(ILogger logger) where T : allows ref struct; + } + "; + + await RunGenerator(Source, DiagDescriptors.LoggingMethodHasAllowsRefStructConstraint); + } + [Theory] [CombinatorialData] public async Task LogMethodParamsRefKind([CombinatorialValues("ref", "out")] string modifier) From 14cb00bcec1409dfdc33cdda777f15498908ba40 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 15:28:28 +0000 Subject: [PATCH 6/7] Update LOGGEN007 description in docs/list-of-diagnostics.md Co-authored-by: svick <287848+svick@users.noreply.github.com> --- docs/list-of-diagnostics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/list-of-diagnostics.md b/docs/list-of-diagnostics.md index e2176891d66..8416aa5bea7 100644 --- a/docs/list-of-diagnostics.md +++ b/docs/list-of-diagnostics.md @@ -65,7 +65,7 @@ You may continue using obsolete APIs in your application, but we advise explorin | `LOGGEN004` | A static logging method must have a parameter that implements the "Microsoft.Extensions.Logging.ILogger" interface | | `LOGGEN005` | Logging methods must be static | | `LOGGEN006` | Logging methods must be partial | -| `LOGGEN007` | Logging methods can't be generic | +| `LOGGEN007` | Logging methods can't use the `allows ref struct` constraint | | `LOGGEN008` | Redundant qualifier in the logging message | | `LOGGEN009` | Don't include exception parameters as templates in the logging message | | `LOGGEN010` | The logging template has no corresponding method parameter | From 8ad114f63c375c5080d41ea725c82807fab030e5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 15:40:15 +0000 Subject: [PATCH 7/7] Use Indent/OutIndent/Unindent in GenTypeConstraints for proper where-clause alignment Co-authored-by: svick <287848+svick@users.noreply.github.com> --- .../Microsoft.Gen.Logging/Emission/Emitter.Utils.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Utils.cs b/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Utils.cs index 6bd3d5d48f7..b387093ac25 100644 --- a/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Utils.cs +++ b/src/Generators/Microsoft.Gen.Logging/Emission/Emitter.Utils.cs @@ -165,7 +165,10 @@ private void GenTypeConstraints(LoggingMethod lm) if (tp.Constraints is not null) { OutLn(); - Out($" where {tp.Name} : {tp.Constraints}"); + Indent(); + OutIndent(); + Out($"where {tp.Name} : {tp.Constraints}"); + Unindent(); } } }