Skip to content

Commit

Permalink
rewriting parsing algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
allantargino committed Feb 3, 2023
1 parent ae0be6b commit 1491de0
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 47 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -637,68 +637,76 @@ private static bool ExtractTemplates(string? message, IDictionary<string, string

// Format item syntax : { index[,alignment][ :formatString] }.
int formatDelimiterIndex = FindIndexOfAny(message, _formatDelimiters, openBraceIndex, closeBraceIndex);

string templateName = message.Substring(openBraceIndex + 1, formatDelimiterIndex - openBraceIndex - 1);

if (string.IsNullOrWhiteSpace(templateName)) // braces with no named argument, such as {} and { }
{
success = false;
break;
}

templateMap[templateName] = templateName;
templateList.Add(templateName);

scanIndex = closeBraceIndex + 1;
}

return success;
}

private static int FindBraceIndex(string message, char brace, int startIndex, int endIndex)
/// <summary>
/// Searches for the next brace index in the message.
/// </summary>
/// <remarks> The search skips any sequences of {{ or }}.</remarks>
/// <example>{{prefix{{{Argument}}}suffix}}</example>
/// <returns>The zero-based index position of the first occurrence of the searched brace; -1 if the searched brace was not found; -2 if the wrong brace was found.</returns>
private static int FindBraceIndex(string message, char searchedBrace, int startIndex, int endIndex)
{
Debug.Assert(brace is '{' or '}');
Debug.Assert(searchedBrace is '{' or '}');

// Example: {{prefix{{{Argument}}}suffix}}.
int braceIndex = -1;
int scanIndex = startIndex;
int braceOccurrenceCount = 0;

while (scanIndex < endIndex)
{
if (braceOccurrenceCount > 0 && message[scanIndex] != brace)
char current = message[scanIndex];

if (current is '{' or '}')
{
if (braceOccurrenceCount % 2 == 0)
char currentBrace = current;

int scanIndexBeforeSkip = scanIndex;
while (current == currentBrace && ++scanIndex < endIndex)
{
// Even number of '{' or '}' found. Proceed search with next occurrence of '{' or '}'.
braceOccurrenceCount = 0;
braceIndex = endIndex;
current = message[scanIndex];
}
else

int bracesCount = scanIndex - scanIndexBeforeSkip;
if (bracesCount % 2 != 0) // if it is an even number of braces, just skip them, otherwise, we found an unescaped brace
{
// An unescaped '{' or '}' found.
if (currentBrace == searchedBrace)
{
if (currentBrace == '{')
{
braceIndex = scanIndex - 1; // For '{' pick the last occurrence.
}
else
{
braceIndex = scanIndexBeforeSkip; // For '}' pick the first occurrence.
}
}
else
{
braceIndex = -2; // wrong brace found
}

break;
}
}
else if (message[scanIndex] == '{')
else
{
if (brace != '{')
{
return -2; // not expected
}

// For '{' pick the last occurrence.
braceIndex = scanIndex;
braceOccurrenceCount++;
scanIndex++;
}
else if (message[scanIndex] == '}')
{
if (brace != '}')
{
return -2; // not expected
}

if (braceOccurrenceCount == 0)
{
// For '}' pick the first occurrence.
braceIndex = scanIndex;
}
braceOccurrenceCount++;
}

scanIndex++;
}

return braceIndex;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -670,7 +670,7 @@ public async Task MalformedFormatString()
IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
partial class C
{
[LoggerMessage(EventId = 1, Level = LogLevel.Debug, Message = ""M1 {A} M1 { M3"")]
[LoggerMessage(EventId = 1, Level = LogLevel.Debug, Message = ""M1 {A} M1 { M1"")]
static partial void M1(ILogger logger);
[LoggerMessage(EventId = 2, Level = LogLevel.Debug, Message = ""M2 {A} M2 } M2"")]
Expand All @@ -691,23 +691,29 @@ partial class C
[LoggerMessage(EventId = 7, Level = LogLevel.Debug, Message = ""{M7{"")]
static partial void M7(ILogger logger);
[LoggerMessage(EventId = 8, Level = LogLevel.Debug, Message = ""M8 {{arg1}}"")]
[LoggerMessage(EventId = 8, Level = LogLevel.Debug, Message = ""{{{arg1 M8"")]
static partial void M8(ILogger logger);
[LoggerMessage(EventId = 9, Level = LogLevel.Debug, Message = ""}M9{arg1}{arg2}"")]
[LoggerMessage(EventId = 9, Level = LogLevel.Debug, Message = ""arg1}}} M9"")]
static partial void M9(ILogger logger);
[LoggerMessage(EventId = 10, Level = LogLevel.Debug, Message = ""{} M10"")]
static partial void M10(ILogger logger);
[LoggerMessage(EventId = 11, Level = LogLevel.Debug, Message = ""{ } M11"")]
static partial void M11(ILogger logger);
}
");

Assert.Equal(9, diagnostics.Count);
Assert.Equal(11, diagnostics.Count);
foreach (var diagnostic in diagnostics)
{
Assert.Equal(DiagnosticDescriptors.MalformedFormatStrings.Id, diagnostic.Id);
}
}

[Fact]
public async Task Templates()
public async Task ValidTemplates()
{
IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
partial class C
Expand All @@ -727,14 +733,14 @@ partial class C
[LoggerMessage(EventId = 5, Level = LogLevel.Debug, Message = ""{arg1} M5"")]
static partial void M5(ILogger logger, int arg1);
[LoggerMessage(EventId = 6, Level = LogLevel.Debug, Message = ""{arg1}{arg2}"")]
[LoggerMessage(EventId = 6, Level = LogLevel.Debug, Message = ""M6{arg1}M6{arg2}M6"")]
static partial void M6(ILogger logger, string arg1, string arg2);
[LoggerMessage(EventId = 7, Level = LogLevel.Debug, Message = ""M7 {arg1} {arg2}"")]
static partial void M7(ILogger logger, string arg1, string arg2);
[LoggerMessage(EventId = 7, Level = LogLevel.Debug, Message = ""M7 {{const}}"")]
static partial void M7(ILogger logger);
[LoggerMessage(EventId = 8, Level = LogLevel.Debug, Message = ""{arg1} M8 {arg2} "")]
static partial void M8(ILogger logger, string arg1, string arg2);
[LoggerMessage(EventId = 8, Level = LogLevel.Debug, Message = ""{{prefix{{{arg1}}}suffix}}"")]
static partial void M8(ILogger logger, string arg1);
}
");

Expand Down

0 comments on commit 1491de0

Please sign in to comment.