diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs index 7c9bf4d38a2116..ef55ac527209a7 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Emitter.cs @@ -3,14 +3,16 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; using System.Threading; +using Microsoft.CodeAnalysis; namespace Microsoft.Extensions.Logging.Generators { public partial class LoggerMessageGenerator { - internal sealed class Emitter + internal sealed class Emitter(Compilation compilation) { // The maximum arity of LoggerMessage.Define. private const int MaxLoggerMessageDefineArguments = 6; @@ -28,6 +30,14 @@ internal sealed class Emitter private const string EditorBrowsableAttribute = "global::System.ComponentModel.EditorBrowsableAttribute(" + "global::System.ComponentModel.EditorBrowsableState.Never)"; + + private readonly bool _hasStringCreate = + compilation.GetSpecialType(SpecialType.System_String).GetMembers("Create").OfType() + .Any(m => m.IsStatic && + m.Parameters.Length == 2 && + m.Parameters[0].Type.Name == "IFormatProvider" && + m.Parameters[1].RefKind == RefKind.Ref); + private readonly StringBuilder _builder = new StringBuilder(DefaultStringBuilderCapacity); private bool _needEnumerationHelper; @@ -163,8 +173,15 @@ private void GenStruct(LoggerMethod lm, string nestedIndentation) {nestedIndentation}{{ "); GenVariableAssignments(lm, nestedIndentation); + + string formatMethodBegin = + !lm.Message.Contains('{') ? "" : + _hasStringCreate ? "string.Create(global::System.Globalization.CultureInfo.InvariantCulture, " : + "global::System.Diagnostics.CodeAnalysis.FormattableString.Invariant("; + string formatMethodEnd = formatMethodBegin.Length > 0 ? ")" : ""; + _builder.Append($@" - {nestedIndentation}return $""{ConvertEndOfLineAndQuotationCharactersToEscapeForm(lm.Message)}""; + {nestedIndentation}return {formatMethodBegin}$""{ConvertEndOfLineAndQuotationCharactersToEscapeForm(lm.Message)}""{formatMethodEnd}; {nestedIndentation}}} "); _builder.Append($@" diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn3.11.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn3.11.cs index 6a40625ba62da8..8dd540a302e35a 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn3.11.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn3.11.cs @@ -33,7 +33,7 @@ public void Execute(GeneratorExecutionContext context) IReadOnlyList logClasses = p.GetLogClasses(receiver.ClassDeclarations); if (logClasses.Count > 0) { - var e = new Emitter(); + var e = new Emitter(context.Compilation); string result = e.Emit(logClasses, context.CancellationToken); context.AddSource("LoggerMessage.g.cs", SourceText.From(result, Encoding.UTF8)); diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.cs index 74cc8e13fcf0ef..3145cc07f9bbe0 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/gen/LoggerMessageGenerator.Roslyn4.0.cs @@ -51,7 +51,7 @@ private static void Execute(Compilation compilation, ImmutableArray logClasses = p.GetLogClasses(distinctClasses); if (logClasses.Count > 0) { - var e = new Emitter(); + var e = new Emitter(compilation); string result = e.Emit(logClasses, context.CancellationToken); context.AddSource("LoggerMessage.g.cs", SourceText.From(result, Encoding.UTF8)); diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithMoreThan6Params.generated.txt b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithMoreThan6Params.generated.txt index 0b851e5acd0047..a6b293e3d47b48 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithMoreThan6Params.generated.txt +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/Baselines/TestWithMoreThan6Params.generated.txt @@ -40,7 +40,7 @@ namespace Microsoft.Extensions.Logging.Generators.Tests.TestClasses var p6 = this._p6; var p7 = global::__LoggerMessageGenerator.Enumerate((global::System.Collections.IEnumerable ?)this._p7); - return $"M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}"; + return string.Create(global::System.Globalization.CultureInfo.InvariantCulture, $"M9 {p1} {p2} {p3} {p4} {p5} {p6} {p7}"); } public static readonly global::System.Func<__Method9Struct, global::System.Exception?, string> Format = (state, ex) => state.ToString(); diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs index ceac90cc93c3ba..2d2047a91248f0 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/LoggerMessageGeneratedCodeTests.cs @@ -3,12 +3,13 @@ using System; using System.Collections.Generic; -using Microsoft.Extensions.Logging.Abstractions; +using System.Globalization; using Microsoft.Extensions.Logging.Generators.Tests.TestClasses; using Microsoft.Extensions.Logging.Generators.Tests.TestClasses.UsesConstraintInAnotherNamespace; using Xunit; using NamespaceForABC; using ConstraintInAnotherNamespace; +using System.Tests; namespace Microsoft.Extensions.Logging.Generators.Tests { @@ -713,7 +714,24 @@ public void TemplateTests() new KeyValuePair("A1", 42), new KeyValuePair("a2", 43), new KeyValuePair("{OriginalFormat}", "M3 {a2} {A1}")); + } + + [Fact] + public void TemplateTests_UsesInvariantCulture() + { + using ThreadCultureChange _ = new("fr-FR"); + + var logger = new MockLogger(); + logger.Reset(); + TemplateTestExtensions.M4(logger, 1.23); + Assert.Null(logger.LastException); + Assert.Equal("M4 1.23", logger.LastFormattedString); + + logger.Reset(); + TemplateTestExtensions.M5(logger, 1.23, 4.56, 7.89, 10.11, 12.13, 14.15, 16.17); + Assert.Null(logger.LastException); + Assert.Equal("M5 1.23 4.56 7.89 10.11 12.13 14.15 16.17", logger.LastFormattedString); } [Fact] diff --git a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs index 840e5bce5cadbb..8bb0f194ce1dda 100644 --- a/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.Logging.Abstractions/tests/Microsoft.Extensions.Logging.Generators.Tests/TestClasses/TemplateTestExtensions.cs @@ -16,5 +16,11 @@ internal static partial class TemplateTestExtensions [LoggerMessage(EventId = 3, Level = LogLevel.Error, Message = "M3 {a2} {A1}")] public static partial void M3(ILogger logger, int a1, int a2); + + [LoggerMessage(EventId = 4, Level = LogLevel.Error, Message = "M4 {A1}")] + public static partial void M4(ILogger logger, double a1); + + [LoggerMessage(EventId = 5, Level = LogLevel.Error, Message = "M5 {A1} {a2} {A3} {a4} {A5} {a6} {A7}")] + public static partial void M5(ILogger logger, double a1, double a2, double a3, double a4, double a5, double a6, double a7); } }