Skip to content
Open
Show file tree
Hide file tree
Changes from all 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 @@ -248,6 +248,13 @@ public IReadOnlyList<LoggerClass> GetLogClasses(IEnumerable<ClassDeclarationSynt
keepMethod = false;
}

bool casingValid = ValidateTemplatesCasingConsistency(lm.TemplateList);
if (!casingValid)
{
Diag(DiagnosticDescriptors.InconsistentTemplateCasing, method.Identifier.GetLocation(), method.Identifier.ToString());
keepMethod = false;
}

if (lm.Name[0] == '_')
{
// can't have logging method names that start with _ since that can lead to conflicting symbol names
Expand Down Expand Up @@ -788,6 +795,36 @@ private static bool ExtractTemplates(string? message, Dictionary<string, string>
return success;
}

/// <summary>
/// Validates that templates list does not contains templates with mixed casing like {hello} and {Hello}
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Grammar error in the comment: "does not contains" should be "does not contain".

Suggested change
/// Validates that templates list does not contains templates with mixed casing like {hello} and {Hello}
/// Validates that templates list does not contain templates with mixed casing like {hello} and {Hello}

Copilot uses AI. Check for mistakes.
/// </summary>
/// <returns>Value indicating lack of mixed casing within templates list</returns>
private static bool ValidateTemplatesCasingConsistency(List<string> templates)
{
int count = templates.Count;

for (int i = 1; i < count; i++)
{
string templateI = templates[i];

for (int j = 0; j < i; j++)
{
string templateJ = templates[j];

bool matchIgnoreCase = StringComparer.OrdinalIgnoreCase.Compare(templateI, templateJ) == 0;
bool matchExact = StringComparer.Ordinal.Compare(templateI, templateJ) == 0;

if (matchIgnoreCase && !matchExact)
{
return false;
}

Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is an unnecessary blank line before the closing brace. This violates the consistent style seen elsewhere in the codebase where closing braces immediately follow the last statement.

Suggested change

Copilot uses AI. Check for mistakes.
}
}

return true;
}

/// <summary>
/// Searches for the next brace index in the message.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,21 @@ partial class C
Assert.Equal(DiagnosticDescriptors.ShouldntMentionLoggerInMessage.Id, diagnostics[1].Id);
}

[Fact]
public async Task MixedCasing()
{
IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
partial class C
{
[LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1 {par1} {PAr1} {a}"")]
static partial void M1(ILogger logger, int par1, int a);
}
");

Assert.Single(diagnostics);
Assert.Equal(DiagnosticDescriptors.InconsistentTemplateCasing.Id, diagnostics[0].Id);
}

[Fact]
Copy link

Copilot AI Feb 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a test case for templates with the exact same casing appearing multiple times (e.g., Message = "{par1} {par1}"). This would help document whether such usage is allowed or should be flagged as an error. The current validation only checks for case mismatches, but doesn't validate whether having the same parameter multiple times is intentional.

Suggested change
[Fact]
[Fact]
public async Task DuplicateTemplateNameSameCasing_IsAllowed()
{
IReadOnlyList<Diagnostic> diagnostics = await RunGenerator(@"
partial class C
{
[LoggerMessage(EventId = 0, Level = LogLevel.Debug, Message = ""M1 {par1} {par1} {a}"")]
static partial void M1(ILogger logger, int par1, int a);
}
");
Assert.Empty(diagnostics);
}
[Fact]

Copilot uses AI. Check for mistakes.
public async Task DoubleLogLevel_InAttributeAndAsParameterButMissingInTemplate_ProducesDiagnostic()
{
Expand Down
Loading