DiagnosticSeverity.Error,
isEnabledByDefault: true);
- public static DiagnosticDescriptor LoggingMethodInNestedType { get; } = new DiagnosticDescriptor(
- id: "SYSLIB1004",
- title: new LocalizableResourceString(nameof(SR.LoggingMethodInNestedTypeMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
- messageFormat: new LocalizableResourceString(nameof(SR.LoggingMethodInNestedTypeMessage), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
- category: "LoggingGenerator",
- DiagnosticSeverity.Error,
- isEnabledByDefault: true);
-
public static DiagnosticDescriptor MissingRequiredType { get; } = new DiagnosticDescriptor(
id: "SYSLIB1005",
title: new LocalizableResourceString(nameof(SR.MissingRequiredTypeTitle), SR.ResourceManager, typeof(FxResources.Microsoft.Extensions.Logging.Generators.SR)),
private void GenType(LoggerClass lc)
{
+ string nestedIndentation = "";
if (!string.IsNullOrWhiteSpace(lc.Namespace))
{
_builder.Append($@"
{{");
}
+ LoggerClass parent = lc.ParentClass;
+ var parentClasses = new List<string>();
+ // loop until you find top level nested class
+ while (parent != null)
+ {
+ parentClasses.Add($"partial {parent.Keyword} {parent.Name} {parent.Constraints}");
+ parent = parent.ParentClass;
+ }
+
+ // write down top level nested class first
+ for (int i = parentClasses.Count - 1; i >= 0; i--)
+ {
+ _builder.Append($@"
+ {nestedIndentation}{parentClasses[i]}
+ {nestedIndentation}{{");
+ nestedIndentation += " ";
+ }
+
_builder.Append($@"
- partial class {lc.Name} {lc.Constraints}
- {{");
+ {nestedIndentation}partial {lc.Keyword} {lc.Name} {lc.Constraints}
+ {nestedIndentation}{{");
foreach (LoggerMethod lm in lc.Methods)
{
if (!UseLoggerMessageDefine(lm))
{
- GenStruct(lm);
+ GenStruct(lm, nestedIndentation);
}
- GenLogMethod(lm);
+ GenLogMethod(lm, nestedIndentation);
}
_builder.Append($@"
- }}");
+ {nestedIndentation}}}");
+
+ parent = lc.ParentClass;
+ while (parent != null)
+ {
+ nestedIndentation = new String(' ', nestedIndentation.Length - 4);
+ _builder.Append($@"
+ {nestedIndentation}}}");
+ parent = parent.ParentClass;
+ }
if (!string.IsNullOrWhiteSpace(lc.Namespace))
{
}
}
- private void GenStruct(LoggerMethod lm)
+ private void GenStruct(LoggerMethod lm, string nestedIndentation)
{
_builder.AppendLine($@"
- [{s_generatedCodeAttribute}]
- private readonly struct __{lm.Name}Struct : global::System.Collections.Generic.IReadOnlyList<global::System.Collections.Generic.KeyValuePair<string, object?>>
- {{");
- GenFields(lm);
+ {nestedIndentation}[{s_generatedCodeAttribute}]
+ {nestedIndentation}private readonly struct __{lm.Name}Struct : global::System.Collections.Generic.IReadOnlyList<global::System.Collections.Generic.KeyValuePair<string, object?>>
+ {nestedIndentation}{{");
+ GenFields(lm, nestedIndentation);
if (lm.TemplateParameters.Count > 0)
{
_builder.Append($@"
- public __{lm.Name}Struct(");
+ {nestedIndentation}public __{lm.Name}Struct(");
GenArguments(lm);
_builder.Append($@")
- {{");
+ {nestedIndentation}{{");
_builder.AppendLine();
- GenFieldAssignments(lm);
+ GenFieldAssignments(lm, nestedIndentation);
_builder.Append($@"
- }}
+ {nestedIndentation}}}
");
}
_builder.Append($@"
- public override string ToString()
- {{
+ {nestedIndentation}public override string ToString()
+ {nestedIndentation}{{
");
- GenVariableAssignments(lm);
+ GenVariableAssignments(lm, nestedIndentation);
_builder.Append($@"
- return $""{lm.Message}"";
- }}
+ {nestedIndentation}return $""{lm.Message}"";
+ {nestedIndentation}}}
");
_builder.Append($@"
- public static string Format(__{lm.Name}Struct state, global::System.Exception? ex) => state.ToString();
+ {nestedIndentation}public static string Format(__{lm.Name}Struct state, global::System.Exception? ex) => state.ToString();
- public int Count => {lm.TemplateParameters.Count + 1};
+ {nestedIndentation}public int Count => {lm.TemplateParameters.Count + 1};
- public global::System.Collections.Generic.KeyValuePair<string, object?> this[int index]
- {{
- get => index switch
- {{
+ {nestedIndentation}public global::System.Collections.Generic.KeyValuePair<string, object?> this[int index]
+ {nestedIndentation}{{
+ {nestedIndentation}get => index switch
+ {nestedIndentation}{{
");
- GenCases(lm);
+ GenCases(lm, nestedIndentation);
_builder.Append($@"
- _ => throw new global::System.IndexOutOfRangeException(nameof(index)), // return the same exception LoggerMessage.Define returns in this case
- }};
+ {nestedIndentation}_ => throw new global::System.IndexOutOfRangeException(nameof(index)), // return the same exception LoggerMessage.Define returns in this case
+ {nestedIndentation}}};
}}
- public global::System.Collections.Generic.IEnumerator<global::System.Collections.Generic.KeyValuePair<string, object?>> GetEnumerator()
- {{
- for (int i = 0; i < {lm.TemplateParameters.Count + 1}; i++)
- {{
- yield return this[i];
- }}
- }}
+ {nestedIndentation}public global::System.Collections.Generic.IEnumerator<global::System.Collections.Generic.KeyValuePair<string, object?>> GetEnumerator()
+ {nestedIndentation}{{
+ {nestedIndentation}for (int i = 0; i < {lm.TemplateParameters.Count + 1}; i++)
+ {nestedIndentation}{{
+ {nestedIndentation}yield return this[i];
+ {nestedIndentation}}}
+ {nestedIndentation}}}
- global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
- }}
+ {nestedIndentation}global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator();
+ {nestedIndentation}}}
");
}
- private void GenFields(LoggerMethod lm)
+ private void GenFields(LoggerMethod lm, string nestedIndentation)
{
foreach (LoggerParameter p in lm.TemplateParameters)
{
- _builder.AppendLine($" private readonly {p.Type} _{p.Name};");
+ _builder.AppendLine($" {nestedIndentation}private readonly {p.Type} _{p.Name};");
}
}
- private void GenFieldAssignments(LoggerMethod lm)
+ private void GenFieldAssignments(LoggerMethod lm, string nestedIndentation)
{
foreach (LoggerParameter p in lm.TemplateParameters)
{
- _builder.AppendLine($" this._{p.Name} = {p.Name};");
+ _builder.AppendLine($" {nestedIndentation}this._{p.Name} = {p.Name};");
}
}
- private void GenVariableAssignments(LoggerMethod lm)
+ private void GenVariableAssignments(LoggerMethod lm, string nestedIndentation)
{
foreach (KeyValuePair<string, string> t in lm.TemplateMap)
{
{
if (lm.TemplateParameters[index].IsEnumerable)
{
- _builder.AppendLine($" var {t.Key} = "
+ _builder.AppendLine($" {nestedIndentation}var {t.Key} = "
+ $"global::__LoggerMessageGenerator.Enumerate((global::System.Collections.IEnumerable ?)this._{lm.TemplateParameters[index].Name});");
_needEnumerationHelper = true;
}
else
{
- _builder.AppendLine($" var {t.Key} = this._{lm.TemplateParameters[index].Name};");
+ _builder.AppendLine($" {nestedIndentation}var {t.Key} = this._{lm.TemplateParameters[index].Name};");
}
}
}
}
- private void GenCases(LoggerMethod lm)
+ private void GenCases(LoggerMethod lm, string nestedIndentation)
{
int index = 0;
foreach (LoggerParameter p in lm.TemplateParameters)
name = lm.TemplateMap[name];
}
- _builder.AppendLine($" {index++} => new global::System.Collections.Generic.KeyValuePair<string, object?>(\"{name}\", this._{p.Name}),");
+ _builder.AppendLine($" {nestedIndentation}{index++} => new global::System.Collections.Generic.KeyValuePair<string, object?>(\"{name}\", this._{p.Name}),");
}
- _builder.AppendLine($" {index++} => new global::System.Collections.Generic.KeyValuePair<string, object?>(\"{{OriginalFormat}}\", \"{ConvertEndOfLineAndQuotationCharactersToEscapeForm(lm.Message)}\"),");
+ _builder.AppendLine($" {nestedIndentation}{index++} => new global::System.Collections.Generic.KeyValuePair<string, object?>(\"{{OriginalFormat}}\", \"{ConvertEndOfLineAndQuotationCharactersToEscapeForm(lm.Message)}\"),");
}
private void GenCallbackArguments(LoggerMethod lm)
_builder.Append(')');
}
- private void GenLogMethod(LoggerMethod lm)
+ private void GenLogMethod(LoggerMethod lm, string nestedIndentation)
{
string level = GetLogLevel(lm);
string extension = lm.IsExtensionMethod ? "this " : string.Empty;
if (UseLoggerMessageDefine(lm))
{
_builder.Append($@"
- [{s_generatedCodeAttribute}]
- private static readonly global::System.Action<global::Microsoft.Extensions.Logging.ILogger, ");
+ {nestedIndentation}[{s_generatedCodeAttribute}]
+ {nestedIndentation}private static readonly global::System.Action<global::Microsoft.Extensions.Logging.ILogger, ");
GenDefineTypes(lm, brackets: false);
- _builder.Append(@$"global::System.Exception?> __{lm.Name}Callback =
- global::Microsoft.Extensions.Logging.LoggerMessage.Define");
+ _builder.Append($@"global::System.Exception?> __{lm.Name}Callback =
+ {nestedIndentation}global::Microsoft.Extensions.Logging.LoggerMessage.Define");
GenDefineTypes(lm, brackets: true);
}
_builder.Append($@"
- [{s_generatedCodeAttribute}]
- {lm.Modifiers} void {lm.Name}({extension}");
+ {nestedIndentation}[{s_generatedCodeAttribute}]
+ {nestedIndentation}{lm.Modifiers} void {lm.Name}({extension}");
GenParameters(lm);
_builder.Append($@")
- {{
- if ({logger}.IsEnabled({level}))
- {{");
+ {nestedIndentation}{{
+ {nestedIndentation}if ({logger}.IsEnabled({level}))
+ {nestedIndentation}{{");
if (UseLoggerMessageDefine(lm))
{
_builder.Append($@"
- __{lm.Name}Callback({logger}, ");
+ {nestedIndentation}__{lm.Name}Callback({logger}, ");
GenCallbackArguments(lm);
else
{
_builder.Append($@"
- {logger}.Log(
+ {nestedIndentation}{logger}.Log(
{level},
new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}),
");
}
_builder.Append($@"
- }}
- }}");
+ {nestedIndentation}}}
+ {nestedIndentation}}}");
static string GetException(LoggerMethod lm)
{
}
/// <summary>
- /// Gets the set of logging classes containing methods to output.
+ /// Gets the set of logging classes or structs containing methods to output.
/// </summary>
public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSyntax> classes)
{
if (lc == null)
{
// determine the namespace the class is declared in, if any
- var ns = classDec.Parent as NamespaceDeclarationSyntax;
- if (ns == null)
+ SyntaxNode? potentialNamespaceParent = classDec.Parent;
+ while (potentialNamespaceParent != null && potentialNamespaceParent is not NamespaceDeclarationSyntax)
{
- if (classDec.Parent is not CompilationUnitSyntax)
- {
- // since this generator doesn't know how to generate a nested type...
- Diag(DiagnosticDescriptors.LoggingMethodInNestedType, classDec.Identifier.GetLocation());
- keepMethod = false;
- }
+ potentialNamespaceParent = potentialNamespaceParent.Parent;
}
- else
+ if (potentialNamespaceParent is NamespaceDeclarationSyntax namespaceParent)
{
- nspace = ns.Name.ToString();
+ nspace = namespaceParent.Name.ToString();
while (true)
{
- ns = ns.Parent as NamespaceDeclarationSyntax;
- if (ns == null)
+ namespaceParent = namespaceParent.Parent as NamespaceDeclarationSyntax;
+ if (namespaceParent == null)
{
break;
}
- nspace = $"{ns.Name}.{nspace}";
+ nspace = $"{namespaceParent.Name}.{nspace}";
}
}
}
{
lc ??= new LoggerClass
{
+ Keyword = classDec.Keyword.ValueText,
Namespace = nspace,
Name = classDec.Identifier.ToString() + classDec.TypeParameterList,
Constraints = classDec.ConstraintClauses.ToString(),
+ ParentClass = null,
};
+ LoggerClass currentLoggerClass = lc;
+ var parentLoggerClass = (classDec.Parent as TypeDeclarationSyntax);
+
+ bool IsAllowedKind(SyntaxKind kind) =>
+ kind == SyntaxKind.ClassDeclaration ||
+ kind == SyntaxKind.StructDeclaration ||
+ kind == SyntaxKind.RecordDeclaration;
+
+ while (parentLoggerClass != null && IsAllowedKind(parentLoggerClass.Kind()))
+ {
+ currentLoggerClass.ParentClass = new LoggerClass
+ {
+ Keyword = parentLoggerClass.Keyword.ValueText,
+ Namespace = nspace,
+ Name = parentLoggerClass.Identifier.ToString() + parentLoggerClass.TypeParameterList,
+ Constraints = parentLoggerClass.ConstraintClauses.ToString(),
+ ParentClass = null,
+ };
+
+ currentLoggerClass = currentLoggerClass.ParentClass;
+ parentLoggerClass = (parentLoggerClass.Parent as TypeDeclarationSyntax);
+ }
+
lc.Methods.Add(lm);
}
}
internal class LoggerClass
{
public readonly List<LoggerMethod> Methods = new ();
+ public string Keyword = string.Empty;
public string Namespace = string.Empty;
public string Name = string.Empty;
public string Constraints = string.Empty;
+ public LoggerClass? ParentClass;
}
/// <summary>
<data name="InvalidLoggingMethodParameterNameMessage" xml:space="preserve">
<value>Logging method parameter names cannot start with _</value>
</data>
- <data name="LoggingMethodInNestedTypeMessage" xml:space="preserve">
- <value>Logging class cannot be in nested types</value>
- </data>
<data name="MissingRequiredTypeTitle" xml:space="preserve">
<value>Could not find a required type definition</value>
</data>
<target state="new">Logging methods cannot have a body</target>
<note />
</trans-unit>
- <trans-unit id="LoggingMethodInNestedTypeMessage">
- <source>Logging class cannot be in nested types</source>
- <target state="new">Logging class cannot be in nested types</target>
- <note />
- </trans-unit>
<trans-unit id="LoggingMethodIsGenericMessage">
<source>Logging methods cannot be generic</source>
<target state="new">Logging methods cannot be generic</target>
<target state="new">Logging methods cannot have a body</target>
<note />
</trans-unit>
- <trans-unit id="LoggingMethodInNestedTypeMessage">
- <source>Logging class cannot be in nested types</source>
- <target state="new">Logging class cannot be in nested types</target>
- <note />
- </trans-unit>
<trans-unit id="LoggingMethodIsGenericMessage">
<source>Logging methods cannot be generic</source>
<target state="new">Logging methods cannot be generic</target>
<target state="new">Logging methods cannot have a body</target>
<note />
</trans-unit>
- <trans-unit id="LoggingMethodInNestedTypeMessage">
- <source>Logging class cannot be in nested types</source>
- <target state="new">Logging class cannot be in nested types</target>
- <note />
- </trans-unit>
<trans-unit id="LoggingMethodIsGenericMessage">
<source>Logging methods cannot be generic</source>
<target state="new">Logging methods cannot be generic</target>
<target state="new">Logging methods cannot have a body</target>
<note />
</trans-unit>
- <trans-unit id="LoggingMethodInNestedTypeMessage">
- <source>Logging class cannot be in nested types</source>
- <target state="new">Logging class cannot be in nested types</target>
- <note />
- </trans-unit>
<trans-unit id="LoggingMethodIsGenericMessage">
<source>Logging methods cannot be generic</source>
<target state="new">Logging methods cannot be generic</target>
<target state="new">Logging methods cannot have a body</target>
<note />
</trans-unit>
- <trans-unit id="LoggingMethodInNestedTypeMessage">
- <source>Logging class cannot be in nested types</source>
- <target state="new">Logging class cannot be in nested types</target>
- <note />
- </trans-unit>
<trans-unit id="LoggingMethodIsGenericMessage">
<source>Logging methods cannot be generic</source>
<target state="new">Logging methods cannot be generic</target>
<target state="new">Logging methods cannot have a body</target>
<note />
</trans-unit>
- <trans-unit id="LoggingMethodInNestedTypeMessage">
- <source>Logging class cannot be in nested types</source>
- <target state="new">Logging class cannot be in nested types</target>
- <note />
- </trans-unit>
<trans-unit id="LoggingMethodIsGenericMessage">
<source>Logging methods cannot be generic</source>
<target state="new">Logging methods cannot be generic</target>
<target state="new">Logging methods cannot have a body</target>
<note />
</trans-unit>
- <trans-unit id="LoggingMethodInNestedTypeMessage">
- <source>Logging class cannot be in nested types</source>
- <target state="new">Logging class cannot be in nested types</target>
- <note />
- </trans-unit>
<trans-unit id="LoggingMethodIsGenericMessage">
<source>Logging methods cannot be generic</source>
<target state="new">Logging methods cannot be generic</target>
<target state="new">Logging methods cannot have a body</target>
<note />
</trans-unit>
- <trans-unit id="LoggingMethodInNestedTypeMessage">
- <source>Logging class cannot be in nested types</source>
- <target state="new">Logging class cannot be in nested types</target>
- <note />
- </trans-unit>
<trans-unit id="LoggingMethodIsGenericMessage">
<source>Logging methods cannot be generic</source>
<target state="new">Logging methods cannot be generic</target>
<target state="new">Logging methods cannot have a body</target>
<note />
</trans-unit>
- <trans-unit id="LoggingMethodInNestedTypeMessage">
- <source>Logging class cannot be in nested types</source>
- <target state="new">Logging class cannot be in nested types</target>
- <note />
- </trans-unit>
<trans-unit id="LoggingMethodIsGenericMessage">
<source>Logging methods cannot be generic</source>
<target state="new">Logging methods cannot be generic</target>
<target state="new">Logging methods cannot have a body</target>
<note />
</trans-unit>
- <trans-unit id="LoggingMethodInNestedTypeMessage">
- <source>Logging class cannot be in nested types</source>
- <target state="new">Logging class cannot be in nested types</target>
- <note />
- </trans-unit>
<trans-unit id="LoggingMethodIsGenericMessage">
<source>Logging methods cannot be generic</source>
<target state="new">Logging methods cannot be generic</target>
<target state="new">Logging methods cannot have a body</target>
<note />
</trans-unit>
- <trans-unit id="LoggingMethodInNestedTypeMessage">
- <source>Logging class cannot be in nested types</source>
- <target state="new">Logging class cannot be in nested types</target>
- <note />
- </trans-unit>
<trans-unit id="LoggingMethodIsGenericMessage">
<source>Logging methods cannot be generic</source>
<target state="new">Logging methods cannot be generic</target>
<target state="new">Logging methods cannot have a body</target>
<note />
</trans-unit>
- <trans-unit id="LoggingMethodInNestedTypeMessage">
- <source>Logging class cannot be in nested types</source>
- <target state="new">Logging class cannot be in nested types</target>
- <note />
- </trans-unit>
<trans-unit id="LoggingMethodIsGenericMessage">
<source>Logging methods cannot be generic</source>
<target state="new">Logging methods cannot be generic</target>
<target state="new">Logging methods cannot have a body</target>
<note />
</trans-unit>
- <trans-unit id="LoggingMethodInNestedTypeMessage">
- <source>Logging class cannot be in nested types</source>
- <target state="new">Logging class cannot be in nested types</target>
- <note />
- </trans-unit>
<trans-unit id="LoggingMethodIsGenericMessage">
<source>Logging methods cannot be generic</source>
<target state="new">Logging methods cannot be generic</target>
--- /dev/null
+// <auto-generated/>
+#nullable enable
+
+namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses.NestedNamespace
+{
+ partial class MultiLevelNestedClass
+ {
+ partial struct NestedStruct
+ {
+ partial record NestedRecord
+ {
+ partial class NestedClassTestsExtensions<T1> where T1 : Class1
+ {
+ partial class NestedMiddleParentClass
+ {
+ partial class Nested<T2> where T2 : Class2
+ {
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")]
+ private static readonly global::System.Action<global::Microsoft.Extensions.Logging.ILogger, global::System.Exception?> __M9Callback =
+ global::Microsoft.Extensions.Logging.LoggerMessage.Define(global::Microsoft.Extensions.Logging.LogLevel.Debug, new global::Microsoft.Extensions.Logging.EventId(9, nameof(M9)), "M9", true);
+
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extensions.Logging.Generators", "6.0.0.0")]
+ public static partial void M9(global::Microsoft.Extensions.Logging.ILogger logger)
+ {
+ if (logger.IsEnabled(global::Microsoft.Extensions.Logging.LogLevel.Debug))
+ {
+ __M9Callback(logger, null);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
}
[Fact]
+ public void NestedClassTests()
+ {
+ var logger = new MockLogger();
+
+ logger.Reset();
+ NestedClassTestsExtensions<ABC>.NestedMiddleParentClass.NestedClass.M8(logger);
+ Assert.Null(logger.LastException);
+ Assert.Equal("M8", logger.LastFormattedString);
+ Assert.Equal(LogLevel.Debug, logger.LastLogLevel);
+ Assert.Equal(1, logger.CallCount);
+
+ logger.Reset();
+ NonStaticNestedClassTestsExtensions<ABC>.NonStaticNestedMiddleParentClass.NestedClass.M9(logger);
+ Assert.Null(logger.LastException);
+ Assert.Equal("M9", logger.LastFormattedString);
+ Assert.Equal(LogLevel.Debug, logger.LastLogLevel);
+ Assert.Equal(1, logger.CallCount);
+
+ logger.Reset();
+ NestedStruct.Logger.M10(logger);
+ Assert.Null(logger.LastException);
+ Assert.Equal("M10", logger.LastFormattedString);
+ Assert.Equal(LogLevel.Debug, logger.LastLogLevel);
+ Assert.Equal(1, logger.CallCount);
+
+ logger.Reset();
+ NestedRecord.Logger.M11(logger);
+ Assert.Null(logger.LastException);
+ Assert.Equal("M11", logger.LastFormattedString);
+ Assert.Equal(LogLevel.Debug, logger.LastLogLevel);
+ Assert.Equal(1, logger.CallCount);
+
+ logger.Reset();
+ MultiLevelNestedClass.NestedStruct.NestedRecord.Logger.M12(logger);
+ Assert.Null(logger.LastException);
+ Assert.Equal("M12", logger.LastFormattedString);
+ Assert.Equal(LogLevel.Debug, logger.LastLogLevel);
+ Assert.Equal(1, logger.CallCount);
+ }
+
+ [Fact]
public void TemplateTests()
{
var logger = new MockLogger();
await VerifyAgainstBaselineUsingFile("TestWithDynamicLogLevel.generated.txt", testSourceCode);
}
+ [Fact]
+ public async Task TestBaseline_TestWithNestedClass_Success()
+ {
+ string testSourceCode = @"
+namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
+{
+ namespace NestedNamespace
+ {
+ public static partial class MultiLevelNestedClass
+ {
+ public partial struct NestedStruct
+ {
+ internal partial record NestedRecord(string Name, string Address)
+ {
+ internal static partial class NestedClassTestsExtensions<T1> where T1 : Class1
+ {
+ internal static partial class NestedMiddleParentClass
+ {
+ internal static partial class Nested<T2> where T2 : Class2
+ {
+ [LoggerMessage(EventId = 9, Level = LogLevel.Debug, Message = ""M9"")]
+ public static partial void M9(ILogger logger);
+ }
+ }
+ }
+ }
+ }
+ }
+ internal class Class1 { }
+ internal class Class2 { }
+ }
+}";
+ await VerifyAgainstBaselineUsingFile("TestWithNestedClass.generated.txt", testSourceCode);
+ }
+
private async Task VerifyAgainstBaselineUsingFile(string filename, string testSourceCode)
{
string[] expectedLines = await File.ReadAllLinesAsync(Path.Combine("Baselines", filename)).ConfigureAwait(false);
}
[Fact]
- public async Task NestedType()
+ public async Task NestedTypeOK()
{
IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
partial class C
}
");
- Assert.Single(diagnostics);
- Assert.Equal(DiagnosticDescriptors.LoggingMethodInNestedType.Id, diagnostics[0].Id);
+ Assert.Empty(diagnostics);
}
[Fact]
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses
+{
+ internal static partial class NestedClassTestsExtensions<T> where T : ABC
+ {
+ internal static partial class NestedMiddleParentClass
+ {
+ internal static partial class NestedClass
+ {
+ [LoggerMessage(EventId = 8, Level = LogLevel.Debug, Message = "M8")]
+ public static partial void M8(ILogger logger);
+ }
+ }
+ }
+
+ internal partial class NonStaticNestedClassTestsExtensions<T> where T : ABC
+ {
+ internal partial class NonStaticNestedMiddleParentClass
+ {
+ internal static partial class NestedClass
+ {
+ [LoggerMessage(EventId = 9, Level = LogLevel.Debug, Message = "M9")]
+ public static partial void M9(ILogger logger);
+ }
+ }
+ }
+ public class ABC {}
+
+ public partial struct NestedStruct
+ {
+ internal static partial class Logger
+ {
+ [LoggerMessage(EventId = 10, Level = LogLevel.Debug, Message = "M10")]
+ public static partial void M10(ILogger logger);
+ }
+ }
+
+ public partial record NestedRecord(string Name, string Address)
+ {
+ internal static partial class Logger
+ {
+ [LoggerMessage(EventId = 11, Level = LogLevel.Debug, Message = "M11")]
+ public static partial void M11(ILogger logger);
+ }
+ }
+
+ public static partial class MultiLevelNestedClass
+ {
+ public partial struct NestedStruct
+ {
+ internal partial record NestedRecord(string Name, string Address)
+ {
+ internal static partial class Logger
+ {
+ [LoggerMessage(EventId = 12, Level = LogLevel.Debug, Message = "M12")]
+ public static partial void M12(ILogger logger);
+ }
+ }
+ }
+ }
+}