Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,6 @@ public static class DiagnosticDescriptors
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)),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,29 +65,60 @@ private static bool UseLoggerMessageDefine(LoggerMethod lm)

private void GenType(LoggerClass lc)
{
int indentationSize = 0;
string nestedIndentation = "";
if (!string.IsNullOrWhiteSpace(lc.Namespace))
{
_builder.Append($@"
namespace {lc.Namespace}
{{");
}

LoggerClass parent = lc.ParentClass;
Stack<string> parentClasses = new Stack<string>();
// loop until you find top level nested class
while (parent != null)
{
parentClasses.Push($@"partial class {parent?.Name + " " + parent?.Constraints}");
parent = parent.ParentClass;
}

// write down top level nested class first
foreach (var parentClass in parentClasses)
{
_builder.Append($@"
{nestedIndentation}{parentClass}
{nestedIndentation}{{");
indentationSize += 4;
nestedIndentation = new String(' ', indentationSize);
}

_builder.Append($@"
partial class {lc.Name} {lc.Constraints}
{{");
{nestedIndentation}partial class {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)
{
indentationSize -= 4;
nestedIndentation = new String(' ', indentationSize);
_builder.Append($@"
{nestedIndentation}}}");
parent = parent.ParentClass;
}

if (!string.IsNullOrWhiteSpace(lc.Namespace))
{
Expand All @@ -96,83 +127,83 @@ partial class {lc.Name} {lc.Constraints}
}
}

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)
{
Expand All @@ -192,20 +223,20 @@ private void GenVariableAssignments(LoggerMethod lm)
{
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)
Expand All @@ -217,10 +248,10 @@ private void GenCases(LoggerMethod lm)
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)
Expand Down Expand Up @@ -321,7 +352,7 @@ private void GenHolder(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;
Expand All @@ -332,13 +363,13 @@ private void GenLogMethod(LoggerMethod lm)
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);

Expand All @@ -347,20 +378,20 @@ private void GenLogMethod(LoggerMethod lm)
}

_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);

Expand All @@ -369,7 +400,7 @@ private void GenLogMethod(LoggerMethod lm)
else
{
_builder.Append($@"
{logger}.Log(
{nestedIndentation}{logger}.Log(
{level},
new global::Microsoft.Extensions.Logging.EventId({lm.EventId}, {eventName}),
");
Expand All @@ -380,8 +411,8 @@ private void GenLogMethod(LoggerMethod lm)
}

_builder.Append($@"
}}
}}");
{nestedIndentation}}}
{nestedIndentation}}}");

static string GetException(LoggerMethod lm)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -331,28 +331,25 @@ public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSynt
if (lc == null)
{
// determine the namespace the class is declared in, if any
var ns = classDec.Parent as NamespaceDeclarationSyntax;
if (ns == null)
NamespaceDeclarationSyntax? namespaceParent = null;
SyntaxNode? potentialNamespaceParent = classDec.Parent;
while (potentialNamespaceParent != null && potentialNamespaceParent as NamespaceDeclarationSyntax == null)
{
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 != null && potentialNamespaceParent as NamespaceDeclarationSyntax != null)
{
nspace = ns.Name.ToString();
namespaceParent = potentialNamespaceParent as NamespaceDeclarationSyntax;
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}";
}
}
}
Expand All @@ -364,8 +361,26 @@ public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSynt
Namespace = nspace,
Name = classDec.Identifier.ToString() + classDec.TypeParameterList,
Constraints = classDec.ConstraintClauses.ToString(),
ParentClass = null,
};

LoggerClass currentLoggerClass = lc;
var parentLoggerClass = (classDec.Parent as ClassDeclarationSyntax);

while (parentLoggerClass != null)
{
currentLoggerClass.ParentClass = new LoggerClass
{
Namespace = nspace,
Name = parentLoggerClass.Identifier.ToString() + parentLoggerClass.TypeParameterList,
Constraints = parentLoggerClass.ConstraintClauses.ToString(),
ParentClass = null,
};

currentLoggerClass = currentLoggerClass.ParentClass;
parentLoggerClass = (parentLoggerClass.Parent as ClassDeclarationSyntax);
}

lc.Methods.Add(lm);
}
}
Expand Down Expand Up @@ -571,6 +586,7 @@ internal class LoggerClass
public string Namespace = string.Empty;
public string Name = string.Empty;
public string Constraints = string.Empty;
public LoggerClass? ParentClass = null;
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,6 @@
<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>
Expand Down
Loading