Skip to content

Commit

Permalink
initial support for '@' symbol in message template (#79038)
Browse files Browse the repository at this point in the history
Co-authored-by: Rafael Nicoletti <rafael.nicoletti@ubuntu>
  • Loading branch information
tkrafael and Rafael Nicoletti committed Jan 9, 2023
1 parent 8d9114b commit 5da4a9e
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ private static bool UseLoggerMessageDefine(LoggerMethod lm)
for (int i = 0; i < lm.TemplateList.Count; i++)
{
string t = lm.TemplateList[i];
if (!t.Equals(lm.TemplateParameters[i].Name, StringComparison.OrdinalIgnoreCase))
var (template, parameter) = SanitizeAtSign(t, lm.TemplateParameters[i].CodeName);
if (!template.Equals(parameter, StringComparison.OrdinalIgnoreCase))
{
// order doesn't match, can't use LoggerMessage.Define
return false;
Expand Down Expand Up @@ -198,15 +199,15 @@ private void GenFields(LoggerMethod lm, string nestedIndentation)
{
foreach (LoggerParameter p in lm.TemplateParameters)
{
_builder.AppendLine($" {nestedIndentation}private readonly {p.Type} _{p.Name};");
_builder.AppendLine($" {nestedIndentation}private readonly {p.Type} {ProtectAtSymbol(p.CodeName)};");
}
}

private void GenFieldAssignments(LoggerMethod lm, string nestedIndentation)
{
foreach (LoggerParameter p in lm.TemplateParameters)
{
_builder.AppendLine($" {nestedIndentation}this._{p.Name} = {p.CodeName};");
_builder.AppendLine($" {nestedIndentation}this.{ProtectAtSymbol(p.CodeName)} = {p.CodeName};");
}
}

Expand All @@ -217,7 +218,8 @@ private void GenVariableAssignments(LoggerMethod lm, string nestedIndentation)
int index = 0;
foreach (LoggerParameter p in lm.TemplateParameters)
{
if (t.Key.Equals(p.Name, StringComparison.OrdinalIgnoreCase))
var (key, parameter) = SanitizeAtSign(t.Key, p.Name);
if (key.Equals(parameter, StringComparison.OrdinalIgnoreCase))
{
break;
}
Expand All @@ -231,13 +233,13 @@ private void GenVariableAssignments(LoggerMethod lm, string nestedIndentation)
if (lm.TemplateParameters[index].IsEnumerable)
{
_builder.AppendLine($" {nestedIndentation}var {t.Key} = "
+ $"global::__LoggerMessageGenerator.Enumerate((global::System.Collections.IEnumerable ?)this._{lm.TemplateParameters[index].Name});");
+ $"global::__LoggerMessageGenerator.Enumerate((global::System.Collections.IEnumerable ?)this.{ProtectAtSymbol(lm.TemplateParameters[index].CodeName)});");

_needEnumerationHelper = true;
}
else
{
_builder.AppendLine($" {nestedIndentation}var {t.Key} = this._{lm.TemplateParameters[index].Name};");
_builder.AppendLine($" {nestedIndentation}var {t.Key} = this.{ProtectAtSymbol(lm.TemplateParameters[index].CodeName)};");
}
}
}
Expand All @@ -248,14 +250,15 @@ private void GenCases(LoggerMethod lm, string nestedIndentation)
int index = 0;
foreach (LoggerParameter p in lm.TemplateParameters)
{
string name = p.Name;
// this is related to https://github.com/serilog/serilog-extensions-logging/issues/197
string name = p.CodeName;
if (lm.TemplateMap.ContainsKey(name))
{
// take the letter casing from the template
name = lm.TemplateMap[name];
}

_builder.AppendLine($" {nestedIndentation}{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.{ProtectAtSymbol(p.CodeName)}),");
}

_builder.AppendLine($" {nestedIndentation}{index++} => new global::System.Collections.Generic.KeyValuePair<string, object?>(\"{{OriginalFormat}}\", \"{ConvertEndOfLineAndQuotationCharactersToEscapeForm(lm.Message)}\"),");
Expand Down Expand Up @@ -603,5 +606,16 @@ private static string ConvertEndOfLineAndQuotationCharactersToEscapeForm(string

return sb.ToString();
}

private static bool ContainsAtSymbol(string value) => value.Length > 0 && value[0] == '@';

private static string ProtectAtSymbol(string value) =>
ContainsAtSymbol(value) ? value : $"_{value}";

private static (string template, string parameter) SanitizeAtSign(string template, string parameter)
{
static string SanitizeSingle(string input) => input.Length > 0 && input[0] == '@' ? input.Substring(1) : input;
return (SanitizeSingle(template), SanitizeSingle(parameter));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -351,11 +351,11 @@ public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSynt
Diag(DiagnosticDescriptors.ShouldntMentionLogLevelInMessage, paramSymbol.Locations[0], paramName);
forceAsTemplateParams = true;
}
else if (lp.IsLogLevel && level != null && !lm.TemplateMap.ContainsKey(paramName))
else if (lp.IsLogLevel && level != null && !lm.TemplateMap.ContainsKey(paramName) && !lm.TemplateMap.ContainsKey(lp.CodeName))
{
Diag(DiagnosticDescriptors.ArgumentHasNoCorrespondingTemplate, paramSymbol.Locations[0], paramName);
}
else if (lp.IsTemplateParameter && !lm.TemplateMap.ContainsKey(paramName))
else if (lp.IsTemplateParameter && !lm.TemplateMap.ContainsKey(paramName) && !lm.TemplateMap.ContainsKey($"@{paramName}") && !lm.TemplateMap.ContainsKey(lp.CodeName))
{
Diag(DiagnosticDescriptors.ArgumentHasNoCorrespondingTemplate, paramSymbol.Locations[0], paramName);
}
Expand Down Expand Up @@ -419,7 +419,9 @@ public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSynt
bool found = false;
foreach (LoggerParameter p in lm.AllParameters)
{
if (t.Key.Equals(p.Name, StringComparison.OrdinalIgnoreCase))
if (t.Key.Equals(p.Name, StringComparison.OrdinalIgnoreCase) ||
t.Key.Equals(p.CodeName, StringComparison.OrdinalIgnoreCase) ||
t.Key[0] == '@' && t.Key.Substring(1).Equals(p.CodeName, StringComparison.OrdinalIgnoreCase))
{
found = true;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,41 @@ partial class C
Assert.Equal(1, generatedSources.Length);
Assert.Equal(21, generatedSources[0].SourceText.Lines.Count);
}
[Theory]
[InlineData("{request}", "request")]
[InlineData("{request}", "@request")]
[InlineData("{@request}", "request")]
[InlineData("{@request}", "@request")]
public async Task AtSymbolArgument(string stringTemplate, string parameterName)
{
IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@$"
partial class C
{{
[LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""{stringTemplate}"")]
static partial void M1(ILogger logger, string {parameterName});
}}
");

Assert.Empty(diagnostics);
}

[Theory]
[InlineData("{request}", "request")]
[InlineData("{request}", "@request")]
[InlineData("{@request}", "request")]
[InlineData("{@request}", "@request")]
public async Task AtSymbolArgumentOutOfOrder(string stringTemplate, string parameterName)
{
IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@$"
partial class C
{{
[LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""{stringTemplate} {{a1}}"")]
static partial void M1(ILogger logger,string a1, string {parameterName});
}}
");

Assert.Empty(diagnostics);
}

private static async Task<IReadOnlyList<Diagnostic>> RunGenerator(
string code,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,14 @@ internal static partial class AtSymbolTestExtensions
{
[LoggerMessage(EventId = 0, Level = LogLevel.Information, Message = "M0 {event}")]
internal static partial void M0(ILogger logger, string @event);

[LoggerMessage(EventId = 1, Level = LogLevel.Information, Message = "M1 {@myevent1}")]
internal static partial void M1(ILogger logger, string @myevent1);

[LoggerMessage(Message = "Force use of Struct, {@myevent2} {otherevent}", EventId = 2)]
public static partial void UseAtSymbol3(this ILogger logger, LogLevel level, string @myevent2, int otherevent);

[LoggerMessage(Message = "Force use of Struct with error, {@myevent3} {otherevent}", EventId = 3)]
public static partial void UseAtSymbol4(this ILogger logger, LogLevel level, string @myevent3, int otherevent, System.Exception ex);
}
}

0 comments on commit 5da4a9e

Please sign in to comment.