Skip to content

Commit

Permalink
Adds validation against invalid bracket pairs
Browse files Browse the repository at this point in the history
* Fixes dotnet#7285
  • Loading branch information
Kritner committed Apr 10, 2024
1 parent abef8ce commit 7adaf12
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
Expand Down Expand Up @@ -192,6 +193,12 @@ private void AnalyzeFormatArgument(OperationAnalysisContext context, IOperation
return;
}

if (!IsValidBrackets(text))
{
context.ReportDiagnostic(formatExpression.CreateDiagnostic(CA2017Rule));
return;
}

LogValuesFormatter formatter;
try
{
Expand Down Expand Up @@ -251,6 +258,46 @@ private void AnalyzeFormatArgument(OperationAnalysisContext context, IOperation
}
}

/// <summary>
/// Does the text have valid brackets?
/// </summary>
/// <param name="text">The text to check.</param>
/// <returns>When true brackets are valid, false otherwise.</returns>
private static bool IsValidBrackets(string text)
{
var relevantBracketPairs = new Dictionary<char, char>()
{
{'}', '{'},
};

var stack = new Stack<char>();

for (var i = 0; i < text.Length; i++)
{
// If we're on a closing bracket...
if (relevantBracketPairs.ContainsKey(text[i]))
{
// and nothing in the stack, invalid
if (stack.Count == 0)
return false;

// Check the "opening" of this type of bracket from the stack matches expectations
var pop = stack.Pop();
if (relevantBracketPairs[text[i]] != pop)
return false;
}

// If we're on a bracket, push onto stack for tracking
if (relevantBracketPairs.ContainsValue(text[i]))
{
stack.Push(text[i]);
}
}

// Entire text has been evaluated and no issues found
return true;
}

private static bool FindLogParameters(IMethodSymbol methodSymbol, [NotNullWhen(true)] out IParameterSymbol? message, out IParameterSymbol? arguments)
{
message = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,24 @@ public async Task CA2017IsProducedForDefineMessageTypeParameterMismatchAsync(str
await TriggerCodeAsync(expression);
}


/// <summary>
/// <para>
/// https://github.com/dotnet/roslyn-analyzers/issues/7285
/// </para>
/// <para>
/// Unmatched brackets cause runtime exceptions in a message template, should flag CA2017
/// </para>
/// </summary>
[Theory]
[InlineData(@"LoggerMessage.DefineScope<int>({|CA2017:""{One}}""|});")]
[InlineData(@"LoggerMessage.DefineScope<int>({|CA2017:""{{One}""|});")]
[InlineData(@"LoggerMessage.DefineScope<int>({|CA2017:""}{One}""|});")]
public async Task Fix7285_CA2017(string format)
{
await TriggerCodeAsync(format);
}

[Theory]
[InlineData("LogTrace", @"""This is a test {Message}""")]
[InlineData("LogDebug", @"""This is a test {Message}""")]
Expand Down

0 comments on commit 7adaf12

Please sign in to comment.